Points
What is a game without points and a high score? Now that the circle can shoot down the squares it is time to add some points. Every square that is shot down will add to the score, where bigger squares will be worth more points. The current score will be shown on the screen, as well as the highest score achieved.
Bigger squares could be worth more because they contain more resources. It is also possible to make bigger circles harder to kill by requiring multiple hits to destroy.
If the current score is the highest score when the game is over, it will be written to a file on disk so that it can be read each time the game is started. This will only work if the game is played on desktop as the WebAssembly version doesn’t have access to the file system. It would be possible to store the high score in the browser storage, but that won’t be covered here to keep the implementation simple.
Implementation
Import module
To be able to read and write files we need to import std::fs modul from the Rust standard library. Add this line directly below the line to import Macroquad at the top of the file.
use std::fs;
New variables
We will need two new variables, score
and high_score
, to keep track of the
player’s points as well as the highest score ever achieved. We’ll use the
function fs::read_to_string()
to read the file highscore.dat
from disk.
The points stored in the file need to be converted to u32
with
i.parse::<u32>()
. If anything goes wrong, if the file doesn’t exist or it
contains something other than a number, the number 0
will be returned
instead.
let mut score: u32 = 0;
let mut high_score: u32 = fs::read_to_string("highscore.dat")
.map_or(Ok(0), |i| i.parse::<u32>())
.unwrap_or(0);
We’re writing the points directly to the computers hard drive, which will not work if the game has been compiled to WebAssembly and is run on a web page. This will be treated as if the file doesn’t exist.
It could be possible to use the browser’s storage, or sending the score to a web server, but that is not covered by this guide.
Updating the high score
If the circle collides with a square we’ll check if the current score is
higher than the high score. If it is higher, we’ll update the high score and
store the new high score to the file highscore.dat
.
if squares.iter().any(|square| circle.collides_with(square)) {
if score == high_score {
fs::write("highscore.dat", high_score.to_string()).ok();
}
gameover = true;
}
Macroquad supports reading files when the game is run on a web page. We could
use the function
load_string()
to load the high score instead. But since it isn’t possible to save the file,
this isn’t particularly useful in this case.
Increasing the score
When a bullet hits a square, we’ll increase the current score based on the
size of the square. After that we’ll update the high_score
if the current
score
is higher.
if bullet.collides_with(square) {
bullet.collided = true;
square.collided = true;
score += square.size.round() as u32;
high_score = high_score.max(score);
}
Resetting the score
When a new game is started, we need to set the score
variable to 0
.
if gameover && is_key_pressed(KeyCode::Space) {
squares.clear();
bullets.clear();
circle.x = screen_width() / 2.0;
circle.y = screen_height() / 2.0;
score = 0;
gameover = false;
}
Displaying scores
Finally, we’ll display the score
and high_score
on the screen. We’ll
display the score
in the top left corner of the screen. To be able to
display the high score in the top right corner we’ll use the function
measure_text()
to calculate how far from the right edge of the screen the text should be
displayed.
To ensure that the dimensions are correct we must use the same arguments for
both measure_text()
and draw_text()
. The arguments for these functions are
text
, font
, font_size
and font_scale
. Since we aren’t setting any
specific font or scaling the size of the text, we’ll use None
as the value
for font
, and 1.0
as font_scale
. The font_size
can be set to 25.0
.
draw_text(
format!("Score: {}", score).as_str(),
10.0,
35.0,
25.0,
WHITE,
);
let highscore_text = format!("High score: {}", high_score);
let text_dimensions = measure_text(highscore_text.as_str(), None, 25, 1.0);
draw_text(
highscore_text.as_str(),
screen_width() - text_dimensions.width - 10.0,
35.0,
25.0,
WHITE,
);
The function measure_text()
returns the struct
TextDimensions
which contains the fields width
, height
, and offset_y
.
Run the game and try to get a high score!