We can already play the game and keep track of score, 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)]
struct Game {
score: u32,
score_best: u32,
}
We set the regular score in the board_shift
system
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.
if game.score_best < game.score {
game.score_best = game.score;
}
So now we're setting the high score, 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().unwrap();
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 '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. Allowing this would break Rust's mutability rules. Consider merging conflicting Queries into a QuerySet.'
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.
There is a fix though, and it's even right in the panic message: we use a QuerySet
.
We can take the same query we were going to make as a separate query and put it into a tuple with the original Query
. That tuple is then provided to QuerySet
as a type argument and all is good.
fn scoreboard(
game: Res<Game>,
mut query_scores: QuerySet<(
Query<&mut Text, With<ScoreDisplay>>,
Query<&mut Text, With<BestScoreDisplay>>,
)>,
) {
We also need to make a small change to the usage of the query. Bevy provides us functions to access the queries in a QuerySet
using a similar access pattern to regular tuples. To access the first query we can insert q0_mut()
into the code we already have written.
let mut text = query_scores.q0_mut().single_mut().unwrap();
text.sections[0].value = game.score.to_string();
Then we can copy/paste that code and ask for q1_mut()
instead of q0_mut()
. We'll also grab game.score_best
instead of game.score
.
let mut text = query_scores.q1_mut().single_mut().unwrap();
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.