Jump physics are a very difficult thing to do properly. They can be thrown in very quickly, but they require a tremendous amount of tweaking to get to feel exactly right for a given game. In general, most people grasp quickly the method of applying gravity as a downward force. And within a game loop, the application of the timestep methodology.
yVelocity += gravity * timestep; yPosition += yVelocity * timestep;
But one special application of jump physics involves utilizing the timing of the button press to alter the effective height of the jump, and due to the intricacies of managing timestep based programming it isn’t so easily solved as the base problem. I’ve approached this problem a few different ways over time, but this recent method I have found to be rather elegant. Basically, it is accomplished by differing the gravity constant according to whether or not the jump button is pressed during the up motion of a jump.
The initial button press introduces a negative (upward) force. Gravity is applied as normal while the button is pressed. While the button isn’t pressed, gravity is applied with a multiplier, effectively limiting jump height when the button is released during a a jump. Once the character’s vertical velocity goes positive (indicating a downward fall), gravity is applied purely as normal.
This method ends up being simple to implement, but requires extra tweaking over the basic format due to the introduction of the gravity multiplier.
Here is a simple implementation in C# for XNA.
As some setup, I have a Character class to contain some physics variables and state data.
class Character
{
public enum CharacterState
{
Idle,
Running,
Jumping,
Falling,
};
public CharacterState State;
public Vector2 Position;
public Vector2 Velocity;
}
I also have an InputManager class that handles button/keyboard inputs based on an enumeration of Action items such as Jump, MoveLeft, MoveRight, Duck, etc. This is mostly my own revised implementation of the InputManager found in the XNA Creators Club RPG Kit sample
Which brings us to the main game engine.
// Gravity constant.
const float Gravity = 1980f;
// How much to increase gravity to shorten jump height while jump button is not pressed.
const float JumpMultiplier = 2f;
// Initial force to apply when initiating a jump.
const float JumpForce = 700f;
Character Character;
public void Update(GameTime gameTime)
{
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
// The character is in the process of jumping (positive vertical velocity).
if (Character.State == Character.Character.Jumping)
{
if (InputManager.IsActionPressed(InputManager.Action.Jump))
{
// Jump button is pressed, apply regular gravity.
Character.Velocity.Y += Gravity * dt;
}
else
{
// Jump button is not pressed, apply gravity with multiplier to reduce jump height.
Character.Velocity.Y += Gravity * JumpMultiplier * dt;
}
// Character was jumping, but vertical velocity means character should now be falling.
if (Character.StateCharacter.Velocity.Y > 0)
Character.State = Character.CharacterState.Falling;
}
else if (Character.State == Character.CharacterState.Falling)
{
// Don't alter gravity while falling, just apply regular gravity.
Character.Velocity.Y += Gravity * dt;
}
// Jump!
if (InputManager.IsActionTriggered(InputManager.Action.Jump))
{
Character.State = Character.CharacterState.Jumping;
Character.Velocity.Y = -JumpForce;
}
// Apply character's current velocity to its position.
Character.Position += Character.Position + Character.Velocity * dt;
// Continue on with collision detection and all that other important stuff.
}