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.
I recently realized that I was doing it wrong in my room definitions. Previously, I was storing the addresses of each section of background wall data, like:
TestRoom.byte#<TestRoomLineDefsA ;background definition..byte#>TestRoomLineDefsA ; The < and > signify low and high bytes of .byte#<TestRoomLineDefsB ; the pointer address..byte#>TestRoomLineDefsB.byte#<TestRoomLineDefsC ; Atari backgrounds are made up of 3 chunks.byte#>TestRoomLineDefsC ; of data which is why there are 3 pointers..byte#ROOM_COLOR_BROWN ;room colors.byte#ROOM_COLOR_GRAY.byte#TOP_CLOSED ;door flags
.byte#<EnemyDefSlime ;what enemies are in this room.byte#>EnemyDefSlime.byte#3 ;number of enemies.byte#NO_ITEM ;any special items in the room
That's 6 bytes for each room that are used to point to the background data. Realistically, there's no way I'm going to have more than 255 different room backgrounds. (There's…
To be somewhat true to the GBA version, Atari Anguna needs doors: doors that open with the right colored key, doors that open when all enemies are killed, and fake walls that don't look like doors, but allow you to walk through them. Plus, since I'm reusing room wall layouts to save cartridge space, I want the ability to say "use room XYZ but seal off the left door"
This was the new challenge this month. I had to find a mechanism for dynamically drawing doors. The general idea would be just to fill in the "door" opening in the wall with more wall -- either some other color for a locked door, or with the wall color for sealed or fake walls.
The trick is dynamically doing this. The top and bottom walls/doors weren't too bad. I made a rule that no enemies will overlap those scanlines, and, by writing separate kernel code for those lines, gave me enough time to modify the background dynamically. It's easy enough to just modify the "background color…
Both Robo-Ninja and Atari Anguna have been moving forward the past few days, although I haven't had much interesting to say about them.
On Robo-Ninja, I've finally gotten far enough along in the mapping where I can finally assume that, by this point, the player has most or all of the powerups from the first 2/3 of the game. Meaning I can finally switch up a lot of the level designs to force all sorts of difficult combinations and toggling of abilities. For example, in room "cave_10":
If you can follow my horrible paintbrush drawing....you start dropping down at the purple arrow on the right. You have you double-jump at the blue part, and while in the air, change the "ground action" ability to slide instead of jump, so you can slide under the wall (orange). Then immediately toggle back to jump (and make sure your wall ability is set to wall jump) to handle the yellow jumps. Then you have a very short stretch to toggle your in-air ability from double-dump to…
I had an interesting observation the other night. Switching back and forth between Robo-Ninja and Atari Anguna makes me much more aware of the differences in how I write code.
For example, when writing code for enemies in both games, I often need to know about various pieces of state that live outside the enemy -- such as the position of the player. (But not limited to the state of the player, which is what makes this tricky). In a game designed with a modern language and architecture, you have to make a lot of decisions about how you both provide and limit access to the state that the enemy would need. How do you expose certain state to the enemy, without giving it access to all sorts of things it shouldn't have access to? There are lots of solutions to that problem, but it's a big part of thinking through the design of a system. (Tangent: I find this to be tricky in hobby-style video games, because I'm always dreaming up new enemies and mechanics that require access to …
The past couple nights have been devoted to getting sound working in Anguna 2600. Unlike video, sound is actually pretty easy, as long as you aren't too picky and don't want it to sound like much. There's 2 sound channels, each one having 3 registers: one for volume, one for waveform (one of 7 or 8 presets), and one that controls frequency. Trying to get any particular notes or scales out of it is near impossible, but getting rough sound effects is pretty easy.
Although I spent way too long last night fiddling with it before realizing that I couldn't set the sound registers before the first video frame for some reason. When I did that, everything locked up. I'm still not sure why, I couldn't find any reference to that in the documentation. Oh well.
My first successful test was to pick a semi-random waveform (#4, which is basically a simple square wave), and tie the frequency to the player's X position, to test it out. This ended up accidentally producing th…
After much mucking about, I finally have the enemy wall collisions working. My general algorithm that I mentioned before was mostly correct, but it needed a little tweaking. The way it works now is:
Check if there was a wall collision last frame. (which would only occur if the enemy was actually drawn!) If so, reset to the "last drawn" position.If no collision and enemy shown, copy the current position to the "last" positionMove the enemyDetermine which enemies will be shown this frame. If shown, copy "last" position to "last shown" positionClear the collision flagsRedraw (and capture changes to the collision flags)
The part that really threw me off was the bold part in step 2. I was copying the current to the "last" position even if the enemy wasn't shown, which, once I worked it out on paper, turned out to let the enemy advance slowly through a wall. But it all works now!
Once that was finished, I also managed to nearly recreate m…
So I got a lot done this weekend (not counting the work I did on my back porch, but this isn't a home improvement blog, so....)
I got the basics of the scripting system working for cutscenes in Robo-Ninja. I reworked my original design slightly (to put the entire script in a single file, instead of a different script for each "puppet" in the scene). I still need to flesh out a number of the script commands, but I have a very ugly basic cutscene working, where Robo-Ninja stands there as Prof. Treeblot walks away from him, and then a buggy half-broken Dr Squidbrain shows up. On Atari Anguna, I did some thinking, and realized the way I had written 3 completely separate kernels was dumb. I managed to rework it so that the the smaller, simpler kernels were just subsets of the bigger more complicated kernel. They are a couple cycles slower, as I had to add a branch to possibly jump out of the kernel loop for the smaller kernels, but I managed to squeeze the branch into a spot wh…
So for the past couple weeks, I've been putting off working on Atari Anguna because I've been up against a bug that sounded difficult and boring to solve: whenever I killed an enemy, it messed up my kernel logic, and everything went haywire.
What's supposed to happen, is that every frame, I sort my enemies by Y position, and then compare all their Y values, select the right kernel mode, then draw them in order. If an enemy is dead, I'd sort it to the end, and select kernel modes that ignored that enemy. But it wasn't working.
I sat down to tackle this today, and happily, it only took about 10 minutes. It was a simple logic error in my sorting routine, which occasionally caused a dead enemy to get sorted to the front of the list.
(Tangent time! I ended up using a *gasp* Bubble Sort for sorting these enemies. Why? Because I realized that for N=3, which is my case, Bubble Sort only requires 3 comparisons (a/b, b/c, a/b again). So it's a really simple sort to imple…
Tonight was back to Robo-Ninja. First, to make Aaron happy, I went ahead and created the jetpack item. He's been wishing for that non-stop for months, so I thought it would be a fun surprise to add for him. Plus, it was one of those few times that something was really easy. It helped that it's so similar to the double-jump item in function.
Then, I felt like doing something different (the scripting stuff, while interesting, required some serious thought and design work, which I didn't feel up to tonight), so I added something I've been planning for awhile: the death counter. What good is a hard game if it doesn't regularly remind you about how bad you are at the game?
With Anguna, when I released it, I had a little contest, where the first person to win the game with 100% of the hidden items being found, won a free copy of the game. It was a fun way to drum up some interest and attention. (and was fun from my end -- the 2nd place winner was only a few hours behind …
Spent awhile tonight working on trying to get my 3-enemy kernel working for the atari game. It basically amounted to a ton of branches, checking to see which enemies were close, which frame I was in, etc.
Basically, I have to see which enemies are close to each other vertically, pick the appropriate kernel (whether to render 1, 2, or all 3 enemies), then swap around the enemies in the enemy array to make sure that the correct enemies are rendered for this frame (since enemies that overlap a row only get rendered on alternating frames).
So tonight was my first pass at it. It works in a few of the cases, but completely freaks out in a few of the arrangements. So now I get to start the fun job of isolating which cases fail, and debugging. This should be fun.
So now on Robo-Ninja I have the fun little diversion of figuring out how to do the scripted sequences for the beginning and ending stories. It will consist of little more than a handful of characters (Robo-Ninja, Prof. Treeblot, and Dr ?, maybe a robot guard) moving around and having word bubbles over their heads.
But I need a nice way of scripting it.
So that's what I'm currently working on, which is fun because it's a different sort of thing. I decided to not go all out and embed a lua interpreter in my game, but I AM making a super-simple script language that I'll run for each character.
I've extended my GameCharacter class (which handles drawing characters, animated them, detecting collisions with other characters) to create a new class that follows a script. Now I just need to actually implement the script system (and parser!)
I haven't finished planning it out fully yet, but I'm envisioning something like:
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.
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, an…
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…
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.
The past couple weeks I've been focusing on making new maps. At this point, I'm done with the temple zone, and almost done with the industry zone.
I think I mentioned that I've added a couple of new items -- the "wall slip" and the laser that Robo-Ninja can shoot out of his eye-holes. I thought that might give me a fun programming break from designing maps. But fortunately, it turns out that I actually designed properly for these things, so they were really easy to add. The wall slip, in particular, only took about 15 minutes, by reusing what I had from the wall cling. The laser took a little while longer -- not because the laser itself was difficult, but I had to add functionality to the enemies that lets them die when hit. Either way, it was all straightforward based on the code design.
So back to editing maps. Robo-Ninja's world is starting to grow out a bit, although there's still a good way to go. I think it's time to stop for awhile, so I don…
So tonight I split some time between writing code for a couple of new items in Robo-Ninja (the laser gun, and the "wall slip", where you can cling to and slowly slide down a wall), and working on trying to jam pack everything into my Atari "kernel" (the name they call the core display loop on atari games).
The contrast between the two was hilarious. I'm going slightly over my cycle count on my Atari kernel -- once I added code for displaying missiles and background, I'm taking too much time per scanline. But I don't really want to cut any functionality out, so I've been tweaking and optimizing, trying to pare down an instruction here, an instruction there. My brain is now tired. I managed to squeeze it down to ALMOST small enough. Squeezing the rest is going to be pretty difficult.
Then back to Robo-Ninja. Writing in Java (instead of 6502 assembly!), not really worrying about performance unless I do something stupid. I can waste lines like crazy, a…
So between making more maps for Robo-Ninja (I finished the rest of the Temple zone yesterday!), I'm still playing with my side-SIDE project of learning Atari programming.
Let me just tell you, that machine is insane.
On most modern hardware, drawing game graphics is a matter of setting up a scene in openGL, (or often, some framework that abstracts away the openGL and lets you tell it what stuff to draw where).
On the GBA, it was a little harder. You had magical memory registers for video ram, which you'd fill up with appropriate graphics, you'd poke palette data into a different register, then you'd have other registers that told it how to build and display sprites or background tiles based on all that. You could, for example, fill in a register to say "put a 16x16 sprite, flipped horizontally, at position X,Y, based on graphic data at video ram location FOO" You still had to do a good bit of work figuring out how to swap data in and out of the limited vide…
The thing that most causes me to give up and stop working on a hobby project is my tendency to decide on a new "ooh, shiny" programming project. I can't tell you how many times I've started something, but gotten distracted by something else along the way.
As I was working on Anguna, there were numerous times where I had to promise myself that I'd work on that "new shiny" project just as soon as I finished Anguna -- that was the only way I could keep myself motivated to finish instead of just giving up and starting the next project. Once I finished, I forgot what all of those other distracting projects were, so it no longer really mattered.
Well, suddenly I've discovered a new shiny. It's not really that new -- ever since about 2005 or so, I've had it in the back of my brain that eventually I wanted to try making an atari 2600 homebrew game. But somehow recently I got interested enough to spend some time reading up on how it worked. AND NOW I…
So I've recently purchased DosBox Turbo, the best (as far as I can find) implementation of DosBox for Android, and have been playing through old DOS games on my phone. It's been pretty awesome -- I've been currently playing Civilization 1, which is one of the best games ever, in my opinion.
The only problem is that for Civ, you need a keyboard with arrows and diagonals, as well as a number of regular keyboard commands. I've used the awesome Hacker's Keyboard for other games, but for Civ, I end up having to toggle back and forth between the "normal" and "alternate characters" modes repeatedly. And I'm using PgUp, PgDn, Home, and End keys for the diagonals, which aren't arranged intuitively on that keyboard. So it's a bit of a hassle.
So the other day, I thought, "how hard can it be to write a new keyboard layout to make this game easier to play?" Turns out the answer is "not very hard." The android sdk distributio…
So tonight I was happily finishing up a level, and getting ready to start on the next, and checking my paper map that I sketched out about a year ago, when I realized I made a mistake. The map is completely missing the Double-Jump item. Yes, it's required, but no, there's nowhere to find it.
I need to now rethink a little bit of my map design to figure out where that's going to fit.
On a completely unrelated note, today I added flame shooters. What's a 2d side-scrolling game without flame shooters? (And as my office-mate Tim said "clearly it wasn't hard enough already, you needed to add something else to kill you")
The work for the past couple nights has been redoing how consumable items work. Like I mentioned before, the problem was that players would likely just stockpile their slow-mo and checkpoint items, saving them for the "who-knows-what" later in the game. I needed a way to encourage players to use them, but not overuse them.
The solution I decided on: every time you pick up a consumable powerup, it adds 1 to your max that you can carry of that item. Whenever you use one, a progress bar VERY SLOWLY starts filling, and when it finishes, you gain back 1 of that item. I can easily tweak the amount of time it takes to regenerate your items, to play with making the items more or less precious. Currently, it's set at 5 minutes of real game time, which feels about right to me as a first pass, but we'll see.
This was one of those changes that was surprisingly easy. I had to track a few more pieces of overall game state (max # of an item, and how far along the regeneration counte…
Well, thanks to my office-mate Tim, I have an old nearly-useless iphone 3g that I decided to see if I could get Robo-Ninja running on. LibGDX can build to iOS using RoboVM, so as much as I doubted it would work, it sounded fun to try. (RoboVM is a tool for compiling java code down to iOS native code, the similarity in naming is purely coincidental)
Step 1, since I'm not interested in paying Apple a $100 yearly subscription to run my code, was to jailbreak the phone. That was painful enough, searching for information and jailbreak files for a phone that old. Piles of broken links and outdated information.
Then I had to borrow a Mac laptop, since it will only build on a Mac. I installed RoboVM and all the necessary tools, and relatively quickly got Robo-Ninja running in the iOS simulator. It was a little slow, but it was Robo-Ninja!
The next hurdle was figuring out to build the ipa file (the iOS app package file, equivalent to the apk files in Android) without actually signing it wi…
Just I did with ramps, I suddenly decided I needed a new gameplay element that throws off my previous design. This, kids, is why you actually plan things out.
This time it was moving platforms. You know, like many games have, a platform that moves up and down or side to side. Nothing revolutionary here. Although my design, for convenience, originally assumed that everything was on a strict grid. So I took some shortcuts by, for example, snapping your character to the grid when he was standing on a platform. And I also got to assume that the gridded static map tiles were the only things that the main character could collide with for "wall" collisions.
Suddenly these moving platforms could cause collisions at any point, not just on the exact grid lines. And the platforms aren't part of my tile map -- they're actually set up as "enemies". So I had to go back and update my collision code to allow it to both function off the strict grid, as well as to provide a…
About half the time when I design a level on Robo-Ninja, I'm moderately dissatisfied with it when I'm finished. It's either too hard, too easy, not interesting enough, or too dependent on getting lucky on your timing instead of actually figuring out how to do it properly.
Tonight, I finally built (most of) a level that I'm quite happy with. It's not hard to get through once you figure out the timings and what to do. But it will probably take a few tries to figure out those timings. And the arrangement of elements on the screen isn't as boring as some of the other maps I've done recently.
I've got quite a few levels to go...hopefully I can keep this up.
So I guess I haven't posted in almost a month. Mainly due to a combination of:
Only having about 30 minutes to an hour at any given time to work on RoboNinja, so by the time I get some work done on it, I don't have as much time to talk about it.The kids had "no screen week"in the middle of May, which meant I decided to take a week off from playing on my computer at home. ("playing" including my hobby programming!)I've had more home repairs than usual, so I've been busier in the evenings than I'd like
Anyway, despite all that, I've been still gradually making progress. First, I managed to get the minimap sorted out -- my build script goes through each map file and builds a minimap image once at build time. Then as you go through the world, it copies little sections from that image to a blank image that is gradually built up to be the minimap that the player can see. When the player saves his progress, I just save that image, and keep gradually…
Well, after I said that I wasn't going to bother getting the gradle build working, it's time to eat my words.
About a week or so ago, Mario (the man behind libGdx) announced that they finally were officially releasing version 1.0 of libGdx. I've been dragging my feet about trying to keep up with changes to the library, as it's not completely backwards-compatible, and I didn't want to get on the treadmill of trying to keep changing my code to stay up-to-date. But after some consideration, I figured a 1.0 release was worth migrating to.
The biggest change between my old version (from about a year ago) and 1.0 was the directory structure and build scripts for your game. Like I mentioned before, they switched to a gradle-based build system (which is quite nice), and with that came some rearranging of the project directories.
I figured the easiest route would be to create a new blank project using their nice project creation tool, then move all my code into the right pl…
Well, after finally get the exporter in Tiled to do what I wanted, (and as a side note: my changes got accepted into the project today. Tiled has one of those maintainers that's actually pleasant to work with!), it was time to make my build scripts to actually generate my minimap. Which made me realize it was time to actually have proper build scripts.
When I started with libGdx (almost a year ago?), it didn't have a good build system prepared with it. You pretty much rolled your own build scripts, or just used the "build" button in your IDE (which embarrassingly, is what I've been doing). The author has recently come out with a gradle build script, but that would require me going back and updating all of my libGdx stuff to use the newest version. Which I'm not ready to do at the moment.
Well, I found someone else's ant script (yuck!) for how my project is set up, so I figured I could start with that, to at least get a scriptable build started. I'd r…
Well, after submitting my changes to Tiled, and discussing it with the maintainer, we decided that instead of working around their goofy hard-coded ommision of the collision layer, it might be worthwhile to completely rework the command-line flags for including or omitting layers. So instead of working on Robo-Ninja directly, I spent the evening working on Tiled.
Specifically, adding command-line flags to their export utility that let you specify layers by name to omit, and let you also tell it to just use the visibility settings of the map itself.
This code is all in C++. I haven't written anything in C++ in YEARS.
Today I started investigating how I want to handle the mini-map. I still haven't completely settled on a design that I like. Some of the questions or factors that I need to think about are:
The minimap needs to be smart enough to (at least mostly) build itself from my level data, without me having to go back and build a big minimap by hand.I want to minimize the effort involved -- I neither want to have to add a bunch of meta-data to my maps to give them hints about displaying themselves on the minimap, neither do I want to have to write a ton of code for the build process to make it build the minimap.How much detail do I want to show on the minimap? In Anguna, the dungeons were just wireframes of the rooms (which is closer to a Super Metroid-style map), but the overworld was actually just the image of the overworld map scaled down to any extremely tiny size.
I'm leaning toward doing scaled down images (in answer to #3), so I started looking into a simple scriptable way to dump…
So this week, I'm back at Robo-Ninja. I think the trick to getting me to finish a development project is to have a fan. Having Aaron pester me about it is a good motivation!
So during approximately 10 minutes last night, and 15 tonight, I managed to get the consumable slow-motion powerup working. It was one of those happy times where everything fits smoothly into your design without bumping into any nasty corners.