THE OCTOBER GAME was taking longer to finish than expected. Since I was unwilling to abandon it after some arbitrary date (that is, set a true deadline), I had to improve my development process.
Why was the game taking longer? What made my predictions so unreliable? What could I change?
I added game play achievements (Cheevos) for a richer experience but it was very costly in terms of complexity. My remedy was to stop adding new features and even defer polishing existing areas until all core functionality was done.
Previously I would start coding features or components without considering alternative implementations. Early bad designs slowed later work and made me reluctant to change course. This fed on itself until I was forced by skyrocketing bugs to back up and start over. It felt like I was being agile but I was actually guilty of shallow thinking. Now I do more up-front planning, with a pencil, paper and lots of diagrams. I force myself to write down at least three ideas before implementing any feature, along with pros and cons. Then I make myself wait about a day before choosing a method and starting to code it. Very often this cool-down period results in simpler design.
It’s so much fun to code that I would chase the siren of code reuse and make classes overly general, adding functions that might be used in the future. WRONG. WRONG. WRONG. You ain’t gonna need it. Another way I restrained my enthusiasm was paper planning, mentioned before. Writing with real pencil and paper slowed me down, plus it was easier to abandon a bad idea before I’d created unit tests, art assets and typed lots of code.
I put too much functionality in my cocos2d scenes, leading to monster Controller classes with scores of methods. I would start with concrete, hard-coded values and replace them with more configurable pieces as I went along, in a layered approach. My mistake was leaving all this code in place. The better way to work was to extract sub-methods into their own classes sooner rather than later. The resulting parts were smaller and easier to test & modify.
Since so much code was trapped in massive Controller classes, it became harder to test elements in isolation. I’d dabbled with Xcode’s built-in unit testing framework, SenTestKit, but I had trouble comprehending how to write tests without having them running live in an app. To get around this, I did manual, ad-hoc testing. It was a real pain to set up and I only did it for the more convoluted Model classes. It was better than nothing, but as time went on, the tests would get out of sync and I’d disable them for older areas. This caught up with me eventually and I had to buckle down and learn how to use SenTestKit properly. I’m back on the testing bandwagon now and my newer code is in better shape.
This was the opposite of premature optimization. I made two big errors. I repeatedly waited too long to test performance on older hardware and I allowed nested loops to happen too often. Calling a method 300 times every 1/60th second is NO way to make a performant game on an iPhone3G. This was the single greatest cause of delay on the project. I’m on my third rewrite of the core game code and it’s mainly for performance reasons. Each time I’ve had to do it, it took longer because of previous bad design decisions. This last rebuild has a much flatter design and more work is done by the underlying game engine.
It took quite a while before I comprehended the cocos2d way. This led to costly dead-ends and performance bottlenecks. In the past year I’ve learned a ton about good class design, proper testing and how to manage the non-coding aspects like work-life balance. I was simply too new at everything to have avoided most of the mistakes I’ve made but by continued practice, research and analysis I get better at making THE OCTOBER GAME.