In src/board.rs
add a Position
struct. This will represent a cell on the board using an x index and a y index. We’ll need a variety of trait behavior so derive the relevant traits as shown. The most important one is Component
, which we need to insert positions as components on entities so we can query them later.
#[derive(
Debug, PartialEq, Copy, Clone, Eq, Hash, Component,
)]
pub struct Position {
pub x: u8,
pub y: u8,
}
In src/lib.rs
add snake
as a public submodule.
pub mod board;
pub mod colors;
pub mod snake;
and then create src/snake.rs
to hold Snake related behavior.
Bring the VecDeque
into scope, as well as the board::Position
from the crate root.
The Snake
struct itself is queried by other systems, so we’ll make it public as well as making it’s segments
accessible. The type of the segments is a VecDeque<Position>
similar to how we might have written the type for a Vec
containing Position
s as Vec<Position>
.
Then we’ll implement the Default
trait for our Snake, so that we have a default starting position and number of segments.
As we talked about in the VecDeque
lesson, we’ll order our Snake segment positions from “head-to-tail”.
use std::collections::VecDeque;
use crate::board::Position;
#[derive(Debug)]
pub struct Snake {
pub segments: VecDeque<Position>,
}
impl Default for Snake {
fn default() -> Self {
Self {
segments: VecDeque::from([
Position { x: 4, y: 4 },
Position { x: 3, y: 4 },
]),
}
}
}
In src/main.rs
bring Snake
into scope and initialize it as a resource. init_resource
will call into our Default
implementation and create a new default Snake
resource when the game boots up.
use bevy::prelude::*;
use snake::{board::spawn_board, snake::Snake};
fn main() {
App::new()
.insert_resource(WindowDescriptor {
title: "Snake!".to_string(),
..Default::default()
})
.add_plugins(DefaultPlugins)
.insert_resource(ClearColor(Color::rgb(
0.52, 0.73, 0.17,
)))
.init_resource::<Snake>()
.add_startup_system(setup)
.add_startup_system(spawn_board)
.run();
}
fn setup(mut commands: Commands) {
commands
.spawn_bundle(OrthographicCameraBundle::new_2d());
}
Spawning a Snake
The Snake
resource now exists in our game, but we need to spawn the sprites it represents onto the board. When we do that we’ll have to access the TILE_SIZE
const to make our snake sprite the appropriate size and use Board
's helper functions to position it on screen.
For these reasons it makes the most sense to define the logic for adding a snake segment to the screen in src/board.rs
.
We’ll add some logic to the spawn_board
system for now and refactor later. Not everything needs to be in a perfect place when we write it for the first time.
The system now needs access to the Snake
resource, so add that to the system’s arguments.
Board
doesn’t implement clone, so when we .insert
it on the board sprite entity it gets moved out of scope. We’ll write a bit of hacky code here and construct a new board because we don’t actually need the specific instance of the board from earlier.
pub fn spawn_board(
mut commands: Commands,
snake: Res<Snake>,
) {
let board = Board::new(20);
commands
.spawn_bundle(...)
.with_children(...)
.insert(board);
let board = Board::new(20);
for segment in snake.segments.iter() {
commands
.spawn_bundle(SpriteBundle {
sprite: Sprite {
color: COLORS.snake,
custom_size: Some(Vec2::new(
TILE_SIZE, TILE_SIZE,
)),
..Sprite::default()
},
transform: Transform::from_xyz(
board.cell_position_to_physical(
segment.x,
),
board.cell_position_to_physical(
segment.y,
),
2.0,
),
..Default::default()
})
.insert(segment.clone());
}
}
Iterating over each of the snake segments, we can spawn in a Sprite
attached to an entity and insert the segment’s Position
as a component on the same entity.
In src/colors.rs
add a color for the snake. I’ve chosen white here.
use bevy::prelude::Color;
pub struct Colors {
pub board: Color,
pub tile_placeholder: Color,
pub tile_placeholder_dark: Color,
pub snake: Color,
}
pub const COLORS: Colors = Colors {
board: Color::rgb(0.42, 0.63, 0.07),
tile_placeholder: Color::rgb(0.62, 0.83, 0.27),
tile_placeholder_dark: Color::rgb(0.57, 0.78, 0.22),
snake: Color::WHITE,
};
Now if we cargo run
we can see the default snake segments occupying their places on the board.