Ludum Dare 23 postmortem 2

(Read Part 1)

Once I decided that my idea to create a game based on bacteria was both interesting and within my capability, I needed to figure out if it would be technically feasible. I knew that I would be working in Game Maker, because I know it the best and can get results quickly with it.

Choose Your Weapons

I toyed with the idea of using the new Game Maker Studio beta, to enable me to target Mac OS X and HTML5 as well as Windows, but after encountering some problems with project corruption back in January while using the then-in-beta Game Maker HTML5 during Global Game Jam 2012, I was leery, and very early on I tried to build something in Studio that I knew was dead-simple and would work for sure if I’d done it in 8.1, and the compiler didn’t like it. The new Studio compiler error message format didn’t look familiar, and I didn’t feel like digging into it — I wanted to rapid prototype, not debug. As soon as that happened, I gave Studio the heave-ho, and dropped back to 8.1. Now that the compo project is submitted, I plan to see if I can bring it into Studio and build it for other platforms.

The Player

The first thing I did was create a bacteria object, set up a timer for it, and make it spawn another instance of itself every N steps. Exponential growth ensued, and I rapidly determined how many nearly-empty objects Game Maker can handle on my 2GHz Core 2 Duo without dropping frames.

To help me out, I created two other objects: a Dashboard, which I used to draw some critical data on the screen, such as fps, the room speed, the number of objects, etc. and a Debug object, which I used to vary parameters and reset the room quickly if I needed to start the game over. Building these tools makes it much easier to analyze the program’s performance, and the metering makes it immediately obvious when you just introduced a problem — or fixed one.

Much above 4-5000 bacteria was probably not going to be acceptable. 4000 is a decent, respectable number, but not really very high when you’re talking about two populations of bacteria, each growing as fast as possible. I knew I would need to come up with some performance optimizations and keep my objects as simple as possible in order to squeeze as many of them out of my hardware as possible.

Next, my bacteria needed a sprite. I’m a half-way decent — but way out of practice — artist when it comes to drawing with pencils on paper. When it comes to creating sprite assets for games, I have a long way to go to approach a level of quality I’d consider acceptable and satisfying. Most of my focus when making games is on the programming end of it — figuring out technically how to do things — and on design, figuring out how to make a game that is fun, challenging, balanced, and deep. This doesn’t leave very much time for drawing and animation, and because I don’t do a lot of it, when I do it, the results usually come very slowly, and I often find the work to be tedious. Game Maker’s sprite editor is OK, but leaves a lot to be desired in terms of ease of workflow and the design of the tool itself.

In the last week leading up to LD48, I learned about some really interesting pixel-art tools, Pickle and Pyxel (Update: and, WHOA, Spriter!), which are actually geared toward making pixel art and sprites. But I hadn’t had time to learn them, and while I could appreciate immediately that they had features that were geared toward the sort of workflow that is employed by pixel artists, and would accommodate my needs better once I understood the tool, I felt like maybe making use of them would involve too much learning curve.

So, in order to keep focused on development, I elected to make my bacteria sprite a simple square. Most often when I am building a project in Game Maker, my very first sprite is a 32×32 blue square. It takes no time to make, just create a sprite at default size, paintbucket the thing blue, and center the origin. Done.

I had the idea to polish this later by creating some kind of amorphous, amoeba-like blob, but I could never manage to animate something that looked right when you had a few hundred of them on the screen. I felt each one needed to look uniquely amorphous, or it just wouldn’t be convincing. In the end I dropped the idea, but it’s something I would like to develop an approach for doing well.

Not that I’m a biologist, but I wanted my squares to look alive. Remembering looking at things under a microscope, I always thought it was way more interesting to look at things that moved than it was things that just sat there. Remembering a technique that WhileTrueFork used for his GGJ12 entry, Ossiphidia, I wrote a script that inexpensively jiggled the position of each bacteria. I liked the way this looked well enough. I also wanted them to be translucent, since most things that are microscopic are, so I did that.

I also wanted the bacteria to be different colors, to make their translucent overlapping more interesting and pretty, so I changed my blue square sprite to a white square, and used the draw_sprite_ext() function to shade each one with a random color that I generated in the creation event. At first I just made it a completely random RGB color, but quickly determined that this would make it too difficult to tell which were yours, so I tightened up the randomness a bit, and got them all a variety of (mostly) blue, with the occasional reddish-purple or green or grey thrown in.

I had it in mind to make the powers of the bacteria more apparent by tying the randomly generated color to them in some way. My first idea was to make the brighter colors indicate higher levels of power, and the three attributes (attack, defense, speed) correspond to the Red, Green, and Blue component of each bacterium’s color. This ended up not going anywhere, for a number of reasons. First, the scales for these abilities are not equal — attack and defense are on a scale of 1-10 (er, units of power), while speed is on a scale of 0-2 (px/step). Second, these ranges of values are sufficiently small as to provide an indistinguishable degree of variety in the final output color if all you do is add the attribute to the color channel — to compensate for that, I added a significant amount of randomization to each color component, which obscured the attribute input and in the end made it insignificant. Third, and most important, it hardly matters what the attributes of an individual bacteria are if you only can control them en masse. And finally, merging all three properties into one meter doesn’t provide a very instinctual readout of the relative strengths and weaknesses of a bacterium. A more reddish one would be stronger on attack, but it would really only be red if it was very weak on speed — the ideal bacteria would be close to white, but again, the randomized component of the color really made it impossible to tell the relative power of a cell. In retrospect, normalizing the bacterial attributes to a 100pt scale would have achieved a better result, but I didn’t bother with it once I realized that it wouldn’t matter if you couldn’t actually control individual bacteria.

Next I needed a way to move your bacteria around, and the easiest thing to do was to have them follow the mouse. I got this working quickly enough, but refining it proved to be one of the things that took up almost all my time this weekend. Simply following the mouse would result in the bacteria neatly stacking up into a pile right under the cursor. Also, I didn’t want every last one to follow your mouse around, I thought it would be good for strategy to be able to leave some behind, and to be able to direct separate groups around.

My first refinement was to create a “too far” distance (random for each bacteria), and if the distance from bacteria to mouse was too far, it would move closer. I also created a “too close” distance (based on “too far”) and programmed the bacteria to move away from the mouse if it was too close. This had the cool effect of making it seem like you were pressing your thumb down if you clicked in the midst of a pile, spreading them out away from the point where you clicked. Next, I created a distance between too close and too far which the bacteria would stop moving. Again, this was random for each bacteria, so the end result was that you’d end up with an irregular mass of them bunching around the mouse, instead of all piling up directly underneath the mouse. This worked brilliantly and I was very pleased with it, it looked convincing and hardly took any time to do. The coolest thing about it was that it simulated clumping without actually having to use collision events at all, and thus it was a very inexpensive way of getting the basic behavior working.

The hardest part about working out the controls, apart from deciding what range of random values the various critical distances needed to be, was trying to figure out how best to make the mouse useful. My first attempt was very good, just to have them follow the mouse. It was a “do the simplest thing that could possibly work” thing, and it worked quite well. Refining that was not so easy — it’s hard to improve on simplicity.

But I wanted them to not just follow the mouse, but to follow clicks. Converting the code I’d written to follow the position where the mouse was clicked, rather than follow the mouse at all times, was relatively simple. But I ended up spending a lot of time trying to get the bacteria to respond to the click in a way that felt right. It seemed following the mouse cursor was a very natural means of movement, while following clicks was clunky and left me frustrated, clicking like mad. What I really wanted was to be able to divide up the colony and send some in one direction, the rest in another.

My final touch on the player controls did not come until nearly the end, when I set up a maximum range, so that bacteria would ignore the mouse if they were too far away from it, allowing you to divide up groups and move clusters of local cells independently. Fine-tuning this required many hours and countless failed attempts, and, due to loss of mental focus from sleep deprivation and exhaustion, excessive amounts of debugging. In retrospect the solution I hit upon was fairly simple and straightforward, yet coming to that design took a great deal of experimentation. But before I did that, I got to work on creating an enemy bacteria, and working out the battle mechanics and AI.

Pre-mouse clicks was where I left things off on Friday, going to sleep about 3:30AM. I woke up the next day around noon, and programmed a bit until 2pm, not in a productive, focused manner, mainly just fiddling about, tweaking things. I had to do some things Saturday, so from 2-8pm I was otherwise occupied, and then it took me another hour or two to really settle down and get back into The Zone. Before I knew it, it was after midnight, and most of Saturday had slipped away from me. I jacked myself up on caffeine and cranked until about 4AM. Really, I jumped around quite a bit, adding this feature or that feature, tweaking other stuff, debugging, thinking about unresolved design questions, trying to settle on an approach that I liked.

(Part 3)

Leave a Reply