SPOILER ALERT: If you are a player of Doodle Blast!, this post will spoil some of the fun and mystique behind the game. Consider yourself warned.
One of the unexpected challenges I ran into with Doodle Blast! was difficulty. Some people picked up the game only to feel overwhelmed and lost within a few seconds. Others got bored in just about the same amount of time. Seeing these reactions, I began to wonder how I could dynamically adjust the game to adapt to the player’s abilities. If you are like me and enjoy having nice, slow starts, I wanted the game to take its time building up. If, on the other hand, you are like my brother, I wanted the game to hit you with everything it’s got as soon as possible. End result – we would both be happy because the game provided a different experience for the each of us. But how the heck do you solve this problem?
As it turns out, the solution I coded came in three parts:
- Variable aggressiveness – I could tell each enemy how aggressive I wanted it to be
- Feedback mechanism – Each enemy could report back on how effective it was at destroying the player
- “Skynet” – Central logic interpreted the feedback to adjust the aggressiveness of the next wave of enemies
Because I’m a geek and because it seemed right, the first two values were passed around as floats, ranging from 0 to 1. Zero in aggressiveness meant that the enemy was as passive as possible (it wouldn’t shoot, for example). Zero in feedback meant that the enemy was completely useless (it was killed before it even entered the screen). Ones indicated the opposite for both.
Since each type of enemy was its own class, each could also provide its own way of determining its effectiveness at being a killing machine. What I, then, did as a player was try playing against each of the enemies at various “aggressive” values to find a happy medium between being bored and not being able to keep up. This value (which for most enemies hovered around 0.65) was the golden standard that each enemy was trying to achieve. If it fell short, Skynet would yank up the aggressiveness of the next wave proportionately. If it overshot, Skynet would make the next wave less aggressive.
That was the basic idea. However, there are a couple of other tidbits worth mentioning.
Assaults – each wave of enemies of a similar type constituted an “assault”. If you were kicking butt across all assaults, Skynet would begin sending two or more assaults at a time to give you some extra excitement. If that proved to be too difficult, the concurrent assaults scaled back down.
Fire power – the relative aggressiveness of all enemies also adjusted based on the number of guns you carried. Basically, the more guns you had, the more indestructible you became and the more aggressive the enemies turned.
Shooting strategy – it doesn’t matter if you lose guns from your tank. The only thing that matters are the hit points of the tank itself. Hence, a particularly useful way to defeat the system was to continuously shoot only at the bottom right corner, and just pick up new guns when the old ones got destroyed. So much for Skynet. To counteract this strategy, I added "assassins". The game actually takes note of your favorite shooting spots and monitors the “histogram” it produces. Initially, it lets you get by. However, after the first 1000m, it sends a way of deadly assassins after you that target your tank directly without mercy. These assassins get spawned at the histogram minimum - meaning, at the place where you are least likely to shoot.
The end result of all this magic is that if I play poorly, I can get to about 600m. If I play really well, I can get to about 700m. And that was basically the aim – to give players an opportunity to enjoy bulk of the game play regardless of whether they were pros or rookies. But, as always, there is lots of room for error. Take “Nino", for example, topping the leader boards with a whooping 8561m under the belt. Kids these days…