Проектная работа - 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();
|
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()
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user