Проектная работа - WIP

This commit is contained in:
7 changed files with 85 additions and 113 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ pub const SNAKE_VELOCITY: u8 = (GRID_SIZE as f32 / 1.0) as u8;
/// Голова змеи /// Голова змеи
#[derive(Component)] #[derive(Component)]
pub struct Head; pub struct Head(pub Direction);
/// Сегмент змеиного хвоста /// Сегмент змеиного хвоста
#[derive(Component)] #[derive(Component)]
+2 -48
View File
@@ -4,51 +4,5 @@ use std::collections::HashMap;
use crate::domain::{Direction, Position}; use crate::domain::{Direction, Position};
/// Путь змеи /// Путь змеи
#[derive(Resource)] #[derive(Resource, Default, Deref, DerefMut)]
pub struct SnakePath { pub struct SnakePath(HashMap<Position, Direction>);
positions: HashMap<Position, Direction>,
fresh: Option<Position>,
}
impl SnakePath {
pub fn new() -> Self {
SnakePath {
positions: HashMap::with_capacity(16),
fresh: None,
}
}
pub fn push(&mut self, position: Position, direction: Direction) -> bool {
if let Some(_) = self.positions.get(&position) {
if let Some(fresh) = self.fresh {
if position == fresh {
self.positions.insert(position, direction);
return true;
}
}
return false;
}
self.positions.insert(position, direction);
self.fresh = Some(position);
return true;
}
pub fn peek(&mut self, position: &Position) -> Option<Direction> {
if let Some(fresh) = self.fresh {
if fresh == *position {
self.fresh = None;
}
}
self.positions.get(position).cloned()
}
pub fn drop(&mut self, position: &Position) {
self.positions.remove(position);
}
}
impl Default for SnakePath {
fn default() -> Self {
Self::new()
}
}
+47 -38
View File
@@ -1,63 +1,46 @@
use bevy::prelude::*; use bevy::prelude::*;
use crate::{ use crate::{
domain::{Direction, End, GRID_SIZE, GridBarier, Head, Position, SnakePath, Tail, Velocity}, domain::{Direction, GRID_SIZE, GridBarier, Head, Position, SnakePath, Tail, Velocity},
tools::split_by_grid, tools::split_by_grid,
}; };
/// Система поворота головы змеи /// Система поворота головы змеи
pub fn head_rotation( pub fn head_rotation(
input: Res<ButtonInput<KeyCode>>, // input: Res<ButtonInput<KeyCode>>, //
query: Single<(&mut Transform, &Direction), With<Head>>, query: Single<(&mut Transform, &mut Head, &Direction)>,
mut snake_path: ResMut<SnakePath>,
) { ) {
let _span = trace_span!("head_rotation").entered(); let _span = trace_span!("head_rotation").entered();
if input.just_pressed(KeyCode::ArrowLeft) { if input.just_pressed(KeyCode::ArrowLeft) {
let (mut transform, direction) = query.into_inner(); let (mut transform, mut head, direction) = query.into_inner();
let translation = transform.translation.clone(); let translation = transform.translation.clone();
let cur_pos = Position::from(&*transform); let cur_pos = Position::from(&*transform);
let mut dst_pos = direction.dst_pos(&*transform);
let mut new_direction = direction.clone(); let mut new_direction = direction.clone();
new_direction.rotate_left(); new_direction.rotate_left();
if !snake_path.push(dst_pos, new_direction) { head.0 = new_direction;
trace!("try 1 failed: cannot push [{dst_pos} => {new_direction}] into SnakePath");
dst_pos.shift(&direction);
if !snake_path.push(dst_pos, new_direction) {
trace!("try 2 failed: cannot push [{dst_pos} => {new_direction}] into SnakePath");
return;
}
}
transform.rotation = new_direction.orientation(); transform.rotation = new_direction.orientation();
trace!("running {direction} at {cur_pos} {translation}: go {new_direction} on {dst_pos}"); trace!("running {direction} at {cur_pos} {translation}: go {new_direction}");
} else if input.just_pressed(KeyCode::ArrowRight) { } else if input.just_pressed(KeyCode::ArrowRight) {
let (mut transform, direction) = query.into_inner(); let (mut transform, mut head, direction) = query.into_inner();
let translation = transform.translation.clone(); let translation = transform.translation.clone();
let cur_pos = Position::from(&*transform); let cur_pos = Position::from(&*transform);
let mut dst_pos = direction.dst_pos(&*transform);
let mut new_direction = direction.clone(); let mut new_direction = direction.clone();
new_direction.rotate_right(); new_direction.rotate_right();
if !snake_path.push(dst_pos, new_direction) { head.0 = new_direction;
trace!("try 1 failed: cannot push [{dst_pos} => {new_direction}] into SnakePath");
dst_pos.shift(&direction);
if !snake_path.push(dst_pos, new_direction) {
trace!("try 2 failed: cannot push [{dst_pos} => {new_direction}] into SnakePath");
return;
}
}
transform.rotation = new_direction.orientation(); transform.rotation = new_direction.orientation();
trace!("running {direction} at {cur_pos} {translation}: go {new_direction} on {dst_pos}"); trace!("running {direction} at {cur_pos} {translation}: go {new_direction}");
} }
} }
/// Система движения змеи fn moving(
pub fn snake_moving( velocity: &f32,
time: Res<Time<Fixed>>, time: &Time<Fixed>,
mut snake_path: ResMut<SnakePath>, direction: &mut Direction,
query: Query<(&mut Transform, &mut Direction, &Velocity, Entity, Has<End>), Or<(With<Head>, With<Tail>, With<End>)>>, mut transform: &mut Transform,
mut commands: Commands, entity: Entity,
commands: &mut Commands,
switch_direction: impl FnOnce(Position) -> Option<Direction>,
) { ) {
let _span = trace_span!("snake_moving").entered();
for (mut transform, mut direction, Velocity(velocity), entity, has_end) in query.into_iter() {
// первая часть движения - до границы сетки // первая часть движения - до границы сетки
let delta = velocity * time.delta_secs(); let delta = velocity * time.delta_secs();
let src = direction.get_coord(&transform); let src = direction.get_coord(&transform);
@@ -71,9 +54,9 @@ pub fn snake_moving(
if let Some(second) = splited.next() { if let Some(second) = splited.next() {
let delta = second.1.abs(); let delta = second.1.abs();
let pos = Position::from(&*transform); let pos = Position::from(&*transform);
if let Some(dir) = snake_path.peek(&pos) { if let Some(dir) = switch_direction(pos) {
trace!("set new direction for {entity} in {pos} --> {dir}"); trace!("set new direction for {entity} in {pos} --> {dir}");
*direction = dir.clone(); *direction = dir;
} }
let src = direction.get_coord(&*transform); let src = direction.get_coord(&*transform);
let sign = direction.get_change_sign(); let sign = direction.get_change_sign();
@@ -82,11 +65,37 @@ pub fn snake_moving(
// создать событие прохождения границы сетки // создать событие прохождения границы сетки
commands.entity(entity).trigger(|e| GridBarier::new(e, pos, (&*direction).clone())); commands.entity(entity).trigger(|e| GridBarier::new(e, pos, (&*direction).clone()));
}
}
// если это кончик хвоста, то нужно очистить [SnakePath] за собой pub fn head_moving(
if has_end { query: Query<(&mut Transform, &mut Direction, &Velocity, &Head, Entity)>,
snake_path.drop(&pos); 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.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<Tail>, Without<Head>)>,
time: Res<Time<Fixed>>,
snake_path: Res<SnakePath>,
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);
} }
} }
+1 -1
View File
@@ -5,6 +5,6 @@ mod observers;
mod startup; mod startup;
mod tools; mod tools;
pub use gameplay::{head_rotation, snake_moving}; pub use gameplay::{head_moving, head_rotation, snake_moving};
pub use startup::startup; pub use startup::startup;
pub use tools::define_log_layer; pub use tools::define_log_layer;
+2 -2
View File
@@ -1,5 +1,5 @@
use bevy::prelude::*; use bevy::prelude::*;
use snake_game::{head_rotation, snake_moving, startup}; use snake_game::{head_moving, head_rotation, snake_moving, startup};
fn main() { fn main() {
App::new() App::new()
@@ -33,7 +33,7 @@ fn main() {
), ),
) )
.add_systems(Update, (head_rotation,)) .add_systems(Update, (head_rotation,))
.add_systems(FixedUpdate, (snake_moving,)) .add_systems(FixedUpdate, (head_moving, snake_moving))
.add_systems( .add_systems(
PostUpdate, PostUpdate,
( (
+7
View File
@@ -14,3 +14,10 @@ pub fn schedule_set_velocity(tail: Entity) -> impl Fn(On<GridBarier>, Commands,
} }
} }
} }
/// Очистка [SnakePath] от старых значений
pub fn clean_snake_path(event: On<GridBarier>, query: Query<&End>, mut snake_path: ResMut<SnakePath>) {
if query.contains(event.entity) {
snake_path.remove(&event.position);
}
}
+3 -1
View File
@@ -19,11 +19,13 @@ fn spawn_snake(commands: &mut Commands) {
commands.insert_resource(SnakePath::default()); commands.insert_resource(SnakePath::default());
commands.spawn(Observer::new(crate::observers::clean_snake_path));
commands.spawn(Observer::new(crate::debug::trace_snake_moving)); commands.spawn(Observer::new(crate::debug::trace_snake_moving));
let head = commands let head = commands
.spawn(( .spawn((
Head, Head(Direction::default()),
Direction::default(), Direction::default(),
Velocity(SNAKE_VELOCITY as f32), Velocity(SNAKE_VELOCITY as f32),
Sprite::from_color(Color::srgb(1., 1., 1.), vec2(HEAD_SIZE as f32, HEAD_SIZE as f32)), Sprite::from_color(Color::srgb(1., 1., 1.), vec2(HEAD_SIZE as f32, HEAD_SIZE as f32)),