|
@@ -5,76 +5,9 @@ use bevy::prelude::*;
|
|
|
use bevy::render::pass::ClearColor;
|
|
|
use rand::prelude::random;
|
|
|
|
|
|
-const ARENA_WIDTH: u32 = 10;
|
|
|
-const ARENA_HEIGHT: u32 = 10;
|
|
|
-
|
|
|
-struct SnakeHead {
|
|
|
- direction: Direction,
|
|
|
-}
|
|
|
-
|
|
|
-struct Food;
|
|
|
-
|
|
|
-struct GrowthEvent;
|
|
|
-
|
|
|
-#[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) -> Size {
|
|
|
- Self {
|
|
|
- width: x,
|
|
|
- height: x,
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-struct Materials {
|
|
|
- head_material: Handle<ColorMaterial>,
|
|
|
- segment_material: Handle<ColorMaterial>,
|
|
|
- food_material: Handle<ColorMaterial>,
|
|
|
-}
|
|
|
-
|
|
|
-#[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,
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#[derive(SystemLabel, Debug, Hash, PartialEq, Eq, Clone)]
|
|
|
-pub enum SnakeMovement {
|
|
|
- Input,
|
|
|
- Movement,
|
|
|
- Eating,
|
|
|
- Growth,
|
|
|
-}
|
|
|
-
|
|
|
-struct SnakeSegment;
|
|
|
-
|
|
|
-#[derive(Default)]
|
|
|
-struct SnakeSegments {
|
|
|
- segments: Vec<Entity>,
|
|
|
-}
|
|
|
+pub mod components;
|
|
|
+use crate::components as c;
|
|
|
+pub mod snake;
|
|
|
|
|
|
fn main() {
|
|
|
App::build()
|
|
@@ -85,25 +18,25 @@ fn main() {
|
|
|
..Default::default()
|
|
|
})
|
|
|
.insert_resource(ClearColor(Color::rgb(0.04, 0.04, 0.04)))
|
|
|
- .insert_resource(SnakeSegments::default())
|
|
|
+ .insert_resource(c::SnakeSegments::default())
|
|
|
.add_startup_system(setup.system())
|
|
|
- .add_startup_stage("game_setup", SystemStage::single(spawn_snake.system()))
|
|
|
+ .add_startup_stage("game_setup", SystemStage::single(snake::spawn.system()))
|
|
|
.add_plugins(DefaultPlugins)
|
|
|
.add_system(
|
|
|
- snake_movement_input
|
|
|
+ snake::input
|
|
|
.system()
|
|
|
- .label(SnakeMovement::Input)
|
|
|
- .before(SnakeMovement::Movement),
|
|
|
+ .label(c::SnakeMovement::Input)
|
|
|
+ .before(c::SnakeMovement::Movement),
|
|
|
)
|
|
|
.add_system_set(
|
|
|
SystemSet::new()
|
|
|
.with_run_criteria(FixedTimestep::step(0.3))
|
|
|
- .with_system(snake_movement.system().label(SnakeMovement::Movement))
|
|
|
+ .with_system(snake::movement.system().label(c::SnakeMovement::Movement))
|
|
|
.with_system(
|
|
|
- snake_eating
|
|
|
+ snake::eating
|
|
|
.system()
|
|
|
- .label(SnakeMovement::Eating)
|
|
|
- .after(SnakeMovement::Movement),
|
|
|
+ .label(c::SnakeMovement::Eating)
|
|
|
+ .after(c::SnakeMovement::Movement),
|
|
|
),
|
|
|
)
|
|
|
.add_system_set_to_stage(
|
|
@@ -117,148 +50,44 @@ fn main() {
|
|
|
.with_run_criteria(FixedTimestep::step(0.9))
|
|
|
.with_system(food_spawner.system()),
|
|
|
)
|
|
|
- .add_event::<GrowthEvent>()
|
|
|
+ .add_event::<c::GrowthEvent>()
|
|
|
.run();
|
|
|
}
|
|
|
|
|
|
fn setup(mut commands: Commands, mut materials: ResMut<Assets<ColorMaterial>>) {
|
|
|
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
|
|
|
- commands.insert_resource(Materials {
|
|
|
+ commands.insert_resource(c::Materials {
|
|
|
head_material: materials.add(Color::rgb(0.7, 0.7, 0.7).into()),
|
|
|
segment_material: materials.add(Color::rgb(0.3, 0.3, 0.3).into()),
|
|
|
food_material: materials.add(Color::rgb(1.0, 0.0, 1.0).into()),
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-fn food_spawner(mut commands: Commands, materials: Res<Materials>) {
|
|
|
+fn food_spawner(mut commands: Commands, materials: Res<c::Materials>) {
|
|
|
commands
|
|
|
.spawn_bundle(SpriteBundle {
|
|
|
material: materials.food_material.clone(),
|
|
|
..Default::default()
|
|
|
})
|
|
|
- .insert(Food)
|
|
|
- .insert(Position {
|
|
|
- x: (random::<f32>() * ARENA_WIDTH as f32) as i32,
|
|
|
- y: (random::<f32>() * ARENA_HEIGHT as f32) as i32,
|
|
|
- })
|
|
|
- .insert(Size::square(0.8));
|
|
|
-}
|
|
|
-
|
|
|
-fn spawn_segment(
|
|
|
- mut commands: Commands,
|
|
|
- material: &Handle<ColorMaterial>,
|
|
|
- position: Position,
|
|
|
-) -> Entity {
|
|
|
- commands
|
|
|
- .spawn_bundle(SpriteBundle {
|
|
|
- material: material.clone(),
|
|
|
- ..Default::default()
|
|
|
+ .insert(c::Food)
|
|
|
+ .insert(c::Position {
|
|
|
+ x: (random::<f32>() * c::ARENA_WIDTH as f32) as i32,
|
|
|
+ y: (random::<f32>() * c::ARENA_HEIGHT as f32) as i32,
|
|
|
})
|
|
|
- .insert(SnakeSegment)
|
|
|
- .insert(position)
|
|
|
- .insert(Size::square(0.65))
|
|
|
- .id()
|
|
|
-}
|
|
|
-
|
|
|
-fn spawn_snake(
|
|
|
- mut commands: Commands,
|
|
|
- materials: Res<Materials>,
|
|
|
- mut segments: ResMut<SnakeSegments>,
|
|
|
-) {
|
|
|
- segments.segments = vec![
|
|
|
- commands
|
|
|
- .spawn_bundle(SpriteBundle {
|
|
|
- material: materials.head_material.clone(),
|
|
|
- sprite: Sprite::new(Vec2::new(10.0, 10.0)),
|
|
|
- ..Default::default()
|
|
|
- })
|
|
|
- .insert(SnakeHead {
|
|
|
- direction: Direction::Up,
|
|
|
- })
|
|
|
- .insert(Position { x: 3, y: 3 })
|
|
|
- .insert(Size::square(0.8))
|
|
|
- .id(),
|
|
|
- spawn_segment(
|
|
|
- commands,
|
|
|
- &materials.segment_material,
|
|
|
- Position { x: 3, y: 2 },
|
|
|
- ),
|
|
|
- ]
|
|
|
-}
|
|
|
-
|
|
|
-fn snake_movement_input(keyboard_input: Res<Input<KeyCode>>, mut heads: Query<&mut SnakeHead>) {
|
|
|
- if let Some(mut head) = heads.iter_mut().next() {
|
|
|
- let dir = 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;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-fn snake_movement(
|
|
|
- segments: ResMut<SnakeSegments>,
|
|
|
- mut heads: Query<(Entity, &SnakeHead)>,
|
|
|
- mut positions: Query<&mut Position>,
|
|
|
-) {
|
|
|
- if let Some((head_entity, head)) = heads.iter_mut().next() {
|
|
|
- let segment_positions = segments
|
|
|
- .segments
|
|
|
- .iter()
|
|
|
- .map(|e| *positions.get_mut(*e).unwrap())
|
|
|
- .collect::<Vec<Position>>();
|
|
|
- let mut head_pos = positions.get_mut(head_entity).unwrap();
|
|
|
- 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,
|
|
|
- }
|
|
|
- segment_positions
|
|
|
- .iter()
|
|
|
- .zip(segments.segments.iter().skip(1))
|
|
|
- .for_each(|(pos, segment)| {
|
|
|
- *positions.get_mut(*segment).unwrap() = *pos;
|
|
|
- });
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-fn snake_eating(
|
|
|
- mut commands: Commands,
|
|
|
- mut growth_writer: EventWriter<GrowthEvent>,
|
|
|
- food_positions: Query<(Entity, &Position), With<Food>>,
|
|
|
- head_positions: Query<&Position, With<SnakeHead>>,
|
|
|
-) {
|
|
|
- for head_pos in head_positions.iter() {
|
|
|
- for (ent, food_pos) in food_positions.iter() {
|
|
|
- if food_pos == head_pos {
|
|
|
- commands.entity(ent).despawn();
|
|
|
- growth_writer.send(GrowthEvent);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ .insert(c::GridSize::square(0.8));
|
|
|
}
|
|
|
|
|
|
-fn size_scaling(windows: Res<Windows>, mut q: Query<(&Size, &mut Sprite)>) {
|
|
|
+fn size_scaling(windows: Res<Windows>, mut q: Query<(&c::GridSize, &mut Sprite)>) {
|
|
|
let window = windows.get_primary().unwrap();
|
|
|
for (size, mut sprite) in q.iter_mut() {
|
|
|
sprite.size = Vec2::new(
|
|
|
- size.width / ARENA_WIDTH as f32 * window.width() as f32,
|
|
|
- size.height / ARENA_HEIGHT as f32 * window.height() as f32,
|
|
|
+ size.width / c::ARENA_WIDTH as f32 * window.width() as f32,
|
|
|
+ size.height / c::ARENA_HEIGHT as f32 * window.height() as f32,
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-fn position_translation(windows: Res<Windows>, mut q: Query<(&Position, &mut Transform)>) {
|
|
|
+fn position_translation(windows: Res<Windows>, mut q: Query<(&c::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.0) + (tile_size / 2.0)
|
|
@@ -267,8 +96,8 @@ fn position_translation(windows: Res<Windows>, mut q: Query<(&Position, &mut Tra
|
|
|
let window = windows.get_primary().unwrap();
|
|
|
for (pos, mut tf) in q.iter_mut() {
|
|
|
tf.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),
|
|
|
+ convert(pos.x as f32, window.width() as f32, c::ARENA_WIDTH as f32),
|
|
|
+ convert(pos.y as f32, window.height() as f32, c::ARENA_HEIGHT as f32),
|
|
|
0.0,
|
|
|
)
|
|
|
}
|