Проектная работа - WIP
This commit is contained in:
@@ -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)]
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+69
-60
@@ -1,92 +1,101 @@
|
|||||||
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 src = direction.get_coord(&transform);
|
||||||
let delta = velocity * time.delta_secs();
|
let sign = direction.get_change_sign();
|
||||||
let src = direction.get_coord(&transform);
|
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 sign = direction.get_change_sign();
|
||||||
let dst = src + sign * delta;
|
let dst = src + sign * delta;
|
||||||
let mut splited = split_by_grid(src, dst, GRID_SIZE as f32).into_iter();
|
direction.set_coord(&mut transform, dst);
|
||||||
let first = splited.next().unwrap_or((0.0, 0.0));
|
|
||||||
direction.set_coord(&mut transform, first.0);
|
|
||||||
|
|
||||||
// вторая часть движения - после границы сетки
|
// создать событие прохождения границы сетки
|
||||||
if let Some(second) = splited.next() {
|
commands.entity(entity).trigger(|e| GridBarier::new(e, pos, (&*direction).clone()));
|
||||||
let delta = second.1.abs();
|
}
|
||||||
let pos = Position::from(&*transform);
|
}
|
||||||
if let Some(dir) = snake_path.peek(&pos) {
|
|
||||||
trace!("set new direction for {entity} in {pos} --> {dir}");
|
pub fn head_moving(
|
||||||
*direction = dir.clone();
|
query: Query<(&mut Transform, &mut Direction, &Velocity, &Head, Entity)>,
|
||||||
}
|
time: Res<Time<Fixed>>,
|
||||||
let src = direction.get_coord(&*transform);
|
mut snake_path: ResMut<SnakePath>,
|
||||||
let sign = direction.get_change_sign();
|
mut commands: Commands,
|
||||||
let dst = src + sign * delta;
|
) {
|
||||||
direction.set_coord(&mut transform, dst);
|
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();
|
||||||
commands.entity(entity).trigger(|e| GridBarier::new(e, pos, (&*direction).clone()));
|
let snake_path = &mut snake_path;
|
||||||
|
let switch_direction = move |pos: Position| {
|
||||||
// если это кончик хвоста, то нужно очистить [SnakePath] за собой
|
snake_path.insert(pos.clone(), next_dir.clone());
|
||||||
if has_end {
|
Some(next_dir)
|
||||||
snake_path.drop(&pos);
|
};
|
||||||
}
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)),
|
||||||
|
|||||||
Reference in New Issue
Block a user