2020-11-04 10:09:02 -05:00
|
|
|
use bevy::prelude::*;
|
2020-12-24 14:07:46 -05:00
|
|
|
use bevy::render::pass::ClearColor;
|
2020-12-24 14:14:18 -05:00
|
|
|
use rand::prelude::random;
|
|
|
|
use std::time::Duration;
|
2020-11-04 10:09:02 -05:00
|
|
|
|
2020-12-24 14:01:05 -05:00
|
|
|
const ARENA_WIDTH: u32 = 10;
|
|
|
|
const ARENA_HEIGHT: u32 = 10;
|
|
|
|
|
|
|
|
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
|
|
|
|
struct Position {
|
|
|
|
x: i32,
|
|
|
|
y: i32,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Size {
|
|
|
|
width: f32,
|
|
|
|
height: f32,
|
|
|
|
}
|
|
|
|
impl Size {
|
|
|
|
pub fn square(x: f32) -> Self {
|
|
|
|
Self {
|
|
|
|
width: x,
|
|
|
|
height: x,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-30 09:52:14 -05:00
|
|
|
struct SnakeHead {
|
|
|
|
direction: Direction,
|
|
|
|
}
|
2020-12-24 13:41:55 -05:00
|
|
|
struct Materials {
|
|
|
|
head_material: Handle<ColorMaterial>,
|
2020-12-30 10:10:39 -05:00
|
|
|
segment_material: Handle<ColorMaterial>,
|
2020-12-24 14:14:18 -05:00
|
|
|
food_material: Handle<ColorMaterial>,
|
|
|
|
}
|
|
|
|
|
2020-12-30 09:52:14 -05:00
|
|
|
struct SnakeMoveTimer(Timer);
|
|
|
|
|
2020-12-30 10:10:39 -05:00
|
|
|
struct SnakeSegment;
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
struct SnakeSegments(Vec<Entity>);
|
|
|
|
|
2020-12-24 14:14:18 -05:00
|
|
|
struct Food;
|
|
|
|
|
|
|
|
struct FoodSpawnTimer(Timer);
|
|
|
|
impl Default for FoodSpawnTimer {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self(Timer::new(Duration::from_millis(1000), true))
|
|
|
|
}
|
2020-12-24 13:41:55 -05:00
|
|
|
}
|
|
|
|
|
2020-12-30 09:52:14 -05:00
|
|
|
#[derive(PartialEq, Copy, Clone)]
|
|
|
|
enum Direction {
|
|
|
|
Left,
|
|
|
|
Up,
|
|
|
|
Right,
|
|
|
|
Down,
|
|
|
|
}
|
|
|
|
impl Direction {
|
|
|
|
fn opposite(self) -> Self {
|
|
|
|
match self {
|
|
|
|
Self::Left => Self::Right,
|
|
|
|
Self::Right => Self::Left,
|
|
|
|
Self::Up => Self::Down,
|
|
|
|
Self::Down => Self::Up,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-24 13:41:55 -05:00
|
|
|
fn setup(commands: &mut Commands, mut materials: ResMut<Assets<ColorMaterial>>) {
|
2020-12-24 13:34:47 -05:00
|
|
|
commands.spawn(Camera2dBundle::default());
|
2020-12-24 13:41:55 -05:00
|
|
|
commands.insert_resource(Materials {
|
|
|
|
head_material: materials.add(Color::rgb(0.7, 0.7, 0.7).into()),
|
2020-12-30 10:10:39 -05:00
|
|
|
segment_material: materials.add(Color::rgb(0.3, 0.3, 0.3).into()),
|
2020-12-24 14:14:18 -05:00
|
|
|
food_material: materials.add(Color::rgb(1.0, 0.0, 1.0).into()),
|
2020-12-24 13:41:55 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-12-30 10:10:39 -05:00
|
|
|
fn spawn_snake(
|
|
|
|
commands: &mut Commands,
|
|
|
|
materials: Res<Materials>,
|
|
|
|
mut segments: ResMut<SnakeSegments>,
|
|
|
|
) {
|
|
|
|
segments.0 = vec![
|
|
|
|
commands
|
|
|
|
.spawn(SpriteBundle {
|
|
|
|
material: materials.head_material.clone(),
|
|
|
|
sprite: Sprite::new(Vec2::new(10.0, 10.0)),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.with(SnakeHead {
|
|
|
|
direction: Direction::Up,
|
|
|
|
})
|
|
|
|
.with(SnakeSegment)
|
|
|
|
.with(Position { x: 3, y: 3 })
|
|
|
|
.with(Size::square(0.8))
|
|
|
|
.current_entity()
|
|
|
|
.unwrap(),
|
|
|
|
spawn_segment(
|
|
|
|
commands,
|
|
|
|
&materials.segment_material,
|
|
|
|
Position { x: 3, y: 2 },
|
|
|
|
),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
fn spawn_segment(
|
|
|
|
commands: &mut Commands,
|
|
|
|
material: &Handle<ColorMaterial>,
|
|
|
|
position: Position,
|
|
|
|
) -> Entity {
|
2020-12-24 13:41:55 -05:00
|
|
|
commands
|
|
|
|
.spawn(SpriteBundle {
|
2020-12-30 10:10:39 -05:00
|
|
|
material: material.clone(),
|
2020-12-24 13:41:55 -05:00
|
|
|
..Default::default()
|
|
|
|
})
|
2020-12-30 10:10:39 -05:00
|
|
|
.with(SnakeSegment)
|
|
|
|
.with(position)
|
|
|
|
.with(Size::square(0.65))
|
|
|
|
.current_entity()
|
|
|
|
.unwrap()
|
2020-11-04 10:09:02 -05:00
|
|
|
}
|
|
|
|
|
2020-12-24 13:49:23 -05:00
|
|
|
fn snake_movement(
|
|
|
|
keyboard_input: Res<Input<KeyCode>>,
|
2020-12-30 09:52:14 -05:00
|
|
|
snake_timer: ResMut<SnakeMoveTimer>,
|
|
|
|
mut heads: Query<(Entity, &mut SnakeHead)>,
|
|
|
|
mut positions: Query<&mut Position>,
|
2020-12-24 13:49:23 -05:00
|
|
|
) {
|
2020-12-30 09:52:14 -05:00
|
|
|
if let Some((head_entity, mut head)) = heads.iter_mut().next() {
|
|
|
|
let mut head_pos = positions.get_mut(head_entity).unwrap();
|
|
|
|
let dir: Direction = if keyboard_input.pressed(KeyCode::Left) {
|
|
|
|
Direction::Left
|
|
|
|
} else if keyboard_input.pressed(KeyCode::Down) {
|
|
|
|
Direction::Down
|
|
|
|
} else if keyboard_input.pressed(KeyCode::Up) {
|
|
|
|
Direction::Up
|
|
|
|
} else if keyboard_input.pressed(KeyCode::Right) {
|
|
|
|
Direction::Right
|
|
|
|
} else {
|
|
|
|
head.direction
|
|
|
|
};
|
|
|
|
if dir != head.direction.opposite() {
|
|
|
|
head.direction = dir;
|
2020-12-24 13:49:23 -05:00
|
|
|
}
|
2020-12-30 09:52:14 -05:00
|
|
|
if !snake_timer.0.finished() {
|
|
|
|
return;
|
2020-12-24 13:49:23 -05:00
|
|
|
}
|
2020-12-30 09:52:14 -05:00
|
|
|
match &head.direction {
|
|
|
|
Direction::Left => {
|
|
|
|
head_pos.x -= 1;
|
|
|
|
}
|
|
|
|
Direction::Right => {
|
|
|
|
head_pos.x += 1;
|
|
|
|
}
|
|
|
|
Direction::Up => {
|
|
|
|
head_pos.y += 1;
|
|
|
|
}
|
|
|
|
Direction::Down => {
|
|
|
|
head_pos.y -= 1;
|
|
|
|
}
|
|
|
|
};
|
2020-12-24 13:45:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-24 14:01:05 -05:00
|
|
|
fn size_scaling(windows: Res<Windows>, mut q: Query<(&Size, &mut Sprite)>) {
|
|
|
|
let window = windows.get_primary().unwrap();
|
|
|
|
for (sprite_size, mut sprite) in q.iter_mut() {
|
|
|
|
sprite.size = Vec2::new(
|
|
|
|
sprite_size.width / ARENA_WIDTH as f32 * window.width() as f32,
|
|
|
|
sprite_size.height / ARENA_HEIGHT as f32 * window.height() as f32,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn position_translation(windows: Res<Windows>, mut q: Query<(&Position, &mut Transform)>) {
|
|
|
|
fn convert(pos: f32, bound_window: f32, bound_game: f32) -> f32 {
|
|
|
|
let tile_size = bound_window / bound_game;
|
|
|
|
pos / bound_game * bound_window - (bound_window / 2.) + (tile_size / 2.)
|
|
|
|
}
|
|
|
|
|
|
|
|
let window = windows.get_primary().unwrap();
|
|
|
|
for (pos, mut transform) in q.iter_mut() {
|
|
|
|
transform.translation = Vec3::new(
|
|
|
|
convert(pos.x as f32, window.width() as f32, ARENA_WIDTH as f32),
|
|
|
|
convert(pos.y as f32, window.height() as f32, ARENA_HEIGHT as f32),
|
|
|
|
0.0,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-24 14:14:18 -05:00
|
|
|
fn food_spawner(
|
|
|
|
commands: &mut Commands,
|
|
|
|
materials: Res<Materials>,
|
|
|
|
time: Res<Time>,
|
|
|
|
mut timer: Local<FoodSpawnTimer>,
|
|
|
|
) {
|
2020-12-24 14:16:49 -05:00
|
|
|
if timer.0.tick(time.delta_seconds()).finished() {
|
2020-12-24 14:14:18 -05:00
|
|
|
commands
|
|
|
|
.spawn(SpriteBundle {
|
|
|
|
material: materials.food_material.clone(),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.with(Food)
|
|
|
|
.with(Position {
|
|
|
|
x: (random::<f32>() * ARENA_WIDTH as f32) as i32,
|
|
|
|
y: (random::<f32>() * ARENA_HEIGHT as f32) as i32,
|
|
|
|
})
|
|
|
|
.with(Size::square(0.8));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-30 09:52:14 -05:00
|
|
|
fn snake_timer(time: Res<Time>, mut snake_timer: ResMut<SnakeMoveTimer>) {
|
|
|
|
snake_timer.0.tick(time.delta_seconds());
|
|
|
|
}
|
|
|
|
|
2020-11-04 10:09:02 -05:00
|
|
|
fn main() {
|
|
|
|
App::build()
|
2020-12-24 14:07:46 -05:00
|
|
|
.add_resource(ClearColor(Color::rgb(0.04, 0.04, 0.04)))
|
|
|
|
.add_resource(WindowDescriptor {
|
|
|
|
title: "Snake!".to_string(),
|
|
|
|
width: 500.0,
|
|
|
|
height: 500.0,
|
|
|
|
..Default::default()
|
|
|
|
})
|
2020-12-30 10:10:39 -05:00
|
|
|
.add_resource(SnakeMoveTimer(Timer::new(Duration::from_millis(150), true)))
|
|
|
|
.add_resource(SnakeSegments::default())
|
2020-11-04 10:09:02 -05:00
|
|
|
.add_startup_system(setup.system())
|
2020-12-24 13:41:55 -05:00
|
|
|
.add_startup_stage("game_setup", SystemStage::single(spawn_snake.system()))
|
2020-12-24 13:45:07 -05:00
|
|
|
.add_system(snake_movement.system())
|
2020-12-24 14:01:05 -05:00
|
|
|
.add_system(position_translation.system())
|
|
|
|
.add_system(size_scaling.system())
|
2020-12-24 14:14:18 -05:00
|
|
|
.add_system(food_spawner.system())
|
2020-12-30 09:52:14 -05:00
|
|
|
.add_system(snake_timer.system())
|
2020-12-24 13:34:47 -05:00
|
|
|
.add_plugins(DefaultPlugins)
|
2020-11-04 10:09:02 -05:00
|
|
|
.run();
|
|
|
|
}
|