Pancake Cat
Overview
Walkthrough
Initialize the Project
Begin by creating a new project called pancake-cat
:
turbo init pancake-cat
This initializes a Rust project with the following structure:
pancake-cat/ # Your project's root directory.
βββ src/ # The directory of your code.
β βββ lib.rs # The main file for the game.
βββ Cargo.toml # Rust project manifest.
βββ turbo.toml # Turbo configuration.
Create a sprites
Folder
Inside your project directory, create a folder named sprites
. This folder will contain all your game sprites.
your-project-dir/ # Your project's root directory.
βββ sprites/ # The directory of your sprite assets.
βββ src/ # The directory of your code.
β βββ lib.rs # The main file for the game.
βββ Cargo.toml # Rust project manifest.
βββ turbo.toml # Turbo configuration.
Add Sprite Assets
Add the following files to the sprites
directory.
Run the Game
At this point, we can run our game and leave it running as we make changes. Don't worry, it is just a blank screen for now!
turbo run -w pancake-cat
Game State Initialization
Add this code to the top of your lib.rs
file. We'll explain what everything does later on:
turbo::init! {
struct GameState {
frame: u32,
last_munch_at: u32,
cat_x: f32,
cat_y: f32,
cat_r: f32,
pancakes: Vec<struct Pancake {
x: f32,
y: f32,
vel: f32,
radius: f32,
}>,
score: u32,
} = Self {
frame: 0,
last_munch_at: 0,
cat_x: 128.0,
cat_y: 112.0,
cat_r: 8.0,
pancakes: vec![],
score: 0,
}
}
Update the Game Loop
Use the Turbo game loop to run your game logic:
turbo::go!({
let mut state = GameState::load();
// The code in the following steps will go here
state.save();
});
Cat Movement
The first thing to do is handle player input to move the cat. Add this code inbetween loading and saving the GameState
:
if gamepad(0).left.pressed() {
state.cat_x -= 2.;
}
if gamepad(0).right.pressed() {
state.cat_x += 2.;
}
Now click over to your game window and press left and right on your keyboard. You should see the cat moving around!
Pancake Generation
Randomly spawn pancakes:
if rand() % 64 == 0 {
let pancake = Pancake {
x: (rand() % 256) as f32,
y: 0.0,
vel: (rand() % 3 + 1) as f32,
radius: (rand() % 10 + 5) as f32,
};
state.pancakes.push(pancake);
}
Pancake Movement & Collision
Now we can add some code that moves the Pancakes downwards, and checks if they are overlapping the cat. When they overlap, we add 1 to state.score
.
let cat_center = (state.cat_x + state.cat_r, state.cat_y + state.cat_r);
state.pancakes.retain_mut(|p| {
p.y += p.vel;
let dx = cat_center.0 - (p.x + p.radius);
let dy = cat_center.1 - (p.y + p.radius);
let distance = (dx * dx + dy * dy).sqrt();
let radii_sum = state.cat_r + p.radius;
let radii_diff = (state.cat_r - p.radius).abs();
if radii_diff <= distance && distance <= radii_sum {
state.score += 1;
state.last_munch_at = state.frame;
false
} else if p.y < 144. + (p.radius * 2.) {
true
} else {
false
}
});
Background Tiles
Draw a simple animated background:
clear(0x00ffffff);
let frame = (state.frame as i32) / 2;
for col in 0..9 {
for row in 0..6 {
let x = ((col * 32 + frame) % (272 + 16)) - 32;
let y = ((row * 32 + frame) % (144 + 16)) - 24;
sprite!("heart", x = x, y = y);
}
}
state.frame += 1;
Drawing the Cat
Render the cat sprite with animation frame logic handled by Turbo:
sprite!("munch_cat", x = state.cat_x - state.cat_r, y = state.cat_y - 16.0);
Drawing the Pancakes
Draw each Pancake by making 3 concentric circles:
for p in &state.pancakes {
circ!(x = p.x, y = p.y + 1.0, d = p.radius + 2., color = 0x000000aa);
circ!(x = p.x, y = p.y, d = p.radius + 1., color = 0xf4d29cff);
circ!(x = p.x, y = p.y, d = p.radius, color = 0xdba463ff);
}
Speech Bubble
Display a speech bubble with βMUNCH!β when the cat catches a pancake:
if state.frame >= 64 && state.frame.saturating_sub(state.last_munch_at) <= 60 {
rect!(w = 30, h = 10, x = state.cat_x + 32.0, y = state.cat_y);
circ!(d = 10, x = state.cat_x + 28.0, y = state.cat_y);
rect!(w = 10, h = 5, x = state.cat_x + 28.0, y = state.cat_y + 5.0);
circ!(d = 10, x = state.cat_x + 56.0, y = state.cat_y);
text!("MUNCH!", x = state.cat_x + 33.0, y = state.cat_y + 3.0, font = "small", color = 0x000000ff);
}
Score Display
Draw the player's score in the top-left corner:
text!("Score: {}", state.score; x = 10, y = 10, font = "large", color = 0xffffffff);
Conclusion
You just built a fully playable game from scratch using Turbo: input handling, game state, rendering, and collision β all in a tight feedback loop with real-time updates.
You've met the cat. Youβve caught the pancakes. And youβve laid the foundation for every game youβll make next.
Next Steps
- Swap out hearts or pancakes for your own sprite sets
- Add
sounds
when the cat munches or misses - Track lives or missed pancakes for added challenge
- Create a title screen and end state
- Export your game to the web using
turbo export