Part 3 of this tutorial added gravity and collision detection to our game. This part will explain how to implement jumps.
Jump Basis
- We will start by defining a Jumper component. This will allow us to centralize all jumping logic.
struct Jumper {
jump_impulse: f32,
}
- In the
spawn_player
method, we will add this new component to our player.
.insert(Jumper { jump_impulse: 10. });
- 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();
}
}
}
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 })
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
}
}
}
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
}
}
}
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()
},
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:
Top comments (1)
I am trying to reset jumping but I am using heron, for some reason my funcion jump_reset dont get any Event collision