Tuesday, November 15, 2011

Wall Kicking

Here is how wall-kicking works:


Desired Outcome
When the player touches a wall, the player should continuously stick to the wall until:
  1. The player touches the ground
  2. The player jumps off the wall
  3. The player presses the DOWN key

The "First Frame" Problem
Most Platformer games collide the player against the environment every frame. When the player overlaps a piece of the ground, wall, or ceiling, the player is moved to the closest point that is not touching the environment. This means that we can only tell if the player touched the environment during the first frame that they touch it. Detecting continuous collision over multiple frames is a little trickier. 

It's pretty easy to tell if the player is constantly touching the ground, since we can count on gravity to keep the player falling through the floor every frame. On walls, we only know that the player has collided on the first touch (unless of course, something is constantly pushing the player into the wall. This is a bad idea unless your character is intentionally sticky, like Meat Boy).


Two Collision Boxes
Bullet Time Ninja detects continuous collision with two collision boxes. One box for the player, and a wallCollisionBox that is slightly to the left or the right of the player (for a left or right wall). The player's box detects the first frame touch. As long as the wallCollisionBox is touching a wall, the player's "usingWall" boolean variable is true.

Here are the relevant bits of code:
  // in the Gameplay update() loop
 FlxG.collide(gameLevel.CollisionTileMap, player);
 FlxG.collide(gameLevel.CollisionTileMap, player.wallCollisionBox);

 // Variables in the Player class
 public var wallCollisionBox:FlxSprite;
 public var usingWall:Boolean;

 public function touchingWall():Boolean { return isTouching(FlxObject.WALL) || wallCollisionBox.isTouching(FlxObject.WALL); }


// the Player update() loop

// did the player land on a wall?
if(!input.isDown(PlayerInput.DOWN) && !isTouching(FlxObject.FLOOR))
{
 if(isTouching(FlxObject.LEFT))
 {
  usingWall = true;
  facing = FlxObject.RIGHT;
 }    
 if(isTouching(FlxObject.RIGHT))
 {
  usingWall = true;
  facing = FlxObject.LEFT;
 }
}

// does the player want to get off a wall?
if( touchingWall() && input.isDown(PlayerInput.DOWN) )
 usingWall = false;
   
// can't use a wall if I'm not touching it
if(!touchingWall())
 usingWall = false;

// I'm on the floor (rofl)
if(isTouching(FlxObject.FLOOR))
 usingWall = false;


// later...

// update the wallCollisionBox's position
wallCollisionBox.x = x + OFF_WALL_TOLERANCE * (facing == FlxObject.LEFT ? 1 : -1);
wallCollisionBox.y = y;

At this point, usingWall is a reliable variable to use in your character animation logic. Notice that it is possible to be touchingWall(), and not usingWall. This is to accomodate pressing the DOWN key while on a wall. Moments like these remind me how cool (and weird) game logic is.

Jumping off walls is very easy, by the way:
  // the Player update() loop
 if(usingWall && input.justPressed(PlayerInput.JUMP))
 {
  velocity.x = maxVelocity.x * (facing == FlxObject.LEFT ? -1 : 1); 
  velocity.y = -JUMP_STRENGTH;
  usingWall = false;
 }

 
Final Comments
Now that you have a broad idea about how wall-kicking works, be prepared to encounter very subtle problems when programming wall-kicking into your own games. I personally hit a few issues related to the fact that touching walls changes the way the ninja faces. I also spent a day solving a strange blinking bug that was caused by the order in which I arranged my if() statements.

Thoughtfully yours,
Greg

3 comments:

  1. Great work Mate!

    One question. How did you make him stick to the walls? your loop doesn't have any code for that.

    Cheers!

    ReplyDelete
  2. It's pretty simple. Somewhere in your update() loop

    if(usingWall)
    {
    // stick the character to the wall
    }
    else
    {
    // not on the wall
    }

    In Bullet Time Ninja, being stuck to the wall means that the player falls half as quickly, and the ninja's pose changes.

    ReplyDelete
  3. Hello Greg...

    Thanks, for your Blog and your generosity with the code.

    I already implements the wall-kicking in my game :O)

    ReplyDelete