gba/book/src/ch03/memory_game.md

213 lines
5.5 KiB
Markdown
Raw Normal View History

2018-11-19 16:19:13 +11:00
# memory_game
For this example to show off our new skills we'll make a "memory" game. The idea
is that there's some face down cards and you pick one, it flips, you pick a
second, if they match they both go away, if they don't match they both turn back
face down. The player keeps going until all the cards are gone, then we'll deal
the cards again.
For this example, I started with the `light_cycle.rs` example and then just
copied it into a new file, `memory_game.rs`. Then I added most all the code from
the previous sections right into that file, so we'll assume that all those
definitions are in scope.
## Getting Some Images
First we need some images to show! Let's have one for our little selector thingy
that we'll move around to pick cards with. How about some little triangles at
the corner of a square like on a picture frame.
```rust
#[rustfmt::skip]
pub const CARD_SELECTOR: Tile4bpp = Tile4bpp {
data : [
0x44400444,
0x44000044,
0x40000004,
0x00000000,
0x00000000,
0x40000004,
0x44000044,
0x44400444
]
};
```
That weird looking attribute keeps rustfmt from spreading out the values, so
that we can see it as an ASCII art. Now that we understand what an individual
tile looks like, let's add some mono-color squares.
```rust
#[rustfmt::skip]
pub const FULL_ONE: Tile4bpp = Tile4bpp {
data : [
0x11111111,
0x11111111,
0x11111111,
0x11111111,
0x11111111,
0x11111111,
0x11111111,
0x11111111,
]
};
#[rustfmt::skip]
pub const FULL_TWO: Tile4bpp = Tile4bpp {
data : [
0x22222222,
0x22222222,
0x22222222,
0x22222222,
0x22222222,
0x22222222,
0x22222222,
0x22222222
]
};
#[rustfmt::skip]
pub const FULL_THREE: Tile4bpp = Tile4bpp {
data : [
0x33333333,
0x33333333,
0x33333333,
0x33333333,
0x33333333,
0x33333333,
0x33333333,
0x33333333
]
};
```
We can control the rest with palbank selection. Since there's 16 palbanks,
that's 48 little colored squares we can make, and 16 different selector colors,
which should be plenty in both cases.
## Setup The Images
### Arrange the PALRAM
Alright, so, as we went over, the first step is to make sure that we've got our
palette data in order. We'll be using this to set our palette values.
```rust
pub fn set_bg_palette(slot: usize, color: u16) {
assert!(slot < 256);
unsafe { PALRAM_BG_BASE.offset(slot as isize).write(color) }
}
```
Should the type of `slot` be changed to `u8` instead of `usize`? Well, maybe.
Let's not worry about it at the moment.
Of course, we don't need to set the color black, all the values start as black.
We just need to set the other colors we'll be wanting. For this demo, we'll just
use the same basic colors for both the BG and Object stuff.
```rust
pub fn init_palette() {
// palbank 0: black/white/gray
set_bg_palette(2, rgb16(31, 31, 31));
set_bg_palette(3, rgb16(15, 15, 15));
// palbank 1 is reds
set_bg_palette(1 * 16 + 1, rgb16(31, 0, 0));
set_bg_palette(1 * 16 + 2, rgb16(22, 0, 0));
set_bg_palette(1 * 16 + 3, rgb16(10, 0, 0));
// palbank 2 is greens
set_bg_palette(2 * 16 + 1, rgb16(0, 31, 0));
set_bg_palette(2 * 16 + 2, rgb16(0, 22, 0));
set_bg_palette(2 * 16 + 3, rgb16(0, 10, 0));
// palbank 2 is blues
set_bg_palette(3 * 16 + 1, rgb16(0, 0, 31));
set_bg_palette(3 * 16 + 2, rgb16(0, 0, 22));
set_bg_palette(3 * 16 + 3, rgb16(0, 0, 10));
// Direct copy all BG selections into OBJ palette too
let mut bgp = PALRAM_BG_BASE;
let mut objp = PALRAM_OBJECT_BASE;
for _ in 0..(4 * 16) {
objp.write(bgp.read());
bgp = bgp.offset(1);
objp = objp.offset(1);
}
}
```
### Arrange the Objects
So, time to think about objects. I'm thinking we'll have 13 objects in use. One
for the selector, and then 12 for the cards (a simple grid that's 4 wide and 3
tall).
We want a way to easily clear away all the objects that we're not using, which
is all the slots starting at some index and then going to the end.
```rust
pub fn clear_objects_starting_with(base_slot: usize) {
let mut obj = ObjectAttributes::default();
obj.set_rendering(ObjectRenderMode::Disabled);
for s in base_slot..128 {
set_object_attributes(s, obj);
}
}
```
Next we set out the positions of our cards. We set the tile data we need, and
then assign the object attributes to go with it. For this, we'll make the
position finder function be its own thing since we'll also need it for the card
selector to move around. Finally, we set our selector as being at position 0,0
of the card grid.
```rust
pub fn position_of_card(card_col: usize, card_row: usize) -> (u16, u16) {
(10 + card_col as u16 * 17, 5 + card_row as u16 * 15)
}
pub fn arrange_cards() {
set_obj_tile_4bpp(1, FULL_ONE);
set_obj_tile_4bpp(2, FULL_TWO);
set_obj_tile_4bpp(3, FULL_THREE);
let mut obj = ObjectAttributes::default();
obj.set_tile_index(2); // along with palbank0, this is a white card
for card_row in 0..3 {
for card_col in 0..4 {
let (col, row) = position_of_card(card_col, card_row);
obj.set_column(col);
obj.set_row(row);
set_object_attributes(1 + card_col as usize + (card_row as usize * 3), obj);
}
}
}
pub fn init_selector() {
set_obj_tile_4bpp(0, CARD_SELECTOR);
let mut obj = ObjectAttributes::default();
let (col, row) = position_of_card(0, 0);
obj.set_column(col);
obj.set_row(row);
set_object_attributes(0, obj);
}
```
### Arrange the Background
TODO
## Shuffling The Cards
TODO
## Picking One Card
TODO
## Picking The Second Card
TODO
## Resetting The Game
2018-11-19 16:19:13 +11:00
TODO