diff --git a/snake-game/src/gameplay.rs b/snake-game/src/gameplay.rs index 69aacf8..2991d7f 100644 --- a/snake-game/src/gameplay.rs +++ b/snake-game/src/gameplay.rs @@ -2,6 +2,9 @@ use bevy::prelude::*; use crate::domain::{Direction, GRID_SIZE, GridBarier, Head, Inert, SnakePath, Velocity}; +mod tools; +use tools::*; + /// Система поворота головы змеи pub fn head_rotation( input: Res>, @@ -49,103 +52,3 @@ pub fn moving( } } } - -/// Разбиение изменения координаты по границам сетки -fn split_by_grid(src: f32, dst: f32, grid: f32) -> Vec<(f32, f32)> { - if grid <= 0.0 { - return vec![(dst, dst - src)]; - }; - - let old_grid = src / grid; - let new_grid = dst / grid; - - let old_grid_right = old_grid.ceil(); - let new_grid_right = new_grid.ceil(); - - let mut output = Vec::with_capacity(2); - if src < dst { - let mut src: f32 = src; - for step in old_grid_right as i32..new_grid_right as i32 { - let step = step as f32 * grid; - if step != src { - output.push((step, step - src)); - } - src = step; - } - output.push((dst, dst - src)); - } else { - let mut dst: f32 = dst; - for step in new_grid_right as i32..old_grid_right as i32 { - let step = step as f32 * grid; - if step != dst { - output.push((dst, dst - step)); - } - dst = step; - } - output.push((dst, dst - src)); - output.reverse(); - } - output -} - -#[cfg(test)] -mod test { - use crate::gameplay::*; - - #[test] - fn test_split_by_grid() { - // движение вправо - assert_eq!(split_by_grid(1.0, 4.0, 5.0), vec![(4.0, 3.0)]); - assert_eq!(split_by_grid(3.0, 5.0, 5.0), vec![(5.0, 2.0)]); - assert_eq!(split_by_grid(3.0, 6.0, 5.0), vec![(5.0, 2.0), (6.0, 1.0)]); - assert_eq!(split_by_grid(3.0, 10.0, 5.0), vec![(5.0, 2.0), (10.0, 5.0)]); - assert_eq!( - split_by_grid(3.0, 11.0, 5.0), - vec![(5.0, 2.0), (10.0, 5.0), (11.0, 1.0)] - ); - assert_eq!(split_by_grid(-2.0, 3.0, 5.0), vec![(0.0, 2.0), (3.0, 3.0)]); - assert_eq!( - split_by_grid(-6.0, 7.0, 5.0), - vec![(-5.0, 1.0), (0.0, 5.0), (5.0, 5.0), (7.0, 2.0)] - ); - assert_eq!( - split_by_grid(-6.0, -2.0, 5.0), - vec![(-5.0, 1.0), (-2.0, 3.0)] - ); - - // движение влево - assert_eq!(split_by_grid(4.0, 1.0, 5.0), vec![(1.0, -3.0)]); - assert_eq!(split_by_grid(5.0, 3.0, 5.0), vec![(3.0, -2.0)]); - assert_eq!(split_by_grid(6.0, 3.0, 5.0), vec![(5.0, -1.0), (3.0, -2.0)]); - assert_eq!( - split_by_grid(10.0, 3.0, 5.0), - vec![(5.0, -5.0), (3.0, -2.0)] - ); - assert_eq!( - split_by_grid(11.0, 3.0, 5.0), - vec![(10.0, -1.0), (5.0, -5.0), (3.0, -2.0)] - ); - assert_eq!( - split_by_grid(3.0, -2.0, 5.0), - vec![(0.0, -3.0), (-2.0, -2.0)] - ); - assert_eq!( - split_by_grid(7.0, -6.0, 5.0), - vec![(5.0, -2.0), (0.0, -5.0), (-5.0, -5.0), (-6.0, -1.0)] - ); - assert_eq!( - split_by_grid(-2.0, -6.0, 5.0), - vec![(-5.0, -3.0), (-6.0, -1.0)] - ); - - // сценарии с нулевой сеткой - assert_eq!(split_by_grid(1.0, 4.0, 0.0), vec![(4.0, 3.0)]); - assert_eq!(split_by_grid(4.0, 1.0, 0.0), vec![(1.0, -3.0)]); - - // Сценарии со стартом в узле сетки - assert_eq!(split_by_grid(0.0, 1.5, 5.0), vec![(1.5, 1.5)]); - assert_eq!(split_by_grid(5.0, 7.0, 5.0), vec![(7.0, 2.0)]); - assert_eq!(split_by_grid(0.0, -1.5, 5.0), vec![(-1.5, -1.5)]); - assert_eq!(split_by_grid(5.0, 3.0, 5.0), vec![(3.0, -2.0)]); - } -} diff --git a/snake-game/src/gameplay/tools.rs b/snake-game/src/gameplay/tools.rs new file mode 100644 index 0000000..9231b2d --- /dev/null +++ b/snake-game/src/gameplay/tools.rs @@ -0,0 +1,99 @@ +/// Разбиение изменения координаты по границам сетки +pub fn split_by_grid(src: f32, dst: f32, grid: f32) -> Vec<(f32, f32)> { + if grid <= 0.0 { + return vec![(dst, dst - src)]; + }; + + let old_grid = src / grid; + let new_grid = dst / grid; + + let old_grid_right = old_grid.ceil(); + let new_grid_right = new_grid.ceil(); + + let mut output = Vec::with_capacity(2); + if src < dst { + let mut src: f32 = src; + for step in old_grid_right as i32..new_grid_right as i32 { + let step = step as f32 * grid; + if step != src { + output.push((step, step - src)); + } + src = step; + } + output.push((dst, dst - src)); + } else { + let mut dst: f32 = dst; + for step in new_grid_right as i32..old_grid_right as i32 { + let step = step as f32 * grid; + if step != dst { + output.push((dst, dst - step)); + } + dst = step; + } + output.push((dst, dst - src)); + output.reverse(); + } + output +} + +#[cfg(test)] +mod test { + use crate::gameplay::*; + + #[test] + fn test_split_by_grid() { + // движение вправо + assert_eq!(split_by_grid(1.0, 4.0, 5.0), vec![(4.0, 3.0)]); + assert_eq!(split_by_grid(3.0, 5.0, 5.0), vec![(5.0, 2.0)]); + assert_eq!(split_by_grid(3.0, 6.0, 5.0), vec![(5.0, 2.0), (6.0, 1.0)]); + assert_eq!(split_by_grid(3.0, 10.0, 5.0), vec![(5.0, 2.0), (10.0, 5.0)]); + assert_eq!( + split_by_grid(3.0, 11.0, 5.0), + vec![(5.0, 2.0), (10.0, 5.0), (11.0, 1.0)] + ); + assert_eq!(split_by_grid(-2.0, 3.0, 5.0), vec![(0.0, 2.0), (3.0, 3.0)]); + assert_eq!( + split_by_grid(-6.0, 7.0, 5.0), + vec![(-5.0, 1.0), (0.0, 5.0), (5.0, 5.0), (7.0, 2.0)] + ); + assert_eq!( + split_by_grid(-6.0, -2.0, 5.0), + vec![(-5.0, 1.0), (-2.0, 3.0)] + ); + + // движение влево + assert_eq!(split_by_grid(4.0, 1.0, 5.0), vec![(1.0, -3.0)]); + assert_eq!(split_by_grid(5.0, 3.0, 5.0), vec![(3.0, -2.0)]); + assert_eq!(split_by_grid(6.0, 3.0, 5.0), vec![(5.0, -1.0), (3.0, -2.0)]); + assert_eq!( + split_by_grid(10.0, 3.0, 5.0), + vec![(5.0, -5.0), (3.0, -2.0)] + ); + assert_eq!( + split_by_grid(11.0, 3.0, 5.0), + vec![(10.0, -1.0), (5.0, -5.0), (3.0, -2.0)] + ); + assert_eq!( + split_by_grid(3.0, -2.0, 5.0), + vec![(0.0, -3.0), (-2.0, -2.0)] + ); + assert_eq!( + split_by_grid(7.0, -6.0, 5.0), + vec![(5.0, -2.0), (0.0, -5.0), (-5.0, -5.0), (-6.0, -1.0)] + ); + assert_eq!( + split_by_grid(-2.0, -6.0, 5.0), + vec![(-5.0, -3.0), (-6.0, -1.0)] + ); + + // сценарии с нулевой сеткой + assert_eq!(split_by_grid(1.0, 4.0, 0.0), vec![(4.0, 3.0)]); + assert_eq!(split_by_grid(4.0, 1.0, 0.0), vec![(1.0, -3.0)]); + + // Сценарии со стартом в узле сетки + assert_eq!(split_by_grid(0.0, 1.5, 5.0), vec![(1.5, 1.5)]); + assert_eq!(split_by_grid(5.0, 7.0, 5.0), vec![(7.0, 2.0)]); + assert_eq!(split_by_grid(0.0, -1.5, 5.0), vec![(-1.5, -1.5)]); + assert_eq!(split_by_grid(5.0, 3.0, 5.0), vec![(3.0, -2.0)]); + } +} diff --git a/snake-game/src/startup.rs b/snake-game/src/startup.rs index 3973af4..5177b1b 100644 --- a/snake-game/src/startup.rs +++ b/snake-game/src/startup.rs @@ -37,7 +37,7 @@ fn spawn_snake(commands: &mut Commands) { vec2(HEAD_SIZE as f32, HEAD_SIZE as f32), ), Transform { - translation: vec3(0.0, 0.0, 1.0), + translation: vec3(0.0, 0.0, 0.0), ..Default::default() }, )) @@ -48,7 +48,7 @@ fn spawn_snake(commands: &mut Commands) { vec2(HEAD_SIZE as f32 * 0.3, HEAD_SIZE as f32 * 0.3), ), Transform { - translation: vec3(HEAD_SIZE as f32 * -0.25, HEAD_SIZE as f32 * 0.25, 0.0), + translation: vec3(HEAD_SIZE as f32 * -0.25, HEAD_SIZE as f32 * 0.25, 1.0), ..Default::default() }, ChildOf(head), @@ -59,7 +59,7 @@ fn spawn_snake(commands: &mut Commands) { vec2(HEAD_SIZE as f32 * 0.2, HEAD_SIZE as f32 * 0.2), ), Transform { - translation: vec3(HEAD_SIZE as f32 * 0.25, HEAD_SIZE as f32 * 0.25, 0.0), + translation: vec3(HEAD_SIZE as f32 * 0.25, HEAD_SIZE as f32 * 0.25, 1.0), ..Default::default() }, ChildOf(head), @@ -93,6 +93,25 @@ fn spawn_snake(commands: &mut Commands) { commands.entity(event.0).remove::(); }, ); + + commands + .entity(tail) + // + .observe( + |event: On, + mut query: Query<(&mut Direction, &Transform)>, + snake_path: Res| { + trace!("first observer working"); + if let Ok((mut direction, transform)) = query.get_mut(event.0) { + if let Some(new_direction) = snake_path.pick(transform) { + *direction = new_direction; + } + } + }, + ) + .observe(|event: On| { + trace!("second observer working"); + }); } /// Создать координатную сетку