DEV Community

Sébastien Belzile
Sébastien Belzile

Posted on • Updated on

Making Games in Rust - Part 4 - Jumps

Part 3 of this tutorial added gravity and collision detection to our game. This part will explain how to implement jumps.

Jump Basis

  1. We will start by defining a Jumper component. This will allow us to centralize all jumping logic.
struct Jumper {
    jump_impulse: f32,
}
Enter fullscreen mode Exit fullscreen mode
  1. In the spawn_player method, we will add this new component to our player.
.insert(Jumper { jump_impulse: 10. });
Enter fullscreen mode Exit fullscreen mode
  1. We will then add a player jumps system and register it with our application.
// In main, register a player movement system
.add_system(player_jumps.system())

// Define a player movement system
fn player_jumps(
    keyboard_input: Res<Input<KeyCode>>,
    mut players: Query<(&Jumper, &mut RigidBodyVelocity), With<Player>>
) {
    for (jumper, mut velocity) in players.iter_mut() {
        if keyboard_input.pressed(KeyCode::Up) {
            velocity.linvel = Vec2::new(0., jumper.jump_impulse).into();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The keyboard_input: Res<Input<KeyCode>> parameter of our method allows us to detect key pressed.

Our mut players: Query<(&Jumper, &mut RigidBodyVelocity), With<Player>> parameter returns all the entities with a Jumper, a RigidBodyVelocity and a Player component. The With tells Bevy that we don't need the Player component.

The code in the method sets the upward speed of our player to the jump impulse that is defined on the component.

If you run cargo run, and press the up key, you should see our player go up, and then come back down until it reaches the floor.

Disable Multiple Jumps

With our current code, if you keep the up key pressed, the player will keep going up and will not stop until the is released. We need a way to disable jumps when the player is in the air.

One way to do this is to add a is_jumping boolean property to our Jumper component.

struct Jumper {
    jump_impulse: f32,
    is_jumping: bool
}
// ...
.insert(Jumper { jump_impulse: 10., is_jumping: false })
Enter fullscreen mode Exit fullscreen mode

Then, we can allow jumps only when this boolean flag is false, and set this flag to true when the player is in the air.

fn player_jumps(
    keyboard_input: Res<Input<KeyCode>>,
    mut players: Query<(&mut Jumper, &mut RigidBodyVelocity), With<Player>>
) {
    for (mut jumper, mut velocity) in players.iter_mut() {
        if keyboard_input.pressed(KeyCode::Up) && !jumper.is_jumping {
            velocity.linvel = Vec2::new(0., jumper.jump_impulse).into();
            jumper.is_jumping = true
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

If you run the application, the player can no longer jump while it's in the air. The only issue we have is that it can only jump once...

We need a way reset the is_jumping flag when the player is not jumping.

Reset the Flag

To do this kind of things, Rapier has an event system that triggers whenever a contact occurs between 2 colliders. We will leverage that to reset our flag.

Add a new jump_reset system and register it with our application:

// In main
.add_system(jump_reset.system())

// ...
pub fn jump_reset(
    mut query: Query<(Entity, &mut Jumper)>,
    mut contact_events: EventReader<ContactEvent>,
) {
    for contact_event in contact_events.iter() {
        for (entity, mut jumper) in query.iter_mut() {
            set_jumping_false_if_touching_floor(entity, &mut jumper, contact_event);
        }
    }
}

fn set_jumping_false_if_touching_floor(entity: Entity, jumper: &mut Jumper, event: &ContactEvent) {
    if let ContactEvent::Started(h1, h2) = event {
        if h1.entity() == entity || h2.entity() == entity {
            jumper.is_jumping = false
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This system retrieves all the Jumper components and all the contact events that occurred. It then looks at whether one of the contact events involves a given jumper. If it's the case, it sets the is_jumping to false flag on our jumper.

If we want this to work, we need to activate contact events for our player collider:

flags: ColliderFlags {
    active_events: ActiveEvents::CONTACT_EVENTS,
    ..Default::default()
},
Enter fullscreen mode Exit fullscreen mode

Run cargo run and play with the up key. Our player can no longer double jump, and it can re-jump after touching the floor.

Part 1:

Discussion (0)