115 lines
4.2 KiB
Rust
115 lines
4.2 KiB
Rust
use std::collections::HashSet;
|
|
|
|
use bevy::{color::palettes::tailwind::*, prelude::*};
|
|
use rand::RngExt;
|
|
|
|
use crate::{domain::*, tools::split_by_grid};
|
|
|
|
/// Общая механика движения
|
|
fn moving(
|
|
velocity: &f32,
|
|
time: &Time<Fixed>,
|
|
direction: &mut Direction,
|
|
transform: &mut Transform,
|
|
entity: Entity,
|
|
commands: &mut Commands,
|
|
switch_direction: impl FnOnce(Position) -> Option<Direction>,
|
|
) {
|
|
// первая часть движения - до границы сетки
|
|
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(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(transform, dst);
|
|
|
|
// создать событие прохождения границы сетки
|
|
commands.entity(entity).trigger(|e| GridBarier::new(e, pos, *direction));
|
|
}
|
|
}
|
|
|
|
/// Система движения головы змеи
|
|
pub fn head_moving(
|
|
query: Query<(&mut Transform, &mut Direction, &Velocity, &Head, Entity)>,
|
|
time: Res<Time<Fixed>>,
|
|
mut snake_path: ResMut<SnakePath>,
|
|
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;
|
|
let dir = *direction;
|
|
let snake_path = &mut snake_path;
|
|
let switch_direction = move |pos: Position| {
|
|
if next_dir != dir {
|
|
snake_path.insert(pos, next_dir);
|
|
Some(next_dir)
|
|
} else {
|
|
None
|
|
}
|
|
};
|
|
moving(velocity, &time, &mut direction, &mut transform, entity, &mut commands, switch_direction);
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
/// Система движения тела змеи
|
|
pub fn snake_moving(
|
|
query: Query<(&mut Transform, &mut Direction, &Velocity, Entity), (With<Tail>, Without<Head>)>,
|
|
time: Res<Time<Fixed>>,
|
|
snake_path: Res<SnakePath>,
|
|
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 spawn_meal_if_needed(
|
|
time: Res<Time<Fixed>>, //
|
|
mut meal_timer: ResMut<MealRespawnTimer>,
|
|
query: Query<&Transform, With<Meal>>,
|
|
mut commands: Commands,
|
|
) {
|
|
if meal_timer.tick(time.elapsed()).just_finished() && query.count() < MAX_MEALS_ON_FIELD as usize {
|
|
let used_positions = query.iter().map(Position::from).collect::<HashSet<Position>>();
|
|
let mut rng = rand::rng();
|
|
let pos = loop {
|
|
const ABS: i8 = (FIELD_SIZE as f32 / 2.0) as i8;
|
|
let rand_x = rng.random::<i8>() % ABS;
|
|
let rand_y = rng.random::<i8>() % ABS;
|
|
let rand_pos = Position::new(rand_x, rand_y);
|
|
if !used_positions.contains(&rand_pos) {
|
|
break rand_pos;
|
|
}
|
|
};
|
|
commands.spawn((
|
|
Meal,
|
|
Sprite::from_color(YELLOW_400, vec2(MEAL_SIZE as f32, MEAL_SIZE as f32)), //
|
|
Transform {
|
|
translation: pos.translation().extend(0.0),
|
|
..Default::default()
|
|
},
|
|
));
|
|
trace!("new meal spawned at {pos}");
|
|
}
|
|
}
|