So Eli at Piko Interactive was testing Anguna, and kept running into a problem where the main boss would disappear halfway through the fight. Not that the dragon would really go away, but the sprite would disappear, but the collisions/bullets/etc would still continue.
Try as I would, I just couldn't reproduce it. He said it happened 100% of the time. But I couldn't see it happen. He sent me a save state and said that it should happen just right after loading from the save, but I still couldn't reproduce it. Finally, though, I just entered the cheat code to get tons of health, put the phone (emulator) down, and when I picked it up, he was gone.
So after quite a few more experiments, I somewhat sorted it out: It happened at 3ish minutes into the boss fight, every time, like clockwork. But the way I played, I never lasted 3 minutes in that fight. I always played it more aggressively, and was either dead or victorious in a minute or two. I never saw the bug, because I played the game differently (this is a good lesson to us programmers: sometimes people use our software differently, and expose completely different behavior than what we usually see! As a side note, the other day I had a problem at work, where a user said a field input NEVER worked. And it always worked for me in testing. Turned out, she always clicked the submit button, where I always pressed enter. The enter key worked. The submit button didn't. oops)
Anyway, once I figured out how to reproduce it, it didn't take long to find the problem. I opened up the code for the boss fight, and saw that the animation for the face (which only had one frame) was set up to display for 9999 game frames...then was supposed to start over (back to the one frame again!). 9999 game frames was about 3 minutes. Aha! Turns out my code to start the animation over wasn't working right for these one-frame animations.
Which meant that there might be other issues with single-frame sprites. I dug around and looked -- most of them were set to some absurdly high number of game frames before they "repeated". Turns out, if you put the game down for about 30 minutes without moving, a bunch of things (doors, items, locks, etc) timed out and disappeared. I guess nobody's ever tried doing that, that bug has been there from the beginning, but nobody has ever reported it!
It only took one line of code to fix it, and make the single-frame animations loop properly. Weird to find and fix a bug that serious and that old, with 1 simple line of code.
Assuming no other issues appear, manufacturing for the cartridges should start soon, according to Eli!
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.
Thursday, June 25, 2015
Saturday, June 20, 2015
Atari Green Toady
So the Green Toady in Anguna is the first somewhat difficult challenge the player faces. Unlike the simple slimes in the first few rooms, the Green Toady not only take a number of a hits to defeat, but also has a more difficult movement pattern:
He first wanders slowly and randomly, but as soon as he is lined up (or more accurately, close-to-lined up) with the player (on either the X or Y axis), he pauses, then charges quickly at the player, until he hits a wall.
Tonight's challenge was to get that working on Atari Anguna. This is the most complicated enemy so far on the Atari, and would require a bit more code than the other enemies. The trickiest part was something that should be simple for anyone with any real 6502 experience, but required some thought for me: figuring out a reasonably efficient way to determine if two numbers are within a certain range of each other. (ie I wanted to see if the enemy X position was within 5 (plus or minus) of the player X position).
What I ended up doing is below, although it's late, and I don't have the brain power to really figure out if there's any edge conditions that I'm missing involving overflowing between positive and negative numbers. (The X register contains the index of which enemy we're dealing with, as there can be more than 1 enemy onscreen at once):
I'm loading the first value, subtracting the second. Then add 5. I check if the result is negative. If so, they're more than 5 apart in the one direction. Then subtract 10, and check if the result is positive. If so, it's more than 5 apart in the other direction. If neither is the case, then they're within 5 of each other. It's only 16 cycles, so pretty efficient. I didn't bother to set or clear the carry flag, so it's not going to be exact (I could be off by 1, depending on the carry flag's state coming into this), but I'm not too worried -- for this case, "around 5" is all I need)
Anyway, the amazing part, is that after fixing a couple typos, the toady mostly worked on the first try. That NEVER happens.
I do have a couple minor tweaks to make him work better (I put the charge on a timer which is too short, and I didn't initialize him into any state, so he just sits there for awhile when he's first on the screen). But still, it was certainly an unexpected lucky outcome for a relatively complex enemy.
He first wanders slowly and randomly, but as soon as he is lined up (or more accurately, close-to-lined up) with the player (on either the X or Y axis), he pauses, then charges quickly at the player, until he hits a wall.
Tonight's challenge was to get that working on Atari Anguna. This is the most complicated enemy so far on the Atari, and would require a bit more code than the other enemies. The trickiest part was something that should be simple for anyone with any real 6502 experience, but required some thought for me: figuring out a reasonably efficient way to determine if two numbers are within a certain range of each other. (ie I wanted to see if the enemy X position was within 5 (plus or minus) of the player X position).
What I ended up doing is below, although it's late, and I don't have the brain power to really figure out if there's any edge conditions that I'm missing involving overflowing between positive and negative numbers. (The X register contains the index of which enemy we're dealing with, as there can be more than 1 enemy onscreen at once):
lda Enemy0X,X sbc PlayerX adc #5 bmi .NotLinedUpX sbc #10 bpl .NotLinedUpX ;chase main char only on Y axis
I'm loading the first value, subtracting the second. Then add 5. I check if the result is negative. If so, they're more than 5 apart in the one direction. Then subtract 10, and check if the result is positive. If so, it's more than 5 apart in the other direction. If neither is the case, then they're within 5 of each other. It's only 16 cycles, so pretty efficient. I didn't bother to set or clear the carry flag, so it's not going to be exact (I could be off by 1, depending on the carry flag's state coming into this), but I'm not too worried -- for this case, "around 5" is all I need)
Anyway, the amazing part, is that after fixing a couple typos, the toady mostly worked on the first try. That NEVER happens.
I do have a couple minor tweaks to make him work better (I put the charge on a timer which is too short, and I didn't initialize him into any state, so he just sits there for awhile when he's first on the screen). But still, it was certainly an unexpected lucky outcome for a relatively complex enemy.
Monday, June 15, 2015
Moving from sourceforge
And along with this, I decided to move the DS version of Anguna (which was open-sourced awhile back, and sitting on sourceforge) away from sourceforge. Because if you haven't paid any attention to the news, Sourceforge has gone nasty.
So the source is up on bitbucket now. I still have some cleaning up I really need to do, but there it is.
So the source is up on bitbucket now. I still have some cleaning up I really need to do, but there it is.
Sunday, June 14, 2015
DS build (probably?) working again!
So again thanks to Sverx, I think I have the DS build of Anguna working again.
In response to one of my previous posts lamenting that I couldn't find a 2008 release of the devkitPro/libnds tools, he mentioned that he had a copy! He sent me what he had, which turned out to be the windows version.
Although GBA anguna was developed on windows, I developed the DS version on linux. So everything I had was set up for linux, and I'm currently using (and running my builds on) linux.
My first attempt to get it working was to just use my current version of my gcc arm cross-compiler with the 2008 version of libnds, which as I suspected didn't work. It compiled, but the linker complained about all sorts of things.
I didn't want to go all the way and set up everything in windows yet, so the next try was stupid: use all the windows toolchain compilers via wine. Has anyone EVER run windows versions of gcc on linux using wine before? A terrible idea. But it seemed to work when I compiled it. (despite taking quite a while to compile!) But when I ran the finished rom, it had all sorts of weird issues once you got into the game, past the splash screens.
But this seemed familiar, similar to the problems I had trying to compile the gba version on linux recently. So I went through and made sure to convert all the files' line endings to unix (not sure why that would be an issue, since they were originally done in linux), but then everything worked!
So despite the terrible idea of running all compilers in wine, I know have DS builds working again. (I think? I haven't tested it yet past the first couple of rooms)
Maybe that means the new dungeon will eventually get ported to the DS!
In response to one of my previous posts lamenting that I couldn't find a 2008 release of the devkitPro/libnds tools, he mentioned that he had a copy! He sent me what he had, which turned out to be the windows version.
Although GBA anguna was developed on windows, I developed the DS version on linux. So everything I had was set up for linux, and I'm currently using (and running my builds on) linux.
My first attempt to get it working was to just use my current version of my gcc arm cross-compiler with the 2008 version of libnds, which as I suspected didn't work. It compiled, but the linker complained about all sorts of things.
I didn't want to go all the way and set up everything in windows yet, so the next try was stupid: use all the windows toolchain compilers via wine. Has anyone EVER run windows versions of gcc on linux using wine before? A terrible idea. But it seemed to work when I compiled it. (despite taking quite a while to compile!) But when I ran the finished rom, it had all sorts of weird issues once you got into the game, past the splash screens.
But this seemed familiar, similar to the problems I had trying to compile the gba version on linux recently. So I went through and made sure to convert all the files' line endings to unix (not sure why that would be an issue, since they were originally done in linux), but then everything worked!
So despite the terrible idea of running all compilers in wine, I know have DS builds working again. (I think? I haven't tested it yet past the first couple of rooms)
Maybe that means the new dungeon will eventually get ported to the DS!
Friday, June 12, 2015
Corrected....
In my previous post about the volatile keyword, Sverx left an insightful comment, noting that because nothing is changing the SRAM data outside of my code, it shouldn't really need to be marked as volatile. Which is true -- nothing really changes it at run-time.
So what was the actual problem and why did marking it as volatile correct it?
For one thing, as sverx mentions in the comment, SRAM can only be written 8 bits at a time. Which I'm doing (casting my data into 8-bit chars and looping through writing them one at a time). But now I'm wondering if the newer versions of gcc saw that, decided I was stupid, and optimized it into 16- or 32-bit writes. Which would make sense why adding debugging messages in the inner loop would change it. Marking as volatile might also have been enough to scare the compiler off from over-optimization, and fixed it as well, although not quite as correctly.
Thanks sverx!
So what was the actual problem and why did marking it as volatile correct it?
For one thing, as sverx mentions in the comment, SRAM can only be written 8 bits at a time. Which I'm doing (casting my data into 8-bit chars and looping through writing them one at a time). But now I'm wondering if the newer versions of gcc saw that, decided I was stupid, and optimized it into 16- or 32-bit writes. Which would make sense why adding debugging messages in the inner loop would change it. Marking as volatile might also have been enough to scare the compiler off from over-optimization, and fixed it as well, although not quite as correctly.
Thanks sverx!
Wednesday, June 10, 2015
Updates
So here's where everything's at right now:
GBA Anguna: new content is finished, the rom is being playtested by folks at Piko Interactive (which they've already found (and I've fixed) one bug), hopefully soon will be sent off for production!
Robo-Ninja: The game is pretty close to finished. This week I've been working on the ending sequences and more google play services integration. I might skip the cloud saved games (it looks like a bit of a pain to convert my save game format to work with it, but we'll see). The biggest change is that Chris (the guy that did the graphics for Anguna and the new awesome Robo-Ninja character graphic) has some great looking designs for improvements to other graphics in the game, including all the UI bits, so I'm planning to wait to see what he comes up with, and probably do a bit of reworking all the UI to use his designs. (although there's still a little bit of work to do until then: tweaking some level designs, testing, and improved keyboard controls for non-touch screens (I plan to provide both a java desktop download, as well as a Google Chrome app)).
Atari Anguna: This poor guy has been getting neglected thanks to the work on GBA Anguna and trying to do a mad dash to finish Robo-Ninja. The core engine is in pretty good shape, and I need to build out a test dungeon to do some more in-depth testing. The next steps will be getting the header/hud thing working right, and then secondary items (bow & arrow, dynamite, etc).
GBA Anguna: new content is finished, the rom is being playtested by folks at Piko Interactive (which they've already found (and I've fixed) one bug), hopefully soon will be sent off for production!
Robo-Ninja: The game is pretty close to finished. This week I've been working on the ending sequences and more google play services integration. I might skip the cloud saved games (it looks like a bit of a pain to convert my save game format to work with it, but we'll see). The biggest change is that Chris (the guy that did the graphics for Anguna and the new awesome Robo-Ninja character graphic) has some great looking designs for improvements to other graphics in the game, including all the UI bits, so I'm planning to wait to see what he comes up with, and probably do a bit of reworking all the UI to use his designs. (although there's still a little bit of work to do until then: tweaking some level designs, testing, and improved keyboard controls for non-touch screens (I plan to provide both a java desktop download, as well as a Google Chrome app)).
Atari Anguna: This poor guy has been getting neglected thanks to the work on GBA Anguna and trying to do a mad dash to finish Robo-Ninja. The core engine is in pretty good shape, and I need to build out a test dungeon to do some more in-depth testing. The next steps will be getting the header/hud thing working right, and then secondary items (bow & arrow, dynamite, etc).
Friday, June 5, 2015
Robo-Ninja Boss Fight
My office-mate Tim has been so kind as to play through Robo-Ninja, giving me great feedback along the way. A week or so ago, he finally got to the end -- at least so far. He got to the room right before the main boss. And has been patiently waiting for me to give him a boss to fight.
Well Tim, we're almost there. I think the boss is done. Although once you kill him, I don't have any sort of victory sequence or anything. He dies and you're just stuck in his room. But hey, progress.
I ended up NOT giving him multiple forms after all. Partially because I'm ready to finish this thing, and partially because while testing it, he seems hard enough already.
So now there's just a few things left to do:
- A few map tweaks based on Tim's feedback
- Fix a couple of bugs in my game engine (again, based on Tim's feedback)
- Add graphical icons on the teleporter UI (I've had blank placeholders sitting there for months now)
- Finish the options and license/credits screen (I've used a ton of Creative-Commons licensed art and sounds, so I have a lot of credit that needs to be mentioned)
- Finish the leaderboard and cloud save integrations
- Add an ending sequence
- Test, Test, and test again
- Release the game, get rich, and retire
No DS re-release for now....
So today I download the source to the Nintendo DS version of the Anguna to see if I could easily get it to build, to push the new level to it as well.
Didn't turn out so well so far.
The development kit for GBA and NDS development (devkitPro) has both the compiler toolchains/build tools/etc for development, as well as some libraries for interacting with the various platforms. The libraries are mostly thin wrappers around the basic functionality (nice names for the various registers, utility functions to deal with them, etc), but there's a few other bigger bits, such as sound libraries, etc.
On the GBA, I didn't use any of the devkitPro libraries directly, and instead just managed my own, using just the simple register definitions.
On the NDS, I ended up using a lot more library calls from devkitPro. Which turned out to be the problem here. The NDS libraries on devkitPro have changes significantly in newer releases. And I'm having trouble coming up with a version of the library from 2008 that works properly. Unless I can magically come up with a working version of libnds circa 2008, the only other solution is to rewrite quite a bit of code to work with the newer version of libnds. No thanks.
Didn't turn out so well so far.
The development kit for GBA and NDS development (devkitPro) has both the compiler toolchains/build tools/etc for development, as well as some libraries for interacting with the various platforms. The libraries are mostly thin wrappers around the basic functionality (nice names for the various registers, utility functions to deal with them, etc), but there's a few other bigger bits, such as sound libraries, etc.
On the GBA, I didn't use any of the devkitPro libraries directly, and instead just managed my own, using just the simple register definitions.
On the NDS, I ended up using a lot more library calls from devkitPro. Which turned out to be the problem here. The NDS libraries on devkitPro have changes significantly in newer releases. And I'm having trouble coming up with a version of the library from 2008 that works properly. Unless I can magically come up with a working version of libnds circa 2008, the only other solution is to rewrite quite a bit of code to work with the newer version of libnds. No thanks.
Thursday, June 4, 2015
Volatile
I spent about 2 hours last night pounding my head in a wall trying to figure out the solution to a new bug that I ran into: Anguna's save game function was no longer working.
In the GBA, the most common save method is to just write to a magical area of ram that happens to be battery-backed, so it retains its values. So when I save, I write the game state to that area, including a checksum. On loading, I read the checksum to see if there's a valid game state in ram (as opposed to un-initialized garbage), and if it's valid, load it.
So last night, I was writing the data, but then when I read it back out, the checksums didn't match!
So I started debugging. I logged the first few bytes writing to save, and logged them again as I read them...everything looked right. So the first few bytes were working. I tried again with the last few bytes -- they matched also. Weird.
So I dumped ALL the data to a log upon saving and loading. And Lo And Behold, they matched, and it worked correctly. Uh oh.
I removed the debugging, and it broke again.
This is normally an unhappy position to be, although this time it quickly led me to the solution. There were no actual side effects of my logging code (other than the logging itself), so clearly the compiler was optimizing something differently.
Then I checked to make sure that the save ram was marked as volatile. Nope. Oops.
In C, you can mark variables/memory/etc as volatile, which means that its value can be read or written outside of your current program. This lets the compiler know NOT to optimize away read/writes to it.
For example, in the gba, to get the state of the controller inputs, you read a particular memory location. If you don't mark it as volatile, the compiler will notice that you are reading it every frame, but never writing to it, and might optimize away your other reads. Which is not what you want.
In this case, I'm not sure exactly WHAT the compiler was thinking in optimizing away my reads/writes to the save data, but marking it as volatile did the trick. And I guess the compiler's optimization strategies have changed since I last worked on Anguna, as I never had it marked volatile before. Oh well.
In the GBA, the most common save method is to just write to a magical area of ram that happens to be battery-backed, so it retains its values. So when I save, I write the game state to that area, including a checksum. On loading, I read the checksum to see if there's a valid game state in ram (as opposed to un-initialized garbage), and if it's valid, load it.
So last night, I was writing the data, but then when I read it back out, the checksums didn't match!
So I started debugging. I logged the first few bytes writing to save, and logged them again as I read them...everything looked right. So the first few bytes were working. I tried again with the last few bytes -- they matched also. Weird.
So I dumped ALL the data to a log upon saving and loading. And Lo And Behold, they matched, and it worked correctly. Uh oh.
I removed the debugging, and it broke again.
This is normally an unhappy position to be, although this time it quickly led me to the solution. There were no actual side effects of my logging code (other than the logging itself), so clearly the compiler was optimizing something differently.
Then I checked to make sure that the save ram was marked as volatile. Nope. Oops.
In C, you can mark variables/memory/etc as volatile, which means that its value can be read or written outside of your current program. This lets the compiler know NOT to optimize away read/writes to it.
For example, in the gba, to get the state of the controller inputs, you read a particular memory location. If you don't mark it as volatile, the compiler will notice that you are reading it every frame, but never writing to it, and might optimize away your other reads. Which is not what you want.
In this case, I'm not sure exactly WHAT the compiler was thinking in optimizing away my reads/writes to the save data, but marking it as volatile did the trick. And I guess the compiler's optimization strategies have changed since I last worked on Anguna, as I never had it marked volatile before. Oh well.
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...
-
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...
-
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...