Files
rust-otus/snake-game/src/gameplay.rs
T

152 lines
5.2 KiB
Rust

use bevy::prelude::*;
use crate::domain::{Direction, GRID_SIZE, GridBarier, Head, Inert, SnakePath, Velocity};
/// Система поворота головы змеи
pub fn head_rotation(
input: Res<ButtonInput<KeyCode>>,
query: Single<(&mut Transform, &mut Direction), With<Head>>,
mut snake_path: ResMut<SnakePath>,
) {
use core::f32::consts::PI;
let (mut transform, mut direction) = query.into_inner();
if input.just_pressed(KeyCode::ArrowLeft) {
transform.rotate_z(PI / 2.0);
direction.rotate_left();
snake_path.push(&transform, direction.clone());
}
if input.just_pressed(KeyCode::ArrowRight) {
transform.rotate_z(-PI / 2.0);
direction.rotate_right();
snake_path.push(&transform, direction.clone());
}
}
/// Системе движения для всех движущихся сущностей
pub fn moving(
time: Res<Time>,
query: Query<(&mut Transform, &mut Inert, &Direction, &Velocity, Entity)>,
mut commands: Commands,
) {
for (mut transform, mut inert, direction, Velocity(velocity), entity) in query.into_iter() {
let delta = velocity * time.delta_secs();
let src = inert.0.get_coord(&transform);
let sign = inert.0.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));
inert.0.set_coord(&mut transform, first.0);
if let Some(second) = splited.next() {
let delta = second.1.abs();
inert.0 = *direction;
let src = inert.0.get_coord(&transform);
let sign = inert.0.get_change_sign();
let dst = src + sign * delta;
inert.0.set_coord(&mut transform, dst);
commands.trigger(GridBarier(entity));
}
}
}
/// Разбиение изменения координаты по границам сетки
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)]);
}
}