Friday 29 October 2010

2D Platform Physics

So much for the new year's resolution.  I haven't coded anything worth playing for a long time, so I thought it was about time that I actually finished another game.

I've recently been inspired by Super Crate Box.  There was once a time when video games were just fun and nothing else, often a creatively awful excuse to mash buttons.  We didn't mind back then either, happily pumping pound coins into an arcade machine or hammering the start button after countless virtual deaths on our home consoles.  Occasionally a software house (often an independent one) will throw together something that reminds us of those times.  Popcap's intensely addictive offerings are nearly always a good example.

Super Crate Box is another.  It's simple, addictive and completely devoid of realism and storyline - in a good way.

2D platformers like Donkey Kong and Ice Climbers were relatively simple to make back in the days when 8-bit seemed like all you'd ever need to create an entertaining experience and therefore there were loads of them.  It's for this reason entirely that I don't mind making my next game another non-scrolling single-screen 2D platformer.  It's the gameplay mechanics that will make my game different to Super Crate Box, emulating only it's simplicity and genre.

In making a start I wanted something playable up and running quickly.  Many platform game authors at this point would decide to grab a prepackaged physics engine, plug in some values to simulate gravity and let their middleware deal with all of the collisions.  However, I've played many a platform game that uses the likes of Box2D, Havok and Farseer that quite honestly sucked.  The problem is that it takes almost as much time to tweak one of these engines to make an enjoyable platform experience as it does to write your own, taking into account the fact that unless if your game relies heavily on realistic physics (such as Trine for example), it doesn't really need all of the bells and whistles.

Remember that our friend Jumpman (who later became Mario) didn't have a 3rd party physics solution to aid him in his fight against giant gorillas.  If it can be done back in the old 8-bit days using about as much processing power as a modern-day wristwatch it can be done by your average coder on a modern day system.

I decided to use a velocity based system where x and y velocities are increased and decreased by the movement code, which is then applied to the player's position.  The level format is a very basic tile-based format without slopes or complicated shapes.  Either a square is passable or impassable.

Firstly, collision checking is still somewhat awkward.  If, for example, we have a level made of 32x32 size blocks and our character is 32x64 in size, we have to make six different checks for collisions.  I'll use Mario to demonstrate.


The most important points are 5 and 6.  These are where you should be checking for collisions with the floor, obviously.  By using two points and not just one in the middle between his feet, you can allow the player to stand on an edge.  If you're after a SNES or Mega-Drive feel you can also use these to start a "balancing" animation as if he's standing on the edge of a cliff.

1 and 2 are very important if you don't want to give your player the ability to jump up through platforms.  Most games will use these for collision checks with the ceilings or even just the roof of your map.

Finally, as the blocks are half the height of the player it's possible for him to jump in such a way that points 1 and 2 are above a block and 5 and 6 are below.  Without these, depending on how you code your physics, you can end up with the player getting stuck, sliding along a platform half stuck in it or just interacting strangely before clipping back out again.

Anyone trying for the more complicated options of using slopes, abstract polygon geometry or smaller tile sizes will find that they will probably have to check more points on their character or opt for a different collision detection approach altogether.

After getting these checks done, modifications can be made to a player's velocity to allow him to walk right up to a wall, rebound of a ceiling and walk fluidly across a floor without bouncing about.  Poor reaction to a collision can end up with either the player managing to force their way through solid objects, the player getting stuck or not being able to walk those last few pixels up to where a wall begins.  I resolved the latter by calculating the distance between the player and the wall after a collision and then making his x velocity this value, so in the next frame he makes that last tiny step.

Finally, jumping can be problematic.  Firstly you need to check that the player is on a floor tile.  If he is, you subtract an amount from his y velocity so that he launches skywards.  I used an approach I saw in Quake and kept a boolean flag that was set every time the player's foot collision check detected a wall.  That meant that my floor collisions had to be in a little bit from the edge of the sprite, so just to the left of number 6 in the image above, just to stop wall collisions from allowing a jump.  Obviously if you want N the Ninja style walljumping you're not going to need to worry too much about this.

I read in an interview with the developer of Braid a point that is worth making about jumping.  It is customary in platform game design to allow the player to jump despite having left a platform and thus being in mid-air for a short time.  The delay should only be a few milliseconds so that it's undetectable to the player, but still allowing for that little bit of lag between your eyes noticing a platform edge and your PC noticing your panicked button press to become less of a problem.  Adding this little touch instantly made the platform physics seem more robust.

1 comment:

  1. Great article - I definitely hear you. Physics engines bring a great many things, but high quality platform games are not one of them. Keeping it basic and tuning the controls to suit the game design makes all the difference.

    ReplyDelete