Jump to content
IGNORED

Super Space Acer


Tursi

Recommended Posts

  • 9 months later...

After far too long, I've finally gotten back to this, and I've implemented two of the more tricky things that I've been putting off forever. One was a final enemy - beam generators. They come down as two mechanical pods that bounce a beam back and forth between them. Destroy either one to stop the beam. You've seen these in countless games, but I've always loved them.

 

The other, which I'll talk about a bit this time, was the implementation of the enemy patterns. Frankly, this one stumped me for a long time - not so much how to code it, but how to create the data. I toyed with a number of ideas, and finally decided that I was happy enough with the enemy behavior, so I should just draw up the patterns.

 

I loaded up a map editor (as something convenient) and started placing enemies on a scaled representation of the first stage. After about 15 minutes I stopped to check my progress, which was on the order of 25%. I very quickly realized that I didn't want to spend the next 6+ hours dropping 'X's on a grid...

 

So I went searching. Surely SOMEONE had made a similar level editor... but I couldn't find anything appropriate.

 

I took a moment and looked at the map I had been creating, and what I noticed was that, for the most part, the enemy spawn points I was plotting created straight lines on the map. A string of saucers made for a nice vertical line, a set of jets swept across the screen was a slight angle, and if they spaced out a bit more, it was a steeper angle. "Maybe I can just create a tool that just draws lines, then."

 

As I thought about how to implement it, I considered the idea of a 'generator' that moved across the screen and spawned a new enemy periodically. I realized that if I just tweaked the horizontal speed and the spawn rate, I could generate not only any slope of line, but even random-like scatter patterns. I further realized, that if I seeded the random number generator predictably, the computer could just set up the generators and we'd get repeatable levels with at least some semblance of patterns out of the enemies.

 

And that's in this version of the code. I may still want to tune it a bit, but I've played all the way through all three difficulties and I think the tuning is not too bad.

 

So how do the generators work? The game supports 1-3 at a time, depending on the difficulty and the level you're on. Each generator is mostly independent, more on that in a moment. Each generator further tracks the following data: horizontal position, countdown timer, maximum timer, movement speed, count of enemies remaining, and type of enemy to generate. They are always positioned at the top of the screen because, in SSA, all enemies begin life at the top of the screen.

 

In order to guarantee the RNG, the generators are checked at fixed points in the level progression -- right now every 32 frames the next generator is checked. (Although because I do this with bitmasking, and there are only 3 generators, every fourth check does nothing. Also, in levels that don't have all three generators out, those idle generators end up doing nothing as well. This is partly for the difficulty curve and partly to control CPU time.)

 

If we check and determine that the generator of interest this frame is idle, and it's allowed to start (again, due to level and difficulty), then we set the RNG based (mostly) on the current level distance counter. Then we create the generator randomly, setting up its position, timer, speed, count and type. The position is where enemies will be dispatched (horizontally), the timer is how many frames between enemy dispatches, the speed is how far (and which way) the generator moves every frame, the count is how many enemies will be dispatched before the generator returns to idle, and the type is what type of enemy that will be. These last two values are constrained by the level and difficulty settings. The timer is also constrained by the enemy type in order to keep a reasonable minimum spacing. In effect, it's a rudimentary particle system generator.

 

There's one additional catch -- to avoid overlap of enemies from being too common a thing, we check the spacing against the other generators and reset the position if it's too close to one of them.

 

During the enemy processing frame, generators are updated. The update is very simple - the speed is added to the position (and wraps around natively with no need to check, since we have 256 pixels across), then the timer is counted down. When it reaches zero, the generator calls the enemy spawn function, and decrements its count. When the count reaches zero, the generator goes idle and will be awakened the next time the distance update reaches it.

 

Enemy spawn is straight-forward now. We know the type of enemy, and we know where it will start. We also know it's legal for the level and don't need to check that anymore either. But there are still two things to check. One is whether there is a legal slot to spawn the enemy in -- both the game engine and the difficulty constrain this. If a free slot is not available, then the opportunity to spawn is discarded. (This is why on the easier difficulties you will see more enemies if you shoot them than if you don't!) A second check is also performed for overlap - if the generator is too close to one of the other generators, it's deferred rather than discarded, on the assumption that the generators will move apart.

 

The overlap avoidance code actually created an interesting bug that only occurred in my testing midway through the medium difficulty level (it might still be there, I didn't finish fixing it... but I didn't see it tonight.) Anyway... about halfway through the stage, enemies stopped appearing, and only spawned one enemy every long time right at the edge until the boss came out. I was a little baffled and added debug to show me where the generators were. And at the magic point, I watched generators 1 and 2 chase each other across the screen. They had started wrapped around (255 and 1) so passed the creation range check, but were moving the same direction at the same speed, and so were always too close to spawn -- except for the one frame where they were split at the edge of the screen again. (Earlier, before I put the test in the generator startup code, I'd seen a similar case where two generators started right beside each other with a speed of zero. Never moved, and couldn't spawn, so the stage stayed empty.)

 

 

Anyway, I'm also proud of the beamgens. SSA uses all the available sprites and the game engine is limited in the number of enemies it allows - 6 enemies, each with their own shot. It was not intended to support a single enemy with two widely spread parts, so I was going to implement it as two enemies. But I was getting really concerned about how to find empty slots and tracking the two indexes, and ultimately I went for a flicker approach where the generator parts alternate each frame. This worked surprisingly well (and you'll see as a result that they still flicker on the F18A, even though no other sprites do ;) ). The beam itself is just the enemy's bullet, so that was a guaranteed spare.

 

Testing collision on the main sprite was easy, but I had to think a bit about the secondary one.. ultimately, I decided to just try actually moving the enemy back and forth, and seeing if collisions lasted long enough that either could be shot. Happily, the answer was yes, so all I had to do in the enemy death function was check against the base X position to see whether the right or left generator was the one destroyed, and then set up the enemy as a dud that looked like the remaining one. I did something very similar to this in Mario Bros, actually. The bonus stage broke my enemy engine by having too many coins to collide with. Rather than add a bunch of special cases, it flickers just four coins through all 10 visible ones for the sake of the collision engine. It's something like 1 in 3 frames on the worst case, but the slope of Mario's jump guarantees contact for that long for all but the most contrived cases. (Of course, each coin you collect improves this as well, once you have two coins it's 1 frame in 2, and when there are only four coins left they are testing collision every frame).

 

Anyway, it's nice to finally have an update. :)

 

ssa_coleco.zip

  • Like 4
Link to comment
Share on other sites

  • 6 years later...

... finally waking this up to finish it, only to find I've lost all the code in that last update.

 

Since I was uploading zips here without any source, it's a long shot, but did anyone capture source from back then from github or such? If not, well... at least I wrote a detailed description of how I implemented them... ;)

 

Link to comment
Share on other sites

Thanks for searching. That's... an odd zip. I didn't make that. The 5 ROM files and the .pc files are the giveaway. (Looks like they are the title page and a screenshot).

But... well, maybe that's a good next lesson to post - how I lost the code. ;) I have everything up to the previous update, just sad that was a fairly big one. I suspect I had it in a branch and for some reason left the folder in a different branch.

 

So anyway, source control should have saved me, if I was using Github back then. Not sure that I was. But either way, about a year ago I reorganized all my projects for Github, and moved all the Super Space Acer projects into one super project. However, when I got back to it last week, I noticed the Coleco version was having git issues, probably due to left-over remnants of an old repo. Since the project built, I didn't worry about it, I just blew everything away without a care and recreated the repo. Then I forced it up to Github, which blew away any history there. And for some reason, I discovered my backup isn't including the .git folders. So it /might/ have been there, but my own carelessness destroyed any chance to check.

 

I was confident it was okay, so maybe the only lesson is, when you are deliberately blowing away history, move slowly and give yourself time to be sure you were right. Either take copies off to the side first and hold them for a few weeks, or at least spread it out over a few days. ;)

 

Oh, also that for some reason I was being all protective of the source code. I hate when I do that.

 

At any rate, I have enough notes to recreate it. Just mad I have to redo graphics. 

 

 

Edited by Tursi
Link to comment
Share on other sites

So here's a new update. I've reimplemented all the code that was lost (and some of it came out a little better.) I've also doubled the ROM size since it was never going to be able to hold the endings at 128k anyway. Let's see... what else is new then?

 

Generally I was trying to post lessons learned, but I think I've already done that above. ;)

 

Some performance optimizations, though I still need to see if I can improve the performance of marking up the boss ship when you hit it with the most powerful weapon.

 

I've tweaked the difficulty levels a bit. Some of this was necessary anyway, since there was no way to know how the enemy generators were previously tuned. But I made easy even easier by restricting enemies to two at a time, and only letting them fire on 8 directions like the original game. Higher difficulties still aim.

 

I added an attract mode. After about 40 seconds on the title page it will alternate between self-play (with a different ship each time), and a brief information screen.

 

One of the two secret ships now has homing shots instead of the standard shots - so both secret ships have different firepower.

 

Updated the music player to my newer vgmcomp2 library. This saved a bit of ROM space and some CPU time as well, so was worth it.

 

I replaced the boss graphics with original artwork, instead of the Tyrian artwork. My brother didn't like that I replaced the bosses he designed when we were kids. ;)

 

I think that's about it. This is mostly a checkpoint to get things back in line. I updated my SDCC toolchain to the latest and also updated my Coleco Banking doc over at Github with the new linker and notes. Lots of tuning left to go, and note that none of the hard level endings are in place yet.

 

Video here: https://odysee.com/@tursilion:1/2023-06-29-06-19-57:c

 

And ROM here: 

ssa20230629.zip

Edited by Tursi
  • Like 9
Link to comment
Share on other sites

  • 2 weeks later...

I've been doing a lot of work on gettin g it closer to its final form. This version changes a fair bit in the interest of balance and variety.

 

In particular, I changed the attack patterns for the last two bosses - they should be a little more interesting and a little more fair. Also, the RNG is now fairly deterministic, so it should be possible to learn patterns. I know the final boss' attack has a few graphical errors yet, but they should be cosmetic only.

 

The game will play sound effects if you have an AY sound chip (SGM, Phoenix, etc), though I've not tested that on hardware yet. You can also mute the music and get sound effects on the built-in SN sound chip by pressing '#' at the difficulty select or ingame. You can also pause with '*'. Or maybe I have those backwards. ;)

 

Added two more end sequences - both secret characters have one, and the ladybug has one. Still need to think of something for the other two... I have some rough ideas, but I'm trying to code different concepts for each one.

 

Edited by Tursi
  • Like 6
Link to comment
Share on other sites

  • 5 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...