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

This commit is contained in:
6 changed files with 87 additions and 87 deletions
+4 -2
View File
@@ -59,8 +59,9 @@ pub fn spawn_grid(commands: &mut Commands) {
let grid = commands.spawn((Grid, Transform::default(), Visibility::default())).id(); let grid = commands.spawn((Grid, Transform::default(), Visibility::default())).id();
for x in -20..=20 { for x in -20..=20 {
let color = if x % 5 == 0 { Color::srgb(1.0, 0.2, 0.5) } else { Color::srgb(1.0, 1.0, 0.5) };
commands.spawn(( commands.spawn((
Sprite::from_color(Color::srgb(1.0, 1.0, 0.5), vec2(0.5, 2000.0)), Sprite::from_color(color, vec2(0.5, 2000.0)),
Transform { Transform {
translation: vec3(x as f32 * GRID_SIZE as f32, 0.0, -1.0), translation: vec3(x as f32 * GRID_SIZE as f32, 0.0, -1.0),
..Default::default() ..Default::default()
@@ -69,8 +70,9 @@ pub fn spawn_grid(commands: &mut Commands) {
)); ));
} }
for y in -20..=20 { for y in -20..=20 {
let color = if y % 5 == 0 { Color::srgb(1.0, 0.2, 0.5) } else { Color::srgb(1.0, 1.0, 0.5) };
commands.spawn(( commands.spawn((
Sprite::from_color(Color::srgb(1.0, 1.0, 0.5), vec2(2000.0, 0.5)), Sprite::from_color(color, vec2(2000.0, 0.5)),
Transform { Transform {
translation: vec3(0.0, y as f32 * GRID_SIZE as f32, -1.0), translation: vec3(0.0, y as f32 * GRID_SIZE as f32, -1.0),
..Default::default() ..Default::default()
+26 -8
View File
@@ -1,4 +1,4 @@
use std::{collections::HashMap, fmt::Debug}; use std::{collections::HashMap, f32::consts::PI, fmt::Debug};
use bevy::prelude::*; use bevy::prelude::*;
@@ -24,12 +24,13 @@ pub struct End;
#[derive(EntityEvent)] #[derive(EntityEvent)]
pub struct GridBarier { pub struct GridBarier {
pub entity: Entity, pub entity: Entity,
pub pos: Position, pub position: Position,
pub direction: Direction,
} }
impl GridBarier { impl GridBarier {
pub fn new(entity: Entity, pos: Position) -> Self { pub fn new(entity: Entity, position: Position, direction: Direction) -> Self {
Self { entity, pos } Self { entity, position, direction }
} }
} }
@@ -98,6 +99,16 @@ impl Direction {
}; };
pos pos
} }
/// Получить ориентацию в пространстве
pub fn orientation(&self) -> Quat {
match self {
Direction::Up => Quat::default(),
Direction::Left => Quat::from_rotation_z(PI / 2.0),
Direction::Down => Quat::from_rotation_z(PI),
Direction::Right => Quat::from_rotation_z(-PI / 2.0),
}
}
} }
impl Default for Direction { impl Default for Direction {
@@ -106,14 +117,21 @@ impl Default for Direction {
} }
} }
impl std::fmt::Display for Direction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Direction::Up => write!(f, "Up"),
Direction::Left => write!(f, "Left"),
Direction::Down => write!(f, "Down"),
Direction::Right => write!(f, "Right"),
}
}
}
/// Скорость движения /// Скорость движения
#[derive(Component)] #[derive(Component)]
pub struct Velocity(pub f32); pub struct Velocity(pub f32);
/// Инерция
#[derive(Component, Default)]
pub struct Inert(pub Direction);
/// Путь змеи /// Путь змеи
#[derive(Resource, Default, Debug, Deref, DerefMut)] #[derive(Resource, Default, Debug, Deref, DerefMut)]
pub struct SnakePath(HashMap<Position, Direction>); pub struct SnakePath(HashMap<Position, Direction>);
+25 -19
View File
@@ -1,7 +1,7 @@
use bevy::prelude::*; use bevy::prelude::*;
use crate::{ use crate::{
domain::{Direction, End, GRID_SIZE, Head, SnakePath, Tail, Velocity}, domain::{Direction, End, GRID_SIZE, GridBarier, Head, SnakePath, Tail, Velocity},
tools::{Position, split_by_grid}, tools::{Position, split_by_grid},
}; };
@@ -11,32 +11,22 @@ pub fn head_rotation(
query: Single<(&mut Transform, &Direction), With<Head>>, query: Single<(&mut Transform, &Direction), With<Head>>,
mut snake_path: ResMut<SnakePath>, mut snake_path: ResMut<SnakePath>,
) { ) {
use core::f32::consts::PI;
if input.just_pressed(KeyCode::ArrowLeft) { if input.just_pressed(KeyCode::ArrowLeft) {
let (mut transform, direction) = query.into_inner(); let (mut transform, direction) = query.into_inner();
let next_pos = direction.next_pos(&*transform); let next_pos = direction.next_pos(&*transform);
let mut direction = direction.clone(); let mut direction = direction.clone();
direction.rotate_left(); direction.rotate_left();
if let Some(prev_direction) = snake_path.insert(next_pos, direction) { snake_path.insert(next_pos, direction);
if direction != prev_direction { transform.rotation = direction.orientation();
transform.rotate_z(PI); trace!("go {direction} on {next_pos}");
}
} else {
transform.rotate_z(PI / 2.0);
}
} else if input.just_pressed(KeyCode::ArrowRight) { } else if input.just_pressed(KeyCode::ArrowRight) {
let (mut transform, direction) = query.into_inner(); let (mut transform, direction) = query.into_inner();
let next_pos = direction.next_pos(&*transform); let next_pos = direction.next_pos(&*transform);
let mut direction = direction.clone(); let mut direction = direction.clone();
direction.rotate_right(); direction.rotate_right();
if let Some(prev_direction) = snake_path.insert(next_pos, direction) { snake_path.insert(next_pos, direction);
if direction != prev_direction { transform.rotation = direction.orientation();
transform.rotate_z(-PI); trace!("go {direction} on {next_pos}");
}
} else {
transform.rotate_z(-PI / 2.0);
}
} }
} }
@@ -44,9 +34,11 @@ pub fn head_rotation(
pub fn snake_moving( pub fn snake_moving(
time: Res<Time<Fixed>>, time: Res<Time<Fixed>>,
snake_path: Res<SnakePath>, snake_path: Res<SnakePath>,
query: Query<(&mut Transform, &mut Direction, &Velocity), Or<(With<Head>, With<Tail>, With<End>)>>, query: Query<(&mut Transform, &mut Direction, &Velocity, Entity, Has<End>), Or<(With<Head>, With<Tail>, With<End>)>>,
mut commands: Commands,
) { ) {
for (mut transform, mut direction, Velocity(velocity)) in query.into_iter() { 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);
let sign = direction.get_change_sign(); let sign = direction.get_change_sign();
@@ -54,6 +46,8 @@ pub fn snake_moving(
let mut splited = split_by_grid(src, dst, GRID_SIZE as f32).into_iter(); let mut splited = split_by_grid(src, dst, GRID_SIZE as f32).into_iter();
let first = splited.next().unwrap_or((0.0, 0.0)); let first = splited.next().unwrap_or((0.0, 0.0));
direction.set_coord(&mut transform, first.0); direction.set_coord(&mut transform, first.0);
// вторая часть движения - после границы сетки
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);
@@ -65,6 +59,18 @@ pub fn snake_moving(
let sign = direction.get_change_sign(); let sign = direction.get_change_sign();
let dst = src + sign * delta; let dst = src + sign * delta;
direction.set_coord(&mut transform, dst); direction.set_coord(&mut transform, dst);
// создать событие прохождения границы сетки
commands.entity(entity).trigger(|e| GridBarier::new(e, pos, (&*direction).clone()));
// если это кончик хвоста, то нужно очистить [SnakePath] за собой
if has_end {
commands.queue(move |world: &mut World| {
if let Some(mut snake_path) = world.get_resource_mut::<SnakePath>() {
snake_path.remove(&pos);
}
});
}
} }
} }
} }
+3 -37
View File
@@ -1,50 +1,16 @@
use crate::domain::*; use crate::{domain::*, tools::Position};
use bevy::prelude::*; use bevy::prelude::*;
#[allow(unused)]
/// Слушатель событий [GridBarier] для хвоста, обновляющий направление его движения из ресурса [SnakePath] и позволяющий следовать за головой
pub fn tail_observer(event: On<GridBarier>, mut query: Query<(&mut Direction, &Transform)>, snake_path: Res<SnakePath>) {
let entity = event.entity;
if event.observer() == entity {
if let Ok((mut direction, transform)) = query.get_mut(event.entity) {
/*
if let Some((position, new_direction)) = snake_path.pick(transform) {
trace!("change direction for {entity:?} in {position:?} to {new_direction:?}");
*direction = new_direction;
}
*/
}
}
}
/// Слушатель событий [GridBarier] для конца змеи, обновляющий направление его движения из ресурса [SnakePath] и удаляющий полученные значения из [SnakePath]
pub fn cleanup_snake_path(event: On<GridBarier>, mut commands: Commands, query: Query<&Transform, With<End>>) {
if let Ok(transform) = query.get(event.entity) {
// trace!("schedule cleaning SnakePath for {:?}", transform);
let transform = transform.clone();
commands.queue(move |world: &mut World| {
let mut snake_path = world.resource_mut::<SnakePath>();
/*
if let Some((pos, dir)) = snake_path.drop(&transform) {
let map = snake_path.debug();
trace!("removed entry from SnakePath: {pos:?} => {dir:?}\n\tsnake_path: {map:?}");
}
*/
});
}
}
/// Запланировать установку скорости движения [Velocity] при следующем событии [GridBarier] /// Запланировать установку скорости движения [Velocity] при следующем событии [GridBarier]
pub fn schedule_set_velocity(tail: Entity) -> impl Fn(On<GridBarier>, Commands, Query<(&mut Velocity, &Transform)>) { pub fn schedule_set_velocity(tail: Entity) -> impl Fn(On<GridBarier>, Commands, Query<(&mut Velocity, &Transform)>) {
move |event: On<GridBarier>, mut commands: Commands, mut query: Query<(&mut Velocity, &Transform)>| { move |event: On<GridBarier>, mut commands: Commands, mut query: Query<(&mut Velocity, &Transform)>| {
if let Ok((mut velocity, transform)) = query.get_mut(tail) { if let Ok((mut velocity, transform)) = query.get_mut(tail) {
/* let position = Position::from(transform);
if event.dst_pos != transform.into() { if event.position != position {
velocity.0 = SNAKE_VELOCITY as f32; velocity.0 = SNAKE_VELOCITY as f32;
commands.entity(event.observer()).despawn(); commands.entity(event.observer()).despawn();
} }
*/
} }
} }
} }
+8 -14
View File
@@ -1,13 +1,13 @@
use bevy::prelude::*; use bevy::prelude::*;
use crate::{ use crate::{
domain::{End, SnakePath, Tail}, domain::{End, GridBarier, SnakePath, Tail},
observers::{cleanup_snake_path, schedule_set_velocity, tail_observer}, observers::schedule_set_velocity,
}; };
/// Инициализация игрового мира /// Инициализация игрового мира
pub fn startup(mut commands: Commands, mut timer: ResMut<Time<Fixed>>) { pub fn startup(mut commands: Commands, mut time: ResMut<Time<Fixed>>) {
timer.set_timestep_hz(32.0); time.set_timestep_hz(32.0);
spawn_snake(&mut commands); spawn_snake(&mut commands);
crate::debug::spawn_grid(&mut commands); crate::debug::spawn_grid(&mut commands);
commands.spawn(Camera2d); commands.spawn(Camera2d);
@@ -15,25 +15,21 @@ pub fn startup(mut commands: Commands, mut timer: ResMut<Time<Fixed>>) {
/// Создать голову змеи /// Создать голову змеи
fn spawn_snake(commands: &mut Commands) { fn spawn_snake(commands: &mut Commands) {
use crate::domain::{Direction, HEAD_SIZE, Head, Inert, SNAKE_VELOCITY, Velocity}; use crate::domain::{Direction, HEAD_SIZE, Head, SNAKE_VELOCITY, Velocity};
commands.insert_resource(SnakePath::default()); commands.insert_resource(SnakePath::default());
commands.spawn(Observer::new(cleanup_snake_path));
/*
commands.spawn(Observer::new(|e: On<GridBarier>| { commands.spawn(Observer::new(|e: On<GridBarier>| {
let entity = e.entity; let entity = e.entity;
let src = e.src_pos; let position = e.position;
let dst = e.dst_pos; let direction = e.direction;
trace!("...<{entity:?}> moving {src:>16} => {dst:>16}"); trace!("...<{entity:?}> is moving on {position} {direction:?}");
})); }));
*/
let head = commands let head = commands
.spawn(( .spawn((
Head, Head,
Direction::default(), Direction::default(),
Inert::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)),
Transform { Transform {
@@ -64,14 +60,12 @@ fn spawn_snake(commands: &mut Commands) {
Tail, Tail,
End, End,
Direction::default(), Direction::default(),
Inert::default(),
Velocity(0.0), Velocity(0.0),
Sprite::from_color(Color::srgb(0.7, 0.7, 0.7), vec2(HEAD_SIZE as f32 * 0.8, HEAD_SIZE as f32 * 0.8)), Sprite::from_color(Color::srgb(0.7, 0.7, 0.7), vec2(HEAD_SIZE as f32 * 0.8, HEAD_SIZE as f32 * 0.8)),
Transform { Transform {
translation: vec3(0.0, 0.0, 0.0), translation: vec3(0.0, 0.0, 0.0),
..Default::default() ..Default::default()
}, },
Observer::new(tail_observer),
)) ))
.id(); .id();
commands.entity(head).observe(schedule_set_velocity(tail)); commands.entity(head).observe(schedule_set_velocity(tail));
+21 -7
View File
@@ -1,6 +1,6 @@
use std::f32::consts::PI; use std::f32::consts::PI;
use bevy::{math::Quat, transform::components::Transform}; use bevy::transform::components::Transform;
#[test] #[test]
fn test_saturating_sub() { fn test_saturating_sub() {
@@ -52,10 +52,24 @@ fn test_ranges() {
fn test_transform() { fn test_transform() {
let mut transform = Transform::default(); let mut transform = Transform::default();
dbg!(transform); dbg!(transform);
transform.rotate_z(PI / 2.0); dbg!({
transform.rotate_z(PI / 2.0); transform.rotate_z(PI / 2.0);
transform.rotate_z(PI / 2.0); transform
transform.rotate_z(PI / 2.0); });
dbg!(transform); dbg!({
let mut quat = Quat::default(); transform.rotate_z(PI / 2.0);
transform
});
dbg!({
transform.rotate_z(PI / 2.0);
transform
});
dbg!({
transform.rotate_z(PI / 2.0);
transform
});
dbg!({
transform.rotate_z(PI * 2.0);
transform
});
} }