The Snake game is fairly playable now. It’s recognizable as Snake in any case.
We still don’t have any way to end the game or restart the game once it ends.
To do that we’re going to introduce game states.
In lib.rs we can create a new enum to represent the states we could be in. We’ll have one for the main menu and one for playing the game.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GameState {
Menu,
Playing,
}
Then in main.rs we’ll insert the Playing state.
.add_loopless_state(GameState::Playing)
To show off how game states can help us, we’ll update the tick system to only run in the Playing state.
The run_in_state function comes from iyes_loopless and is called on the system function itself.
.add_stage_before(
CoreStage::Update,
"snake_tick",
FixedTimestepStage::new(Duration::from_millis(
100,
))
.with_stage(
SystemStage::parallel().with_system(
tick.run_in_state(GameState::Playing),
),
),
)
Now, if we run the game the snake should still respond to our movement and eat apples, etc.
If we change the add_loopless_state function to insert GameState::Menu instead, our snake won’t move because the tick system won’t be running!
This is how we’ll switch back and forth between the game running and the menu system after a game over.
We can also apply this to any of the other systems we have running. In our FoodPlugin, bring use iyes_loopless::prelude::*; and crate::GameState into scope, then apply run_in_state to food_event_listener.
impl Plugin for FoodPlugin {
fn build(&self, app: &mut App) {
app.add_event::<NewFoodEvent>().add_system(
food_event_listener
.run_in_state(GameState::Playing),
);
}
}
and ControlsPlugin
impl Plugin for ControlsPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<Direction>().add_system(
user_input.run_in_state(GameState::Playing),
);
}
}