Showing posts with label physics. Show all posts
Showing posts with label physics. Show all posts

Wednesday, March 3, 2010

Doodle Blast! - Day 4 - Tweaks and More Flicks

Stats: 52 code files, 4588 lines of code, 2 textures, 20 sounds

Today was mostly about code clean up, bug hunting, and game play tweaks. The gist of the game was more-or-less there, but it wasn’t very playable yet and I wanted to spend some time tweaking details to make the game cuter. I was also starting to get a little tired and I needed to ease off just a touch.

One of the things I played with was flicking from the day before. You could flick the little soldiers into the air, but they looked very boring when they were falling down because I just applied a random rotation to them when they were released. So, I nixed that behavior and instead let them turn to follow the direction of their path, so that when they were falling down, they would do so head-first. And who doesn’t get a kick from seeing little stick figures falling head-first towards the ground???


I used the following method to turn a given sprite in the direction of the moving, underlying cpBody object:
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)));
Basically, the rotation of the sprite was attempting to conform to the velocity vector, but it did so over time with some easing-in baked in. The constants, in this case, were set to: DIRECTIONAL_MAX_THETA_DELTA = 5 (degrees) and DIRECTIONAL_MOVE_FACTOR = 0.3 (ie. 30%).

You will also notice that there are two types of runners here – the red ones, aka the suicide bombers, which don’t shoot but explode on contact and take away hit points, and the black ones that do shoot but don’t take any hit points when they crash into the tank (incidentally, I stole these pictures from Pop Fizz; that was before the final images were ready). The red peeps also had a count-down over them. Basically, they exploded when the count reached zero. In the final game, I cut that feature because it just polluted the UI and it didn’t really add that much more excitement to the game.

Monday, March 1, 2010

Doodle Blast! - Day 2 - Collisions

Stats: 50 code files, 3871 lines of code, 2 textures, 11 sounds

Today, I introduced a simple set of “enemies”. I added a helicopter I copied from the original game sketch and I also added a building that I shamelessly stole from Pop Fizz, the Airplanes world. I introduced the concept of enemy hit points, simple collision detection, and continuous fire. The final game supports continuous fire for only a certain types of guns to make the game play more varied and to prevent players from just holding their fingers at the corners of the screen as they plow forward. However, at this stage, I made every gun (namely the two that I implemented) support continuous fire. This was partly because it was fun to feel the destructive power of the tank and partly to run a preliminary test to see how many objects I could throw on the screen before the frame rate began to suffer. So far, the code faired remarkably well. Even in the craziest of situations, Cocos2D was pulling at a solid 45 fps.

The frequency at which the enemies were spawning was controlled by a constant and it was the first time I started playing with how many enemies are fun to destroy vs. how many enemies are just too overwhelming. I showed the preliminary results to my brother, pointed him to the constant and let him play. His findings were that there was no such thing as too many enemies and he was rooting for the biggest mayhem on the screen he could get. I liked the idea of having a more leisurely play. To accommodate us both as players, I’ve started scheming for an enemy-spawning logic that adapts to the player’s style, lovingly referring to it as Skynet. But I’m getting ahead of myself because Skynet didn’t get to see the light of day until a couple of days later.

The most interesting part in today’s addition was the collision detection elimination. As I mentioned before, Chipmunk has a handy way of filtering out unnecessary collisions, thus decreasing the amount of computation and boosting perf. There are three tiers at which you can filter out collisions
  • Groups
  • Layers
  • Collision handlers
You can read all about these here. But the basic idea behind them is simple. Bodies within the same group do not collide with each other. Bodies that do not share at least one layer (layer being a bit in a bit mask) do not collide with each other. And finally, bodies for which there doesn’t exist a collision handler (defined by a function pointer for a bodyType-bodyType pair) do not collide with each other. Chipmunk creates a default collision handler that is a catch-all handler for all bodies that satisfy the group and layer requirements. This default handler handles all collisions that trickle down to it. In Doodle Blast, I’ve overridden this behavior such that no bodies collided by default unless there was an explicit collision handler defined. Then, I’ve split all objects within the game into groups and layers as follows:
Item Group Layer
Player guns 1 PLAYER
Friendly fire 2 ENEMY
Enemy fire 3 PLAYER
Enemies 4 ENEMY | PLAYER
Bonuses 5 BONUS
I’ve added a collision handler (using the same generic function pointer returning “yes, please, handle this collision”) for the following types of bodies:
Player <-> Enemy
Player <-> Enemy Fire
Enemy <-> Friendly Fire
Et voila! Now I knew about all the collisions I cared about without the overhead of calculating too much of anything else.

There was one more trick. I wanted the guns to stack neatly on top of each other. So, I gave each gun a stacking shape (essentially a rectangle), didn’t assign any group to it (meaning, they all collided with each other), placed them all into their own collision layer (meaning, they all collided with each other but nothing else), and added a generic StackingShape <-> StackingShape collision handler to deal with the guns bumping into each other.

That’s it. These assignments essentially gave me all of the game interactivity for free. Chipmunk was my friend.

I added a couple of more tweaks here and there (scores, bonuses, ...) and, at the end of the day, this was the result so far:



Sunday, February 28, 2010

Doodle Blast! - Day 1 - The Prototype

Stats: 38 code files, 2562 lines of code, 2 textures, 6 sounds

As I said earlier, day 1 consisted of me cheating because instead of spending the promised one day, I actually spent a weekend getting a running prototype together. I ended up borrowing code heavily from Pop Fizz. I didn’t have to re-implement asynchronous data load, animated particle systems, and persistent state storage. What I did end up writing was the basic structure of the game, a class to abstract out the tank guns, and a class to abstract out projectiles.

The big question I wanted to answer was whether to use a full-blown physics simulation or whether it would be sufficient to just fake it. All I really needed was a position, velocity, and acceleration vectors for every item on the screen and a full physics engine seemed like an overkill, not to mention a computational overhead. I also needed to solve collision detection. Simplified collision detention can be easy to implement, but I needed it to be fast. So, I sat down and started to write a spatial hash mechanism that would help me speed up the process. Half way through that effort I realized that I’m not being very smart because a physics engine would give me all of these things for free. I hence gave up and embraced Chipmunk all the way.

In the end, that ended up being a great decision. The final game uses Chipmunk not only to move things around the screen and detect collisions, but also to simulate bumps on the ground, stack guns on top of each other, and model the final explosion of the tank when the player dies. As far as overhead goes, it turns out that Chipmunk is pretty efficient at what it does. More importantly, version 5.0 came up with some really useful enhancements that allow you disregard unnecessary computations early on. In addition to breaking objects up into layers and groups, you can specify custom collision handlers that can have up to 4 callbacks through out each collision event:

  • Begin: Two shapes just started touching for the first time this step. Chipmunk has not yet calculated collision forces for this collision, and the preSolve() and postSolve() callbacks have not yet been called.
  • Pre Solve: Two shapes are touching. Some collision force computation has already taken place.
  • Post Solve: Two shapes are touching and their collision response has been processed.
  • Separate: Two shapes have just stopped touching for the first time this frame.
(adapted from Chipmunk docs)

For the most part, all I care about is the Begin event. It signifies that a bullet has hit an enemy or a player. I don’t actually care about the collision forces, so the calculation is pretty cheap and can stop right there.

The physics decision out of the way, I wrapped some sprites around Chipmunk bodies and this was the final result:


You will notice that I added a simple tutorial place-holder right from the start. My tendency is always to leave the tutorial until the very end at which point it’s a pain in the butt to stitch it into the game. So, with this project, I wanted to make sure that tutorial was built into the game from the get-go. The final tutorial ended up being pretty much identical to this one and having it there from the start saved me some head scratching later on.

You will also notice that the guns themselves are animated. That was a big part of the visual appeal for me, so I wanted to test that behavior right away. The initial gun logic was pretty straightforward – aim, shoot, and cool. As I will talk about in a later post, this implementation was too simplistic and I had to create a whole state machine to model each gun. But more on that later.

For now, this was as far as the two days have gotten me, but the results looked promising enough to keep going.