We can play the game and keep track of score while we do, as well as reset the score when starting a new game. We'll build on top of what we already have to record the highest score achieved so far.
Adding a score_best
next to the score
field in our Game
struct will allow us to track the best score right next to the current score.
#[derive(Default, Resource)]
struct Game {
score: u32,
score_best: u32,
}
We set the regular score in the board_shift
system already.
game.score += tile.2.value;
so that's where we'll set the high score as well, right at the bottom of the system as the last thing we do. If the current game score is better than the high score, set the high score to the current score.
....
tile_writer.send(NewTileEvent);
}
if game.score_best < game.score {
game.score_best = game.score;
}
So now we're setting the high score in our Game
struct, but we're not displaying it. Back in src/ui.rs
we can update the scoreboard
system to display the high score in the Text
with the BestScoreDisplay
component that we set up when we built the ui out.
Currently in scoreboard
we have a single query for the Text
with the ScoreDisplay
component.
fn scoreboard(
game: Res<Game>,
mut query_scores: Query<&mut Text, With<ScoreDisplay>>,
) {
let mut text = query_scores.single_mut();
text.sections[0].value = game.score.to_string();
}
This is basically the exact same query we need to make for the ScoreBestDisplay
. If we copy/paste the query and slightly modify it for the best score.
fn scoreboard(
game: Res<Game>,
mut query_scores: Query<&mut Text, With<ScoreDisplay>>,
mut query_best_scores: Query<&mut Text, With<BestScoreDisplay>>,
) {
then run our game, it crashes.
thread 'main' panicked at 'error[B0001]: Query<&mut bevy_text::text::Text, bevy_ecs::query::filter::With<boxes::ui::BestScoreDisplay>> in system boxes::ui::scoreboard accesses component(s) bevy_text::text::Text in a way that conflicts with a previous system parameter. Consider using `Without<T>` to create disjoint Queries or merging conflicting Queries into a `ParamSet`.', /Users/chris/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_ecs-0.10.0/src/system/system_param.rs:259:5
This is because we're asking Bevy for mutable references to Text
components in both queries. This is a restriction that Bevy warns us about because the way the query system is provided to us doesn't allow us to have two different queries for the same mutable component types.
Remember that a mut
reference can also be phrased as “an exclusive reference”.
There are two possible fixes, and both are even right in the panic message: we could use a ParamSet
or we can use another filter: Without
.
We’d use ParamSet
for more complex situations and in this case, using Without
works for us.
We’re telling Rust and Bevy through the type system that we have two queries that won’t overlap in the mutable components they access.
The first Query with have two filters: With<ScoreDisplay>
and Without<BestScoreDisplay>
, while the second query will have the opposite.
fn scoreboard(
game: Res<Game>,
mut query_scores: Query<
&mut Text,
(
With<ScoreDisplay>,
Without<BestScoreDisplay>,
),
>,
mut query_best_scores: Query<
&mut Text,
(
With<BestScoreDisplay>,
Without<ScoreDisplay>,
),
>,
) {
...
}
The code for updating each of the text sections is basically the same. The only differences are which query we’re using and which value we’re displaying.
let mut text = query_scores.single_mut();
text.sections[0].value = game.score.to_string();
let mut text = query_best_scores.single_mut();
text.sections[0].value = game.score_best.to_string();
Running the game now gives us a game that keeps the highest score around. When we reset the game only the current game score gets reset and if we beat our high score it continuously updates as we play.