diff --git a/snake-game/src/debug.rs b/snake-game/src/debug.rs index c20cee0..958aa43 100644 --- a/snake-game/src/debug.rs +++ b/snake-game/src/debug.rs @@ -21,32 +21,6 @@ pub fn print_all_entities_by_press_enter(input: Res>, mut c } } -/// регулировка скорости игры -pub fn time_speed(input: Res>, mut time: ResMut>) { - if input.just_pressed(KeyCode::Equal) { - let mut speed = time.relative_speed(); - speed *= 2.0; - time.set_relative_speed(speed); - trace!("time speed set x2.0"); - } else if input.just_pressed(KeyCode::Minus) { - let mut speed = time.relative_speed(); - speed *= 0.5; - time.set_relative_speed(speed); - trace!("time speed set x0.5"); - } else if input.just_pressed(KeyCode::Backspace) { - time.set_relative_speed(1.0); - trace!("time speed reset"); - } else if input.just_pressed(KeyCode::Space) { - if time.is_paused() { - time.unpause(); - trace!("unpause game"); - } else { - time.pause(); - trace!("pause game"); - } - } -} - /// Замедлить время и поставить на паузу pub fn set_initial_time_speed_to_slow_and_pause(mut time: ResMut>) { time.set_relative_speed(1.0 / 8.0); diff --git a/snake-game/src/domain.rs b/snake-game/src/domain.rs index c2e53af..42ae89a 100644 --- a/snake-game/src/domain.rs +++ b/snake-game/src/domain.rs @@ -12,9 +12,9 @@ pub use snake_path::SnakePath; mod position; pub use position::Position; -pub const GRID_SIZE: u8 = 48; -pub const HEAD_SIZE: u8 = (GRID_SIZE as f32 * 1.1) as u8; -pub const SNAKE_VELOCITY: u8 = (GRID_SIZE as f32 / 1.0) as u8; +pub const GRID_SIZE: u8 = 32; +pub const HEAD_SIZE: u8 = (GRID_SIZE as f32 * 1.1).ceil() as u8; +pub const SNAKE_VELOCITY: u8 = (GRID_SIZE as f32 / 0.33).ceil() as u8; pub const FIELD_SIZE: u8 = 20; /// Голова змеи diff --git a/snake-game/src/fixed_update.rs b/snake-game/src/fixed_update.rs new file mode 100644 index 0000000..202434f --- /dev/null +++ b/snake-game/src/fixed_update.rs @@ -0,0 +1,74 @@ +use bevy::prelude::*; + +use crate::{domain::*, tools::split_by_grid}; + +/// Общая механика движения +fn moving( + velocity: &f32, + time: &Time, + direction: &mut Direction, + mut transform: &mut Transform, + entity: Entity, + commands: &mut Commands, + switch_direction: impl FnOnce(Position) -> Option, +) { + // первая часть движения - до границы сетки + let delta = velocity * time.delta_secs(); + let src = direction.get_coord(&transform); + let sign = direction.get_change_sign(); + let dst = src + sign * delta; + let mut splited = split_by_grid(src, dst, GRID_SIZE as f32).into_iter(); + let first = splited.next().unwrap_or((0.0, 0.0)); + direction.set_coord(&mut transform, first.0); + + // вторая часть движения - после границы сетки + if let Some(second) = splited.next() { + let delta = second.1.abs(); + let pos = Position::from(&*transform); + if let Some(dir) = switch_direction(pos) { + trace!("set new direction for {entity} in {pos} --> {dir}"); + *direction = dir; + } + let src = direction.get_coord(&*transform); + let sign = direction.get_change_sign(); + let dst = src + sign * delta; + direction.set_coord(&mut transform, dst); + + // создать событие прохождения границы сетки + commands.entity(entity).trigger(|e| GridBarier::new(e, pos, (&*direction).clone())); + } +} + +/// Система движения головы змеи +pub fn head_moving( + query: Query<(&mut Transform, &mut Direction, &Velocity, &Head, Entity)>, + time: Res>, + mut snake_path: ResMut, + mut commands: Commands, +) { + let _span = trace_span!("head_moving").entered(); + for (mut transform, mut direction, Velocity(velocity), head, entity) in query.into_iter() { + let next_dir = head.0.clone(); + let snake_path = &mut snake_path; + let switch_direction = move |pos: Position| { + snake_path.insert(pos.clone(), next_dir.clone()); + Some(next_dir) + }; + moving(velocity, &time, &mut direction, &mut transform, entity, &mut commands, switch_direction); + } +} + +/// Система движения тела змеи +pub fn snake_moving( + query: Query<(&mut Transform, &mut Direction, &Velocity, Entity), (With, Without)>, + time: Res>, + snake_path: Res, + mut commands: Commands, +) { + let _span = trace_span!("snake_moving").entered(); + for (mut transform, mut direction, Velocity(velocity), entity) in query.into_iter() { + let snake_path = &snake_path; + let switch_direction = move |pos: Position| snake_path.get(&pos).cloned(); + moving(velocity, &time, &mut direction, &mut transform, entity, &mut commands, switch_direction); + } +} diff --git a/snake-game/src/game.rs b/snake-game/src/game.rs index cd70f4a..0e81fb6 100644 --- a/snake-game/src/game.rs +++ b/snake-game/src/game.rs @@ -22,21 +22,21 @@ impl Plugin for SnakeGame { app.add_systems(Startup, crate::startup::setup_time); app.add_systems(Startup, crate::startup::startup); - app.add_systems(Update, crate::gameplay::head_rotation); + app.add_systems(Update, crate::update::head_rotation); + app.add_systems(Update, crate::update::time_speed); - app.add_systems(PostUpdate, crate::gameplay::check_bounds_colisions); - app.add_systems(PostUpdate, crate::gameplay::check_tail_colisions); - app.add_systems(PostUpdate, crate::gameplay::check_meal_collisions); + app.add_systems(PostUpdate, crate::post_update::check_bounds_colisions); + app.add_systems(PostUpdate, crate::post_update::check_tail_colisions); + app.add_systems(PostUpdate, crate::post_update::check_meal_collisions); - app.add_systems(FixedUpdate, crate::gameplay::head_moving); - app.add_systems(FixedUpdate, crate::gameplay::snake_moving); + app.add_systems(FixedUpdate, crate::fixed_update::head_moving); + app.add_systems(FixedUpdate, crate::fixed_update::snake_moving); // DEBUG: - // app.add_systems(PostStartup, crate::debug::print_all_entities); app.add_systems(Startup, crate::debug::set_initial_time_speed_to_slow_and_pause); + // app.add_systems(PostStartup, crate::debug::print_all_entities); app.add_systems(Update, crate::debug::grow_up_on_tab); - app.add_systems(Update, crate::debug::print_all_entities_by_press_enter); - app.add_systems(Update, crate::debug::time_speed); + // app.add_systems(Update, crate::debug::print_all_entities_by_press_enter); } } diff --git a/snake-game/src/gameplay.rs b/snake-game/src/gameplay.rs deleted file mode 100644 index fe41740..0000000 --- a/snake-game/src/gameplay.rs +++ /dev/null @@ -1,157 +0,0 @@ -use bevy::prelude::*; - -use crate::{ - domain::{Collision, Direction, FIELD_SIZE, GRID_SIZE, GridBarier, Head, Meal, Position, SnakePath, Tail, Velocity}, - tools::split_by_grid, -}; - -/// Система поворота головы змеи -pub fn head_rotation( - input: Res>, // - query: Single<(&mut Transform, &mut Head, &Direction)>, -) { - let _span = trace_span!("head_rotation").entered(); - if input.just_pressed(KeyCode::ArrowLeft) { - let (mut transform, mut head, direction) = query.into_inner(); - let translation = transform.translation.clone(); - let cur_pos = Position::from(&*transform); - let mut new_direction = direction.clone(); - new_direction.rotate_left(); - head.0 = new_direction; - transform.rotation = new_direction.orientation(); - trace!("running {direction} at {cur_pos} {translation}: go {new_direction}"); - } else if input.just_pressed(KeyCode::ArrowRight) { - let (mut transform, mut head, direction) = query.into_inner(); - let translation = transform.translation.clone(); - let cur_pos = Position::from(&*transform); - let mut new_direction = direction.clone(); - new_direction.rotate_right(); - head.0 = new_direction; - transform.rotation = new_direction.orientation(); - trace!("running {direction} at {cur_pos} {translation}: go {new_direction}"); - } else if input.just_pressed(KeyCode::ArrowUp) { - let (mut transform, mut head, direction) = query.into_inner(); - let translation = transform.translation.clone(); - let cur_pos = Position::from(&*transform); - head.0 = direction.clone(); - transform.rotation = direction.orientation(); - trace!("running {direction} at {cur_pos} {translation}: go {direction}"); - } -} - -/// Общая механика движения -fn moving( - velocity: &f32, - time: &Time, - direction: &mut Direction, - mut transform: &mut Transform, - entity: Entity, - commands: &mut Commands, - switch_direction: impl FnOnce(Position) -> Option, -) { - // первая часть движения - до границы сетки - let delta = velocity * time.delta_secs(); - let src = direction.get_coord(&transform); - let sign = direction.get_change_sign(); - let dst = src + sign * delta; - let mut splited = split_by_grid(src, dst, GRID_SIZE as f32).into_iter(); - let first = splited.next().unwrap_or((0.0, 0.0)); - direction.set_coord(&mut transform, first.0); - - // вторая часть движения - после границы сетки - if let Some(second) = splited.next() { - let delta = second.1.abs(); - let pos = Position::from(&*transform); - if let Some(dir) = switch_direction(pos) { - trace!("set new direction for {entity} in {pos} --> {dir}"); - *direction = dir; - } - let src = direction.get_coord(&*transform); - let sign = direction.get_change_sign(); - let dst = src + sign * delta; - direction.set_coord(&mut transform, dst); - - // создать событие прохождения границы сетки - commands.entity(entity).trigger(|e| GridBarier::new(e, pos, (&*direction).clone())); - } -} - -/// Система движения головы змеи -pub fn head_moving( - query: Query<(&mut Transform, &mut Direction, &Velocity, &Head, Entity)>, - time: Res>, - mut snake_path: ResMut, - mut commands: Commands, -) { - let _span = trace_span!("head_moving").entered(); - for (mut transform, mut direction, Velocity(velocity), head, entity) in query.into_iter() { - let next_dir = head.0.clone(); - let snake_path = &mut snake_path; - let switch_direction = move |pos: Position| { - snake_path.insert(pos.clone(), next_dir.clone()); - Some(next_dir) - }; - moving(velocity, &time, &mut direction, &mut transform, entity, &mut commands, switch_direction); - } -} - -/// Система движения тела змеи -pub fn snake_moving( - query: Query<(&mut Transform, &mut Direction, &Velocity, Entity), (With, Without)>, - time: Res>, - snake_path: Res, - mut commands: Commands, -) { - let _span = trace_span!("snake_moving").entered(); - for (mut transform, mut direction, Velocity(velocity), entity) in query.into_iter() { - let snake_path = &snake_path; - let switch_direction = move |pos: Position| snake_path.get(&pos).cloned(); - moving(velocity, &time, &mut direction, &mut transform, entity, &mut commands, switch_direction); - } -} - -/// Система отслеживания столкновений с границей -pub fn check_bounds_colisions( - head: Single<(&Transform,), (With,)>, - mut commands: Commands, // -) { - let (head_transform,) = head.into_inner(); - let (x, y) = (head_transform.translation.x, head_transform.translation.y); - const SIDE: f32 = (FIELD_SIZE as f32 / 2.0) * GRID_SIZE as f32; - if x < -SIDE || x > SIDE || y < -SIDE || y > SIDE { - commands.trigger(Collision::Bounds); - }; -} - -/// Система отслеживания столкновений c хвостом -pub fn check_tail_colisions( - head: Single<(&Transform, Entity), (With,)>, - query: Query<(&Transform, &Velocity, &Tail)>, - mut commands: Commands, // -) { - let (head_transform, head_entity) = head.into_inner(); - for (tail_transform, velocity, tail) in query.into_iter() { - if velocity.0 == 0.0 || tail.0 == head_entity { - continue; - } - let distance = head_transform.translation.distance(tail_transform.translation); - if distance < GRID_SIZE as f32 { - commands.trigger(Collision::Tail); - } - } -} - -/// Система отслеживания столкновений с едой -pub fn check_meal_collisions( - head: Single<(&Transform,), (With,)>, - query: Query<(&Transform, Entity), (With,)>, - mut commands: Commands, // -) { - let (head_transform,) = head.into_inner(); - for (meal_transform, meal_entity) in query.into_iter() { - let distance = head_transform.translation.distance(meal_transform.translation); - if distance < GRID_SIZE as f32 { - commands.trigger(Collision::Meal(meal_entity)); - } - } -} diff --git a/snake-game/src/lib.rs b/snake-game/src/lib.rs index 253fb20..d82f921 100644 --- a/snake-game/src/lib.rs +++ b/snake-game/src/lib.rs @@ -1,10 +1,12 @@ mod core; mod debug; mod domain; +mod fixed_update; mod game; -mod gameplay; mod observers; +mod post_update; mod startup; mod tools; +mod update; pub use game::SnakeGame; diff --git a/snake-game/src/post_update.rs b/snake-game/src/post_update.rs new file mode 100644 index 0000000..d56c4e2 --- /dev/null +++ b/snake-game/src/post_update.rs @@ -0,0 +1,49 @@ +use bevy::prelude::*; + +use crate::domain::*; + +/// Система отслеживания столкновений с границей +pub fn check_bounds_colisions( + head: Single<(&Transform,), (With,)>, + mut commands: Commands, // +) { + let (head_transform,) = head.into_inner(); + let (x, y) = (head_transform.translation.x, head_transform.translation.y); + const SIDE: f32 = (FIELD_SIZE as f32 / 2.0) * GRID_SIZE as f32; + if x < -SIDE || x > SIDE || y < -SIDE || y > SIDE { + commands.trigger(Collision::Bounds); + }; +} + +/// Система отслеживания столкновений c хвостом +pub fn check_tail_colisions( + head: Single<(&Transform, Entity), (With,)>, + query: Query<(&Transform, &Velocity, &Tail)>, + mut commands: Commands, // +) { + let (head_transform, head_entity) = head.into_inner(); + for (tail_transform, velocity, tail) in query.into_iter() { + if velocity.0 == 0.0 || tail.0 == head_entity { + continue; + } + let distance = head_transform.translation.distance(tail_transform.translation); + if distance < GRID_SIZE as f32 { + commands.trigger(Collision::Tail); + } + } +} + +/// Система отслеживания столкновений с едой +pub fn check_meal_collisions( + head: Single<(&Transform,), (With,)>, + query: Query<(&Transform, Entity), (With,)>, + mut commands: Commands, // +) { + let (head_transform,) = head.into_inner(); + for (meal_transform, meal_entity) in query.into_iter() { + let distance = head_transform.translation.distance(meal_transform.translation); + if distance < GRID_SIZE as f32 { + commands.trigger(Collision::Meal(meal_entity)); + } + } +} diff --git a/snake-game/src/startup.rs b/snake-game/src/startup.rs index f27d5bf..e519891 100644 --- a/snake-game/src/startup.rs +++ b/snake-game/src/startup.rs @@ -31,8 +31,8 @@ pub fn startup(mut commands: Commands) { // DEBUG: - commands.spawn(Observer::new(crate::debug::trace_collisions)); - commands.spawn(Observer::new(crate::debug::trace_snake_moving)); + // commands.spawn(Observer::new(crate::debug::trace_collisions)); + // commands.spawn(Observer::new(crate::debug::trace_snake_moving)); crate::debug::spawn_grid(&mut commands); crate::debug::spawn_some_meal(&mut commands); diff --git a/snake-game/src/update.rs b/snake-game/src/update.rs new file mode 100644 index 0000000..957455f --- /dev/null +++ b/snake-game/src/update.rs @@ -0,0 +1,53 @@ +use bevy::prelude::*; + +use crate::domain::{Direction, Head}; + +/// Система поворота головы змеи +pub fn head_rotation( + input: Res>, // + query: Single<(&mut Transform, &mut Head, &Direction)>, +) { + if input.just_pressed(KeyCode::ArrowLeft) { + let (mut transform, mut head, direction) = query.into_inner(); + let mut new_direction = direction.clone(); + new_direction.rotate_left(); + head.0 = new_direction; + transform.rotation = new_direction.orientation(); + } else if input.just_pressed(KeyCode::ArrowRight) { + let (mut transform, mut head, direction) = query.into_inner(); + let mut new_direction = direction.clone(); + new_direction.rotate_right(); + head.0 = new_direction; + transform.rotation = new_direction.orientation(); + } else if input.just_pressed(KeyCode::ArrowUp) { + let (mut transform, mut head, direction) = query.into_inner(); + head.0 = direction.clone(); + transform.rotation = direction.orientation(); + } +} + +/// Регулировка скорости игры +pub fn time_speed(input: Res>, mut time: ResMut>) { + if input.just_pressed(KeyCode::Equal) { + let mut speed = time.relative_speed(); + speed *= 2.0; + time.set_relative_speed(speed); + trace!("time speed set x2.0: {}", time.relative_speed()); + } else if input.just_pressed(KeyCode::Minus) { + let mut speed = time.relative_speed(); + speed *= 0.5; + time.set_relative_speed(speed); + trace!("time speed set x0.5: {}", time.relative_speed()); + } else if input.just_pressed(KeyCode::Backspace) { + time.set_relative_speed(1.0); + trace!("time speed reset: {}", time.relative_speed()); + } else if input.just_pressed(KeyCode::Space) { + if time.is_paused() { + time.unpause(); + trace!("unpause game"); + } else { + time.pause(); + trace!("pause game"); + } + } +}