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!("off_head_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)); } } }