Skip to content

Commit 14106f7

Browse files
authored
Fixed minor issues in Pong tutorial book (#1051)
- [x] Fixed a reference to `hblank` in Chapter 4.2 - [x] Fixed ButtonController variable name in Chapter 4.4 - [x] Fixed background aseprite file name in Chapter 4.6 - [x] Fixed missing use path to `Mixer` and `SoundChannel` in Chapter 4.8 - [x] Fixed use path to `sound::mixer::SoundData` in Chapter 4.8 - [x] ~~Fixed missing path to Priority in Ball's code snippet in Chapter 4.9~~ - [x] Added getter and decrement function for Paddle's health in Chapter 4.9 - [x] Added note to call `show_cpu_health(...);` in Chapter 4.9 - [x] Fixed missing `pub` keywords for Ball and Paddle's structs and their functions in multiple chapters. While this is fine when both structs are in the same module as main function, it won't compile when user moves them into separate files. I hope you will find my changes useful :smile:
2 parents c446e7a + 3a1d85e commit 14106f7

File tree

8 files changed

+42
-28
lines changed

8 files changed

+42
-28
lines changed

book/src/pong/02_sprites.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,7 @@ After it has finished rendering to each pixel of the screen, it briefly pauses r
8989
This period of no drawing is called the 'vertical blanking interval' which is shortened to `vblank`.
9090
There is also a 'horizontal blanking interval', but that is outside of the scope of this tutorial[^hblank].
9191

92-
[^hblank]:
93-
Timing this can give you some really cool effects allowing you to push the hardware.
94-
`agb` provides support for this by using `dma`, this is an advanced technique that is out of scope of this tutorial.
92+
[^hblank]: Timing this can give you some really cool effects allowing you to push the hardware. `agb` provides support for this by using `dma`, this is an advanced technique that is out of scope of this tutorial.
9593

9694
The `frame.commit()` method automatically waits for this `vblank` state before rendering your sprites to avoid moving a sprite while it is being rendered which could cause tearing of your objects.
9795

book/src/pong/03_meta_sprites.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,25 @@ paddle_end.set_vflip(true);
4040
Now the paddle will display correctly. It's rather awkward to use, however, having to set all these positions correctly. Therefore we should encapsulate the logic of this object.
4141

4242
```rust
43-
struct Paddle {
43+
pub struct Paddle {
4444
x: i32,
4545
y: i32,
4646
}
4747

4848
impl Paddle {
49-
fn new(start_x: i32, start_y: i32) -> Self {
49+
pub fn new(start_x: i32, start_y: i32) -> Self {
5050
Self {
5151
x: start_x,
5252
y: start_y,
5353
}
5454
}
5555

56-
fn set_pos(&mut self, x: i32, y: i32) {
56+
pub fn set_pos(&mut self, x: i32, y: i32) {
5757
self.x = x;
5858
self.y = y;
5959
}
6060

61-
fn show(&self, frame: &mut GraphicsFrame) {
61+
pub fn show(&self, frame: &mut GraphicsFrame) {
6262
Object::new(sprites::PADDLE_END.sprite(0))
6363
.set_pos((self.x, self.y))
6464
.show(frame);

book/src/pong/04_paddle_movement.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ To add button control to our game, we will need a [`ButtonController`](https://d
2626
Add this near the top of your main function:
2727

2828
```rust
29-
let mut input = agb::input::ButtonController::new();
29+
let mut button_controller = agb::input::ButtonController::new();
3030
```
3131

3232
The button controller is not part of the `Gba` struct because it only allows for reading and not writing so does not need to be controlled by the borrow checker.
@@ -40,7 +40,7 @@ button_controller.update();
4040
To handle the movement of the paddles, let's add a new method to the `Paddle` struct.
4141

4242
```rust
43-
fn move_by(&mut self, y: i32) {
43+
pub fn move_by(&mut self, y: i32) {
4444
self.y += y;
4545
}
4646
```

book/src/pong/05_paddle_collision.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,23 @@ ball.set_pos(ball_pos);
5353
You can store the paddle position as `pos` instead of `x` and `y` separately:
5454

5555
```rust
56-
struct Paddle {
56+
pub struct Paddle {
5757
pos: Vector2D<i32>,
5858
}
5959
```
6060

6161
You can change the `set_pos()` method on `Paddle` to take a `Vector2D<i32>` instead of separate `x` and `y` arguments as follows:
6262

6363
```rust
64-
fn set_pos(&mut self, pos: Vector2D<i32>) {
64+
pub fn set_pos(&mut self, pos: Vector2D<i32>) {
6565
self.pos = pos;
6666
}
6767
```
6868

6969
And when rendering:
7070

7171
```rust
72-
fn show(frame: &mut GraphicsFrame) {
72+
pub fn show(frame: &mut GraphicsFrame) {
7373
Object::new(sprites::PADDLE_END.sprite(0))
7474
.set_pos(self.pos)
7575
.show(frame);
@@ -86,7 +86,7 @@ fn show(frame: &mut GraphicsFrame) {
8686
`move_by()` can also be updated as follows:
8787

8888
```rust
89-
fn move_by(&mut self, y: i32) {
89+
pub fn move_by(&mut self, y: i32) {
9090
self.y += vec2(0, y);
9191
}
9292
```
@@ -105,7 +105,7 @@ We will assume that the ball and the paddle both have axis-aligned bounding boxe
105105
Lets add a simple method to the `Paddle` impl which returns the collision rectangle for it:
106106

107107
```rust
108-
fn collision_rect(&self) -> Rect<i32> {
108+
pub fn collision_rect(&self) -> Rect<i32> {
109109
Rect::new(self.pos, vec2(16, 16 * 3))
110110
}
111111
```

book/src/pong/06_background.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use agb::include_background_gfx;
4444

4545
include_background_gfx!(
4646
mod background,
47-
PLAY_FIELD => deduplicate "gfx/play_field.aseprite",
47+
PLAY_FIELD => deduplicate "gfx/background.aseprite",
4848
);
4949
```
5050

book/src/pong/07_fixnums.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@ The solution to this problem used by almost every Game Boy Advance game is to us
1818
Before we go to put fixed point numbers in the game, we need to do a quick change to pull the ball into its own struct.
1919

2020
```rust
21-
struct Ball {
21+
pub struct Ball {
2222
pos: Vector2D<i32>,
2323
velocity: Vector2D<i32>,
2424
}
2525

2626
impl Ball {
27-
fn new(pos: Vector2D<i32>, velocity: Vector2D<i32>) -> Self {
27+
pub fn new(pos: Vector2D<i32>, velocity: Vector2D<i32>) -> Self {
2828
Self { pos, velocity }
2929
}
3030

31-
fn update(&mut self, paddle_a: &Paddle, paddle_b: &Paddle) {
31+
pub fn update(&mut self, paddle_a: &Paddle, paddle_b: &Paddle) {
3232
// Speculatively move the ball, we'll update the velocity if this causes it to intersect with either the
3333
// edge of the map or a paddle.
3434
let potential_ball_pos = self.pos + self.velocity;
@@ -54,7 +54,7 @@ impl Ball {
5454
self.pos += self.velocity;
5555
}
5656

57-
fn show(&self, frame: &mut GraphicsFrame) {
57+
pub fn show(&self, frame: &mut GraphicsFrame) {
5858
Object::new(sprites::BALL.sprite(0))
5959
.set_pos(self.pos)
6060
.show(frame);
@@ -101,14 +101,14 @@ We'll now replace the paddle position and the ball position and velocity with `F
101101
Some notable changes:
102102

103103
```rust
104-
fn move_by(&mut self, y: Fixed) {
104+
pub fn move_by(&mut self, y: Fixed) {
105105
// we now need to cast the 0 to a Fixed which you can do with
106106
// `Fixed::from(0)` or `0.into()`. But the preferred one is the `num!` macro
107107
// which we imported above.
108108
self.pos += vec2(num!(0), y);
109109
}
110110

111-
fn collision_rect(&self) -> Rect<Fixed> {
111+
pub fn collision_rect(&self) -> Rect<Fixed> {
112112
// Same idea here with creating a fixed point rectangle
113113
Rect::new(self.pos, vec2(num!(16), num!(16 * 3)))
114114
}
@@ -118,7 +118,7 @@ Since you can only show things on the Game Boy Advance's screen in whole pixel c
118118
integer to show the paddle in a specific location:
119119

120120
```rust
121-
fn show(&self, frame: &mut GraphicsFrame) {
121+
pub fn show(&self, frame: &mut GraphicsFrame) {
122122
let sprite_pos = self.pos.round();
123123

124124
Object::new(sprites::PADDLE_END.sprite(0))

book/src/pong/08_bgm.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ ffmpeg -i ~/Downloads/laserShoot.wav -ar 32768 sfx/ball-paddle-hit.wav
4949
Import the wav file using [`include_wav!()`](https://docs.rs/agb/latest/agb/macro.include_wav.html).
5050

5151
```rust
52-
use agb::{include_wav, mixer::SoundData};
52+
use agb::{include_wav, sound::mixer::SoundData};
5353

5454
static BALL_PADDLE_HIT: SoundData = include_wav!("sfx/ball-paddle-hit.wav");
5555
```
@@ -68,6 +68,8 @@ mixer.play_sound(hit_sound);
6868
We'll do this in a separate function:
6969

7070
```rust
71+
use agb::sound::mixer::{Mixer, SoundChannel};
72+
7173
fn play_hit(mixer: &mut Mixer) {
7274
let hit_sound = SoundChannel::new(BALL_PADDLE_HIT);
7375
mixer.play_sound(hit_sound);

book/src/pong/09_keeping_score.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,35 @@ If you lose while you have 0 lives, you lose the game.
2828
Firstly, let's add the score to the `Paddle` objects:
2929

3030
```rust
31-
struct Paddle {
31+
pub struct Paddle {
3232
pos: Vector2D<Fixed>,
3333
health: i32,
3434
}
3535
```
3636

3737
and in the `new()` function, initialise it to 3.
3838

39+
We're going to use it for displaying the health as hearts and decrementing it when ball touches the side of the screen. Add these functions to `Paddle` struct.
40+
41+
```rust
42+
pub fn health(&self) -> i32 {
43+
self.health
44+
}
45+
46+
pub fn decrement_health(&mut self) {
47+
self.health -= 1;
48+
}
49+
```
50+
3951
We can then reduce the health in the ball's update function (you'll have to change the `update` function to take `&mut Paddle`):
4052

4153
```rust
4254
if potential_ball_pos.x <= num!(0) {
4355
self.velocity.x *= -1;
44-
paddle_a.health -= 1;
56+
paddle_a.decrement_health();
4557
} else if potential_ball_pos.x >= num!(agb::display::WIDTH - 16) {
4658
self.velocity.x *= -1;
47-
paddle_b.health -= 1;
59+
paddle_b.decrement_health();
4860
}
4961
```
5062

@@ -131,7 +143,7 @@ So let's display up to 3 hearts with the given tile indexes by placing the follo
131143

132144
```rust
133145
for i in 0..3 {
134-
let tile_index = if i < paddle_a.health { 4 } else { 5 };
146+
let tile_index = if i < paddle_a.health() { 4 } else { 5 };
135147
player_health_background.set_tile(
136148
(i + 4, 0),
137149
&background::SCORE.tiles,
@@ -183,7 +195,7 @@ fn show_cpu_health(paddle: &Paddle, frame: &mut GraphicsFrame) {
183195

184196
// For each heart frame, show that too
185197
for i in 0..3 {
186-
let heart_frame = if i < paddle.health { 0 } else { 1 };
198+
let heart_frame = if i < paddle.health() { 0 } else { 1 };
187199

188200
Object::new(sprites::HEART.sprite(heart_frame))
189201
.set_pos(top_left + vec2(16 + i * 8 + TEXT_HEART_GAP, 0))
@@ -192,6 +204,8 @@ fn show_cpu_health(paddle: &Paddle, frame: &mut GraphicsFrame) {
192204
}
193205
```
194206

207+
Don't forget to call `show_cpu_health(&paddle_b, &mut frame);` before `frame.commit()`!
208+
195209
Running the example again you'll see the health bar for the player and the CPU, and you wouldn't be able
196210
to tell that they are using completely different rendering mechanisms.
197211

0 commit comments

Comments
 (0)