

1 coder, 1 sketch, 7 days - follow the story behind the game




Unless you have a dedicated team of testers working with you, it’s pretty easy to publish an app with bugs in it. Hopefully, the majority of your scenarios work because you’ve tried them out (that is assuming, of course, that you are not publishing titles for a device you’ve never seen - hello, iPad!). However, there is always some weird combination of user input you haven’t thought of that, apparently, everyone else has.
We’ve all been there - you write an app and a friend of yours wants to take a look. You reluctantly give up possession of your precious little baby and cringe in the background as your friend taps on all the wrong places. Three seconds later, he returns the device to you with your app all locked up or, better yet, not running at all. You and your friend never talk again.
Okay, so maybe that’s just me. However, my point is that even in these scenarios, you are pretty safe. Apart from losing a friendship or two, you can go home, try to recreate some of the issues, and fix the bugs you find along the way.
When you ship, however, chasing crashes becomes much more difficult. Apart from getting the occasional “The app crashes and it sucks! Don’t buy it” review, you don’t get much information about how your app performs. You may not even know that it crashes at all; not unless you look at Apple’s iTunes Connect portal in a little more detail.
I’ve just submitted an update for Doodle Blast! today and discovered this handy little button hiding underneath the application details:
Clicking on it, you get a nice little summary of what, if any, crashed have been reported:
Besides being told about the crashes, you can download a .crash file, which magically opens up on your machine and shows you the full stack trace of your app at the time of the crash.
That’s pretty cool! I’ve worked on a variety of platforms where you might get some core dumps but, no matter how hard you try, you can never get your hands on the actual stack trace. Either your symbols are too old, or they are from the wrong build, or you just didn’t clap your hands three times before throwing some salt over your shoulder. Whatever the case, the bottom line is that getting some useful information out of a crash log is always a challenge.
This is not the case here. You just need to know that these crash logs are available. I didn’t… until now. And, sadly, it turns out that Doodle Blast! crashes every now and then. Curious, I looked at the downloaded .crash log and discovered that if you tap on the Doodle Blast! title text in the main menu, bad things happen. Don’t do it, kids. You’ve been warned. Fix coming up in the next update…
- but Doodle Blast! has passed the "3, 000 unique scores" mark as of 5 minutes ago. Only 997, 000 more to go!

"Remember drawing spaceships as a kid and then filling every available bit of their surface areas with guns? This is the tank equivalent! Photos and videos do not do this game justice. You have to play it and control the awesome firepower to really appreciate it."
"I'll take a gamble and say that this game's going to be as big as Doodle Jump and Angry Birds!"
"Cool style, not as good as doodle jump but well worth getting for a mess about"
"Omg this is the next Doodle Jump."
"...Not going to be the next Doodle Jump..."
"This is better and more addicting than Doodle Jump and Angry Birds"
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:
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…
_tableController = [[ScoreTableController alloc] initWithStyle:UITableViewStylePlain];UITableView* table = _tableController.tableView;[[[CCDirector sharedDirector] openGLView] addSubview:table];
Day 1 – prototype: 74 viewsDay 2 – collisions: 105 viewsDay 4 – flicks: 11 views
cpVect direction = cpvnormalize(body->v);float theta = -CC_RADIANS_TO_DEGREES(acosf(direction.x) * (direction.y > 0 ? 1.0f : -1.0f));float currentAngle = sprite.rotation;float dTheta = theta - currentAngle;while (dTheta > 180) dTheta -= 360;
while (dTheta < -180) dTheta += 360;
sprite.rotation += fminf(DIRECTIONAL_MAX_THETA_DELTA, fmaxf(-DIRECTIONAL_MAX_THETA_DELTA, (dTheta * DIRECTIONAL_MOVE_FACTOR)));
Today I introduced visual damage model on the player. Interestingly enough, most people playing Doodle Blast! for the first time completely miss it. I wanted to avoid having a health-meter bar or a number somewhere on the top of the screen, so I tried to solve this problem visually instead:
But as I said, most people miss it, so I might need to add a health bar after all.
The other big change in today’s build was the addition of flicking. Following the initial sketch of the game, I wanted to have little soldiers running around. I also wanted them to be flickable as flicking seems to be a popular gesture these days. The implementation was pretty simple.
Each soldier had a state variable, which could contain one of the following values:
To calculate how and whether a soldier was flicked after it was grabbed, I kept track of the movement of each touch. When I received a TouchMoved event, I used the last touch position along with the last touch time-stamp to compute the speed thusly:
touchSpeed = (currentLocation – lastLocation) / touchTimeDelta;
When the touch ended, I used the touchSpeed, clamped by some max value, and simply set the released soldier off in that direction.
This approach, however, created a problem. You suddenly couldn’t just shoot a soldier, because every touch near a soldier’s vicinity turned into a flick gesture. To solve this problem, I defined two constants: FLICK_TIME_EPSYLON and FLICK_DISTANCE_EPSYLON. If a given touch event took less time than FLICK_TIME_EPSYLON (0.3 secs) and if that touch event moved a total of less than FLICK_DISTANCE_EPSYLON (20 pixels), I wouldn’t consider that gesture a flick, but I would consider it a tap instead and still direct one of the guns to shoot at that location.
Finally, there was one more interesting change that I introduced the day before but didn’t get a chance to talk about yet. I wanted the tank and the stack of guns to feel like they are barely holding together as they clank along moving forward on a bumpy road. What I ended up doing was giving a random small impulse to the tank and to a random gun in the stack every 0 – 2 secs. You can see those bumps in the video from yesterday.
Item Group LayerPlayer guns 1 PLAYERFriendly fire 2 ENEMYEnemy fire 3 PLAYEREnemies 4 ENEMY | PLAYERBonuses 5 BONUS
Player <-> EnemyPlayer <-> Enemy FireEnemy <-> Friendly Fire