The morning after I posted that last post, I realized my "simple solution" was wrong. Because I can't determine if an enemy is showing on a frame until after I've moved all the enemies for that frame. So I really do need to save 3 sets of previous X/Y coordinates. That's 6 whole bytes of my 24 remaining.
I don't like it. But I'm not sure I have any options.
I started by blogging the process of porting my homebrew game Anguna from the Gameboy Advance to the Nintendo DS. Now, my random thoughts on development and chronicling whatever hobby project catches my fancy.
Tuesday, September 23, 2014
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.
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.
Playtesting and rearranging
Last night I finished a big section of the RoboNinja world map, and so I decided this was a good time to do some serious playtesting. I loaded up the game onto my phone and got to work. (at which point I noticed that my free trial of my vm hosting where I had my jenkins server had expired, so I need to figure out a replacement. I need to find something that can do Android builds, a place to push and host binaries, and all that without spending much money!)
It took me around 30 minutes to play through all the content that I currently have. Most of it worked fairly well, and played like I expected. There were a few spots where somehow the phone played just differently enough so that it was harder on the phone than it was on the desktop version, which will need to be fixed. (At one point in particular, I kept respawning inside a wall, which never happened before on that map).
The biggest thing that I realized had to do with the section of maps that I just finished. It's a rather long excursion that leads fairly linearly to a single item. You play through a bunch of moderately difficult maps, to be rewarded with the wall jump item (which lets you jump upwards off walls). Unfortunately, this is a bit underwhelming. It seems cool, but doesn't really change the way you play the game all that much. Compared with the double-jump item (where you can do another jump mid-jump) that you find a few screens earlier, which completely changes everything.
So I decided it was time to swap them. Which meant I needed to play through that part of the world again, to make sure that the challenge/balance was right (and actually possible) without having to get one before the other. (Turns out, no. There was one place where you had to have the double-jump or slam item to be able to make a jump. So that needs to be fixed).
So now they are all swapped around. And the first half of the game world is complete. We're getting there!
It took me around 30 minutes to play through all the content that I currently have. Most of it worked fairly well, and played like I expected. There were a few spots where somehow the phone played just differently enough so that it was harder on the phone than it was on the desktop version, which will need to be fixed. (At one point in particular, I kept respawning inside a wall, which never happened before on that map).
The biggest thing that I realized had to do with the section of maps that I just finished. It's a rather long excursion that leads fairly linearly to a single item. You play through a bunch of moderately difficult maps, to be rewarded with the wall jump item (which lets you jump upwards off walls). Unfortunately, this is a bit underwhelming. It seems cool, but doesn't really change the way you play the game all that much. Compared with the double-jump item (where you can do another jump mid-jump) that you find a few screens earlier, which completely changes everything.
So I decided it was time to swap them. Which meant I needed to play through that part of the world again, to make sure that the challenge/balance was right (and actually possible) without having to get one before the other. (Turns out, no. There was one place where you had to have the double-jump or slam item to be able to make a jump. So that needs to be fixed).
So now they are all swapped around. And the first half of the game world is complete. We're getting there!
Monday, September 15, 2014
Anguna 2600
I just spent a week traveling for a work conference. Between the 8 hours of flying, and a few nights in my hotel room, I got a lot done. Both on Robo-Ninja, and my Atari project.
On Robo-Ninja, I got through a few more maps, added a new enemy that flashes a "bright light" which temporarily blinds you from seeing the room you're in, and started working on a scripting system for animating my intro and ending story sequences. I'll get to all that in more detail later.
On my Atari project, I finally have it looking and acting somewhat like an actual video game. There's still a ton to do (finish collision detection, rewrite my display kernel to allow 3 enemies onscreen at once, allow you to switch rooms, add sounds, etc, etc, etc), but at this point, I've been vague long enough: my goal is Anguna for the atari. It's going to be pretty primitive, but I think I can make a passable version of the game for the 2600.
I've got a number of interesting (to me!) things to talk about regarding the game: the challenges of getting the top header bar working, my strategy for multiple enemies onscreen, how I may have to end up using bankswitching to allow for more than 4K of ROM, how collision detection works on the atari (one of the few things that's easy!). But it's late, so I'll leave it with that picture for now.
On Robo-Ninja, I got through a few more maps, added a new enemy that flashes a "bright light" which temporarily blinds you from seeing the room you're in, and started working on a scripting system for animating my intro and ending story sequences. I'll get to all that in more detail later.
On my Atari project, I finally have it looking and acting somewhat like an actual video game. There's still a ton to do (finish collision detection, rewrite my display kernel to allow 3 enemies onscreen at once, allow you to switch rooms, add sounds, etc, etc, etc), but at this point, I've been vague long enough: my goal is Anguna for the atari. It's going to be pretty primitive, but I think I can make a passable version of the game for the 2600.
I've got a number of interesting (to me!) things to talk about regarding the game: the challenges of getting the top header bar working, my strategy for multiple enemies onscreen, how I may have to end up using bankswitching to allow for more than 4K of ROM, how collision detection works on the atari (one of the few things that's easy!). But it's late, so I'll leave it with that picture for now.
Subscribe to:
Posts (Atom)
NES Anguna
Well, I had a little bit of time still, while Frankengraphics is finishing up her game Project Blue, to have a little downtime on Halcyon, s...
-
So I've recently purchased DosBox Turbo , the best (as far as I can find) implementation of DosBox for Android, and have been playing th...
-
Well, I had a little bit of time still, while Frankengraphics is finishing up her game Project Blue, to have a little downtime on Halcyon, s...
-
When I'm too tired/frustrated to work on my NES game, I've been playing around with ideas for another hobby project that I've he...