Проектная работа - WIP
This commit is contained in:
@@ -59,8 +59,9 @@ pub fn spawn_grid(commands: &mut Commands) {
|
||||
let grid = commands.spawn((Grid, Transform::default(), Visibility::default())).id();
|
||||
|
||||
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((
|
||||
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 {
|
||||
translation: vec3(x as f32 * GRID_SIZE as f32, 0.0, -1.0),
|
||||
..Default::default()
|
||||
@@ -69,8 +70,9 @@ pub fn spawn_grid(commands: &mut Commands) {
|
||||
));
|
||||
}
|
||||
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((
|
||||
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 {
|
||||
translation: vec3(0.0, y as f32 * GRID_SIZE as f32, -1.0),
|
||||
..Default::default()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{collections::HashMap, fmt::Debug};
|
||||
use std::{collections::HashMap, f32::consts::PI, fmt::Debug};
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
@@ -24,12 +24,13 @@ pub struct End;
|
||||
#[derive(EntityEvent)]
|
||||
pub struct GridBarier {
|
||||
pub entity: Entity,
|
||||
pub pos: Position,
|
||||
pub position: Position,
|
||||
pub direction: Direction,
|
||||
}
|
||||
|
||||
impl GridBarier {
|
||||
pub fn new(entity: Entity, pos: Position) -> Self {
|
||||
Self { entity, pos }
|
||||
pub fn new(entity: Entity, position: Position, direction: Direction) -> Self {
|
||||
Self { entity, position, direction }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +99,16 @@ impl Direction {
|
||||
};
|
||||
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 {
|
||||
@@ -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)]
|
||||
pub struct Velocity(pub f32);
|
||||
|
||||
/// Инерция
|
||||
#[derive(Component, Default)]
|
||||
pub struct Inert(pub Direction);
|
||||
|
||||
/// Путь змеи
|
||||
#[derive(Resource, Default, Debug, Deref, DerefMut)]
|
||||
pub struct SnakePath(HashMap<Position, Direction>);
|
||||
|
||||
+25
-19
@@ -1,7 +1,7 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
@@ -11,32 +11,22 @@ pub fn head_rotation(
|
||||
query: Single<(&mut Transform, &Direction), With<Head>>,
|
||||
mut snake_path: ResMut<SnakePath>,
|
||||
) {
|
||||
use core::f32::consts::PI;
|
||||
|
||||
if input.just_pressed(KeyCode::ArrowLeft) {
|
||||
let (mut transform, direction) = query.into_inner();
|
||||
let next_pos = direction.next_pos(&*transform);
|
||||
let mut direction = direction.clone();
|
||||
direction.rotate_left();
|
||||
if let Some(prev_direction) = snake_path.insert(next_pos, direction) {
|
||||
if direction != prev_direction {
|
||||
transform.rotate_z(PI);
|
||||
}
|
||||
} else {
|
||||
transform.rotate_z(PI / 2.0);
|
||||
}
|
||||
snake_path.insert(next_pos, direction);
|
||||
transform.rotation = direction.orientation();
|
||||
trace!("go {direction} on {next_pos}");
|
||||
} else if input.just_pressed(KeyCode::ArrowRight) {
|
||||
let (mut transform, direction) = query.into_inner();
|
||||
let next_pos = direction.next_pos(&*transform);
|
||||
let mut direction = direction.clone();
|
||||
direction.rotate_right();
|
||||
if let Some(prev_direction) = snake_path.insert(next_pos, direction) {
|
||||
if direction != prev_direction {
|
||||
transform.rotate_z(-PI);
|
||||
}
|
||||
} else {
|
||||
transform.rotate_z(-PI / 2.0);
|
||||
}
|
||||
snake_path.insert(next_pos, direction);
|
||||
transform.rotation = direction.orientation();
|
||||
trace!("go {direction} on {next_pos}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +34,11 @@ pub fn head_rotation(
|
||||
pub fn snake_moving(
|
||||
time: Res<Time<Fixed>>,
|
||||
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 src = direction.get_coord(&transform);
|
||||
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 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);
|
||||
@@ -65,6 +59,18 @@ pub fn snake_moving(
|
||||
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()));
|
||||
|
||||
// если это кончик хвоста, то нужно очистить [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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,16 @@
|
||||
use crate::domain::*;
|
||||
use crate::{domain::*, tools::Position};
|
||||
|
||||
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]
|
||||
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)>| {
|
||||
if let Ok((mut velocity, transform)) = query.get_mut(tail) {
|
||||
/*
|
||||
if event.dst_pos != transform.into() {
|
||||
let position = Position::from(transform);
|
||||
if event.position != position {
|
||||
velocity.0 = SNAKE_VELOCITY as f32;
|
||||
commands.entity(event.observer()).despawn();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
domain::{End, SnakePath, Tail},
|
||||
observers::{cleanup_snake_path, schedule_set_velocity, tail_observer},
|
||||
domain::{End, GridBarier, SnakePath, Tail},
|
||||
observers::schedule_set_velocity,
|
||||
};
|
||||
|
||||
/// Инициализация игрового мира
|
||||
pub fn startup(mut commands: Commands, mut timer: ResMut<Time<Fixed>>) {
|
||||
timer.set_timestep_hz(32.0);
|
||||
pub fn startup(mut commands: Commands, mut time: ResMut<Time<Fixed>>) {
|
||||
time.set_timestep_hz(32.0);
|
||||
spawn_snake(&mut commands);
|
||||
crate::debug::spawn_grid(&mut commands);
|
||||
commands.spawn(Camera2d);
|
||||
@@ -15,25 +15,21 @@ pub fn startup(mut commands: Commands, mut timer: ResMut<Time<Fixed>>) {
|
||||
|
||||
/// Создать голову змеи
|
||||
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.spawn(Observer::new(cleanup_snake_path));
|
||||
|
||||
/*
|
||||
commands.spawn(Observer::new(|e: On<GridBarier>| {
|
||||
let entity = e.entity;
|
||||
let src = e.src_pos;
|
||||
let dst = e.dst_pos;
|
||||
trace!("...<{entity:?}> moving {src:>16} => {dst:>16}");
|
||||
let position = e.position;
|
||||
let direction = e.direction;
|
||||
trace!("...<{entity:?}> is moving on {position} {direction:?}");
|
||||
}));
|
||||
*/
|
||||
|
||||
let head = commands
|
||||
.spawn((
|
||||
Head,
|
||||
Direction::default(),
|
||||
Inert::default(),
|
||||
Velocity(SNAKE_VELOCITY as f32),
|
||||
Sprite::from_color(Color::srgb(1., 1., 1.), vec2(HEAD_SIZE as f32, HEAD_SIZE as f32)),
|
||||
Transform {
|
||||
@@ -64,14 +60,12 @@ fn spawn_snake(commands: &mut Commands) {
|
||||
Tail,
|
||||
End,
|
||||
Direction::default(),
|
||||
Inert::default(),
|
||||
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)),
|
||||
Transform {
|
||||
translation: vec3(0.0, 0.0, 0.0),
|
||||
..Default::default()
|
||||
},
|
||||
Observer::new(tail_observer),
|
||||
))
|
||||
.id();
|
||||
commands.entity(head).observe(schedule_set_velocity(tail));
|
||||
|
||||
Reference in New Issue
Block a user