Sunday, September 21, 2014

Multiple Enemies

Ok, now I need to talk about getting multiple enemies on the screen in Anguna 2600. Time to write a novel here.

A little bit of background first: The atari is designed to only show 2 sprites on the screen at once. (although each one can be doubled or tripled horizontally) But, because you can change things up every scanline (if you are fast enough!) you can reset the sprites every few vertical lines, to make a number of things happening onscreen, so long as you commit to spacing them out vertically. Which is why so many games had rows of sprites in their own little lanes on the screen. Classic examples are Freeway and Space Jockey:

In each of these, the player is one sprite, and sprite 2 is reused over and over vertically (in freeway, you can see the various options for duplicating the sprite horizontally)

But things get messy if you want to try to show more sprites than that on a single line. There are a few crazy tricks (you can place both sprites next to each other, triple them, and then update the graphic data on the fly as the scanline moves across the screen. But that pretty much takes ALL your processing time, so you can't do anything else as well). Or you can do what pac-man does with the ghosts, which is draw alternating sprites on different frames. With this technique, you could theoretically draw as many sprites as you wanted, but since you're only drawing a couple of them per frame, they get more and faded the more you draw (on pac-man, I believe each ghost is only drawn every 4 frames!)

My goal with Anguna, if I can figure it out, is to allow 3 enemies onscreen at once. What I hope to do, is adaptively adjust how I draw them, depending on the current screen layout.  If they are all spaced out nicely, in different vertical locations, I'd just draw them separately, like space jockey or freeway. But as soon as 2 (or more) of them would overlap vertical positions, I'd start fading and alternate drawing them.

That way, it wouldn't be a faded mess most of the time, but I could degrade to fading when necessary. The tricky part of this is that I'd basically have to write multiple different display kernels, and choose the right one accordingly (there's not enough time during the tight display loop to branch and handle the different cases, so I'd have to write separate (but mostly similar) display loops, and pre-select the right one before drawing. I'm not sure if I'm smart enough to pull this off.

The other problem I foresee is going to be collision detection. The one relatively easy thing the Atari 2600 gives you is built-in pixel-level collision detection. You can read a register to see if your sprite collided with the playfield walls. Or with another sprite. Or a missile. Or the "ball" (the only other on-screen object that the Atari provides besides 2 sprites, 2 missiles, and a playfield). The Atari automatically sets the values of the collisions registers as it's drawing, whenever the TIA (the fancy Atari display chip) actually draws the collision.

Which causes problems when I'm fading enemies. Because an enemy-wall collision might only be detected every 3rd frame (if all 3 enemies are on the same row, so I'm having to alternate drawing them). I still want them to move at regular speed, but if they run into a wall, they have to back up to their previous location. I previously was saving the player and enemies "last location" along with the current location, so I can revert to that in the case of a wall collision.

But with my plan, with enemies, I'll have to store the last position from the last time they were actually drawn. (as that was the last time that I knew they weren't colliding with a wall) But I don't know for sure if they'll be drawn this frame, until after I move them. It's not a terribly complicated problem, but requires keeping a little bit more data in ram to sort it out. Which I don't have much of. The Atari gives you 120 bytes of ram (for both variables and your stack!). I've used up most of that already, and only have about 24 bytes remaining, which I'm greedily guarding.

Ok, actually, the process of just typing this up and explaining it made the optimal solution pretty obvious to me -- I can do this using just 2 more bytes of ram. I just need to temporarily store the last X and last Y for an enemy in a temporary variable, before I move them. Then, if I've decided I'm drawing that enemy, copy that those temp variables to the actual lastX and lastY for that enemy. I can reuse those temporary variables so that I don't have to allocate more ram than that.

Ok, well, now that I've scared off any readers that I may have still had, I think I'm done for now. It was worth it just to make me organize my thoughts enough to solve that silly problem.


Bryan R said...

If only you were building C-64 Anguna... 8 luxurious sprites!

Nathan Tolbert said...

Wow. 8 sprites would make this all too easy! ;-)

Metatile Designer

For the NES game, I'm building my levels out of 32x32 pixel metatiles.  What is a metatile?  The NES background tiles are 8x8 pixels, bu...