Skip to main content

Optimizing space, adding a switch.

Thanks to the nice folks at AtariAge, I got some feedback, mostly about various bugs, which have been cleaned up.  Now I'm trying to power through and add the rest of the content. Which is tricky, because I just don't have that much space left.

The biggest area of concern right now is enemies.  My space left in the bank where I store enemy data is small. Just a couple hundred bytes.  And I needed to add definitions for at LEAST 3 more enemies (bosses for the next 3 dungeons). And hopefully, another 3 or so more general enemies, so the 2nd half of the game isn't completely boring.

The first order of business was to read through the existing enemy definition code, and start optimizing. The first few enemy functions were unnecessarily huge. The amount of code for the slime and toady were just silly. I managed to compact it all a little bit, and squeezed in the 3 boss monsters.

Without much space left, the next 2 enemies were just palette/graphic swaps with more HP/Damage of previous enemies.

But one thing I wanted to add in dungeon 3 was a switch that controlled doors. How could I manage that with only about 10 bytes left?  I'm handling it like an enemy (it's a non-player object, and enemies are the only things I have in that category). The first hurdle was that my index that points to the enemy definition overflowed.

I have my enemies laid out in a big block of data, one after another:
    .byte #<SlimeFrame0
    .byte #>SlimeFrame0             
    .byte #<SlimeFrame1
    .byte #>SlimeFrame1             
    .byte #8                        ;sprite height
    .byte #$86                      ;color
    .byte #<EnemySlimeUpdate
    .byte #>EnemySlimeUpdate
    .byte #1                        ;hp
    .byte #1                        ;damage

    .byte #&lt;SlimeFrame0
    .byte #>SlimeFrame0             
    .byte #&lt;SlimeFrame1
    .byte #>SlimeFrame1             
    .byte #8                        ;sprite height
    .byte #$D8                      ;color
    .byte #<EnemyGreenSlimeUpdate
    .byte #>EnemyGreenSlimeUpdate
    .byte #1                        ;hp
    .byte #1                        ;damage

Actually, on the 6502, this is a pretty inefficient way of laying out data, which I'll talk about another data. But anyway, for each room, I store an index of which enemy type we're dealing with (ie #0 for slime, 1 for Green Slime). Then I multiply that number by 11 (the number of bytes per enemy), add it to the address of EnemyDefSlime, and we have the address of our enemy definition.

But the switch was my 24th enemy. 24 times 11 is 264. (do you see where I'm going with this?) The 8 bit number I used to track the index overflowed.  

Well, I really didn't want to switch to a 16 bit number for this (math beyond 8 bits is a hassle on the 6502), so I ended doing a one-off hack, and hardcoding a special case for #24 to just go to the right definition.

The update code for the switch was tiny, and I managed to squeeze it in by doing another optimization pass at my other enemies.  But now I needed to add code in the door handler to open/close the doors based on my switch.  But my bank that handled that logic was also full. So I ended up moving a bunch of code to my kernel/graphics bank to free up space in my main bank.

Long story short (too late), I got it working. I have exactly 0 bytes of space remaining in my allocated enemy area, and 1 byte left in my main bank.  I still have 200 bytes of space in the graphic bank and the room definitions bank, so there's a TINY bit of wiggle room left.

But not much.  Let's hope I don't find any other bugs, or come up with any other crazy things that I feel I need to add.

Edit: Oh no, I just realized that I forgot to actually implement the power increase that happens when you find the power ring item. That would only be about 5 bytes, but it needs to be in the main bank. Sigh.


sverx said…
Are you sure you can't cram all the enemy info into 10 bytes instead of 11? So you'll have room for 25 enemies. Some of your info in those structs look like they can actually occupy just few bits, or can be turned into an enum to save even more...
Nathan Tolbert said…
Great question. I totally could, although it means sacrifices other places. Really, I should have done it completely differently (parallel arrays), but now that I'm this far along, it comes down to optimizations within the enemy definitions vs the amount of code in other places to support it.

For example, the last byte (enemy size and missile size) is currently in the right format to copy directly to a sprite hardware register. I could probably overload it with more information, but I'm not sure I'd save more than the 24 bytes in the long run once I wrote the code to break the data out and get it in the right format. It was just easier at this point to do my cheesy hack instead. But I like your line of thinking...I might do a blog post just detailing some of the options I could have done to improve it.

Popular posts from this blog

Retrospex 32 Review

RetrospexInternational recently sent me a couple units of their new handheld device, the Retrospex 32, a new dedicated GameboyAdvance emulator handheld.  To make the unit playable out of the box, they pre-loaded a handful of homebrew games, including Anguna, which is why they were kind enough to send me 2 of the units to play with.  I was pretty excited to get my hands on the device and try it (I loved my old GBA micro with a good flash cart!), and see Anguna running on it. So here's my thoughts after playing with it.

Their website lists the Retrospex 32 for £59.99, which is around $100 USD. It seems like it's marketed toward people into retro-gaming (which makes sense for a dedicated GBA emulator device). At that price, with that target market, and such a limited set of functionality (why not make it a multi-machine emulator, and emulate all the old consoles?), it would hopefully do a really good job of it.

The short version of my review: it doesn't. It has one job (emula…

Making the game fun

The real trick for Spacey McRacey (as I'm calling it now) is going to be making it fun.  And that's what I'm rather unsure about at this point.

I have a game design that basically works. The technical issues are mostly sorted out, I just need to get a few more implemented before I can seriously play test it.

But fun? It's hard to know if it's actually going to be any fun to play.  With a 4-player party-style game, it's seems like it might be hard to hit that fine line where everyone is close and competing, where everything feels exciting and tense, as opposed to tedious and boring.  And despite envisioning my game as fun, it might just be boring to play.

Some of that comes down to tweaking it. Tweaking the speeds, difficulties, etc, will make a difference. (If it's too easy to shoot people from behind, then it will be nearly impossible to hold a lead for very long, which could ruin it and make it no fun. If it's too hard to kill the guy in front, it wil…

Killer Queen

So at PRGE, I played an arcade game that just left me amazed.  Killer Queen.

It's a 10-player game. You have 2 cabinets linked together, and 5 players huddled on each one. Each one is a team of 5 people, working together to play a simple one-screen 2d platformer.  But what made it work was the high quality game design.

First, the game is relatively simple, yet there is a lot going on at once.  One player plays the queen, the most important and powerful character on the team. The others start as workers, but can become warriors who can fly around and attack in a very joust-like flappy contest of height.  The real trick is that there are three completely different ways to win: either collect a bunch of berries and bring them back to your base, or ride a REALLY SLOW snail across the screen (while other people try to kill you, and you hope your team protects you), or kill the enemy queen 3 times.  There's some other things going on as well (using berries to upgrade, capturing upgr…