Making THE OCTOBER GAME: A Short Research App, but a Loooong Post
Before starting to build the final game, I spent the last few days working on a tracer bullet project. It’s not the same as a prototype. Prototypes have simplified functionality of the actual game. Rather, it’s a tiny research project, to answer questions and validate working methods.
One goal was to get more practice developing apps in passes, rather than all up front.
I also needed to get a better idea of how to structure game level data for two reasons.
- To make it easy to hand-construct new levels
- To load and save from storage in a way that lets me preserve the game state across app launches
I was worried it could turn into a time sink and a distraction from the real game. Luckily, the extremely tight deadline for THE OCTOBER GAME helped me call a halt before I started noodling too badly. Part of the plan was to document what I learned and apply it to the real game.
Since I needed to answer game questions, I had to make a game. It had to have enough complexity to answer my questions but not so much that it took too forever to build.
Here’s what I came up with.
It took a lot of restraint to not make the graphics nicer or add sound.
The game screen consisted of a fleet of 8 assorted enemies in fixed positions. The enemies were of three types:
- Mook – circle
- Tank – circle with outline
- Boss – rounded square
For each level, a different mix of enemy types were spawned. I had a button that set the fleet of them to attack mode in some order; either one-by-one or as groups. Once in attack mode, an enemy could be disabled with a touch, which incremented the game score. Once all enemies were disabled, the whole fleet was marked as defeated and the next level loaded with a new set of enemies. I tried to make it no fun at all. I succeeded.
Here’s what I learned:
After making a Level class to hold the enemy types, their attack order and their colors, I realized that an NSDictionary would do just as well, plus have the added advantage of being easy to extend. You just added more objects and keys.
In that Level class, I made all the ivars NSCoding compliant; that is, added standard methods to convert all members to binary data when saving to disk. This was a huge waste of time. I should have just represented objects like UIColor as a simple string with some hex digits. Strings can be dumped right into a NSDictionary and written to a plist, easy as you please. It’s trivial to turn that into a color when reading back the level data. Plus it’s easy to type when building levels by hand. You can grab the hex value of a color right out of Photoshop or the system color picker.
I subclassed a UIView for the enemy objects, tacking on some properties to hold two colors and the object state. I did a similar thing with the Fleet class, making it a UIViewController. This was a mistake. It violated the Model-View-Controller paradigm and needlessly entangled game data with the UI, making it a pain to save the complete game state. At app termination, I would have to query all the enemies for their states and info, the fleet for it’s states and combine that with the other game data like score and lives left. That’s just too much to do when the app is being shut down. What I should have done was put all the enemy and fleet info into a model class conforming to NSCoding. I’d make them properties of the Game class and just freeze-dry the whole shebang at app termination. I also would have added persistence from the very beginning as it’s tedious and draining to do at the end.
While building the classes, I did write unit tests as I went along. This was a good idea. They caught any number of initializer bugs, bad description methods and other dumb stuff. It also forced me to construct the classes in a way that clarified my thinking on inter-class communication. Since discovering NSNotifications a while ago I became so enamored with them that I used them everywhere. A typical noob mistake, leading to a hopeless ball of yarn. I’ve learned it’s best to have classes structured in a well-formed hierarchy to keep the lines of communication clear and simple. That’s not to say that notifications don’t have their uses. They work great for triggering sounds and for sending important state changes upwards. For instance, when the fleet gets defeated, it broadcasts that information. The root controller listens and triggers a level bump. That way I don’t have to constantly poll for state info or keep count of enemies destroyed. Notifications are useful, just not a panacea.
State changes, generically, were also a topic to probe. When doing earlier game prototypes, I started to see a vague similarity in the way I worked with state properties in various classes. My recurring pattern was to declare it as an enum and use a switch statement to execute various state-related activities. I did have trouble managing all the permutations of states and transitions. It was too much to keep in my head, so I started to make diagrams.
This was a big win. Seeing it in graphical form really helped me reason about state. Some of these, I clean up in Adobe Illustrator or in Graphity. It’s as important to practice diagramming and illustration as writing and programming. Clear images aid clear thinking.
It’s not enough to know when to go off on a tangent, it’s important to realize when to come back. I’ve learned enough with this project. If you look back at the screens, you’ll see there’s no Score or Lives display. That’s because I already know how to do those and seeing them onscreen doesn’t teach me anything about game level design, data persistence or game states. There are also no other screens except for the “Game Over” one and it doesn’t even have a back button. The app dead ends right there. That’s fine. It served it’s purpose and any work done beyond that is wasted effort.
Now to get back designing THE OCTOBER GAME.