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

This commit is contained in:
8 changed files with 171 additions and 139 deletions
+55
View File
@@ -0,0 +1,55 @@
use bevy::prelude::*;
/// ДЛЯ ОТЛАДКИ - вывод всех сущностей мира
pub fn print_all_entities(mut commands: Commands, query: Query<Entity>) {
for entity in query {
commands.entity(entity).log_components();
}
}
/// ДЛЯ ОТЛАДКИ - вывод всех сущностей мира по нажатию Enter
pub fn print_all_entities_by_press_enter(
input: Res<ButtonInput<KeyCode>>,
mut commands: Commands,
query: Query<Entity>,
) {
if input.just_pressed(KeyCode::Enter) {
for entity in query {
commands.entity(entity).log_components();
}
}
}
/// Координатная сетка
#[derive(Component)]
pub struct Grid;
/// Создать координатную сетку
pub fn spawn_grid(commands: &mut Commands) {
use crate::domain::GRID_SIZE;
let grid = commands
.spawn((Grid, Transform::default(), Visibility::default()))
.id();
for x in -20..=20 {
commands.spawn((
Sprite::from_color(Color::srgb(1.0, 1.0, 0.5), vec2(0.5, 2000.0)),
Transform {
translation: vec3(x as f32 * GRID_SIZE as f32, 0.0, -1.0),
..Default::default()
},
ChildOf(grid),
));
}
for y in -20..=20 {
commands.spawn((
Sprite::from_color(Color::srgb(1.0, 1.0, 0.5), vec2(2000.0, 0.5)),
Transform {
translation: vec3(0.0, y as f32 * GRID_SIZE as f32, -1.0),
..Default::default()
},
ChildOf(grid),
));
}
}
+3 -7
View File
@@ -7,10 +7,6 @@ pub const GRID_SIZE: u8 = 48;
pub const HEAD_SIZE: u8 = (GRID_SIZE as f32 * 1.1) as u8; pub const HEAD_SIZE: u8 = (GRID_SIZE as f32 * 1.1) as u8;
pub const SNAKE_VELOCITY: u8 = (GRID_SIZE as f32 / 1.0) as u8; pub const SNAKE_VELOCITY: u8 = (GRID_SIZE as f32 / 1.0) as u8;
/// Координатная сетка
#[derive(Component)]
pub struct Grid;
/// Голова змеи /// Голова змеи
#[derive(Component)] #[derive(Component)]
pub struct Head; pub struct Head;
@@ -118,9 +114,9 @@ impl SnakePath {
self.0.get(&key).cloned() self.0.get(&key).cloned()
} }
pub fn pop(&mut self, transform: &Transform) -> Option<Direction> { pub fn drop(&mut self, transform: &Transform) -> Option<(i16, i16, Direction)> {
let key = SnakePath::to_key(transform); let key = SnakePath::to_key(transform);
self.0.remove(&key) self.0.remove(&key).map(|d| (key.0, key.1, d))
} }
} }
@@ -139,7 +135,7 @@ mod test {
assert!(snake_path.pick(&Transform::default()) == Some(Direction::default())); assert!(snake_path.pick(&Transform::default()) == Some(Direction::default()));
assert_eq!(snake_path.0.len(), 1); assert_eq!(snake_path.0.len(), 1);
assert!(snake_path.pop(&Transform::default()) == Some(Direction::default())); assert!(snake_path.drop(&Transform::default()) == Some((0, 0, Direction::default())));
assert_eq!(snake_path.0.len(), 0); assert_eq!(snake_path.0.len(), 0);
} }
} }
+7 -2
View File
@@ -15,15 +15,20 @@ pub fn head_rotation(
let (mut transform, mut direction) = query.into_inner(); let (mut transform, mut direction) = query.into_inner();
let mut append_snake_path = |transform: &Transform, direction: &Direction| {
snake_path.push(&transform, direction.clone());
trace!("append snake_path: {:?}", snake_path);
};
if input.just_pressed(KeyCode::ArrowLeft) { if input.just_pressed(KeyCode::ArrowLeft) {
transform.rotate_z(PI / 2.0); transform.rotate_z(PI / 2.0);
direction.rotate_left(); direction.rotate_left();
snake_path.push(&transform, direction.clone()); append_snake_path(&transform, &direction);
} }
if input.just_pressed(KeyCode::ArrowRight) { if input.just_pressed(KeyCode::ArrowRight) {
transform.rotate_z(-PI / 2.0); transform.rotate_z(-PI / 2.0);
direction.rotate_right(); direction.rotate_right();
snake_path.push(&transform, direction.clone()); append_snake_path(&transform, &direction);
} }
} }
+4 -40
View File
@@ -1,46 +1,10 @@
/// Инициализация логирования pub mod debug;
pub fn define_log_layer() -> Option<bevy::log::BoxedFmtLayer> {
use std::fs::File;
use tracing::Level;
use tracing_subscriber::{Layer, filter::Targets, fmt::layer};
let targets = Targets::new().with_default(Level::TRACE);
let stdout = layer()
.compact()
.with_thread_names(true)
.with_file(false)
.with_line_number(false)
.with_filter(targets.clone())
.boxed();
let file = match File::options()
.write(true)
.create(true)
.append(true)
.open("snake-game.log")
{
Ok(file) => file,
Err(e) => {
eprintln!("No access to log file: {:?}", e);
return Some(stdout);
}
};
let log_file = layer()
.compact()
.with_ansi(false)
.with_thread_names(true)
.with_file(false)
.with_line_number(false)
.with_writer(file)
.with_filter(targets.clone())
.boxed();
Some(Box::new(vec![stdout, log_file]))
}
mod domain; mod domain;
mod gameplay; mod gameplay;
mod observers;
mod startup; mod startup;
mod tools; mod tools;
pub use gameplay::{head_rotation, moving}; pub use gameplay::{head_rotation, moving};
pub use startup::{print_all_entities, setup_frame_rate, startup}; pub use startup::{setup_frame_rate, startup};
pub use tools::define_log_layer;
+5 -1
View File
@@ -19,7 +19,11 @@ fn main() {
.insert_resource(bevy::winit::WinitSettings::desktop_app()) .insert_resource(bevy::winit::WinitSettings::desktop_app())
.insert_resource(ClearColor(Color::srgb(0.3, 0.6, 0.8))) .insert_resource(ClearColor(Color::srgb(0.3, 0.6, 0.8)))
.add_systems(Startup, (startup, setup_frame_rate)) .add_systems(Startup, (startup, setup_frame_rate))
// .add_systems(PostStartup, snake_game::print_all_entities) // .add_systems(PostStartup, snake_game::debug::print_all_entities)
.add_systems(Update, (head_rotation, moving)) .add_systems(Update, (head_rotation, moving))
// .add_systems(
// PostUpdate,
// snake_game::debug::print_all_entities_by_press_enter,
// )
.run(); .run();
} }
+55
View File
@@ -0,0 +1,55 @@
use crate::domain::*;
use bevy::prelude::*;
#[allow(unused)]
/// Слушатель событий [GridBarier] для хвоста, обновляющий направление его движения из ресурса [SnakePath] и позволяющий следовать за головой
pub fn tail_observer(
event: On<GridBarier>,
mut query: Query<(&mut Direction, &Transform)>,
snake_path: Res<SnakePath>,
) {
trace!("...changing direction: triggered");
if event.observer() == event.0 {
trace!("...changing direction: observer == event.0");
if let Ok((mut direction, transform)) = query.get_mut(event.0) {
trace!("...changing direction: query success");
if let Some(new_direction) = snake_path.pick(transform) {
trace!("change direction for {:?} to {:?}", event.0, 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.0) {
// 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((x, y, dir)) = snake_path.drop(&transform) {
trace!(
"removed entry from SnakePath: ({:?}, {:?}) => {:?}\n\tsnake_path: {:?}",
x, y, dir, *snake_path
);
}
});
}
}
/// Запланировать установку скорости движения [Velocity] при следующем событии [GridBarier]
pub fn schedule_set_velocity(tail: Entity) -> impl Fn(On<GridBarier>, Commands) {
move |event: On<GridBarier>, mut commands: Commands| {
commands
.entity(tail)
.entry::<Velocity>()
.and_modify(|mut velocity| velocity.0 = SNAKE_VELOCITY as f32);
commands.entity(event.observer()).despawn();
}
}
+5 -41
View File
@@ -2,8 +2,8 @@ use bevy::{prelude::*, winit::WinitSettings};
use std::time::Duration; use std::time::Duration;
use crate::{ use crate::{
domain::{End, Grid, SnakePath, Tail}, domain::{End, SnakePath, Tail},
tools::{end_observer, schedule_set_velocity}, observers::{cleanup_snake_path, schedule_set_velocity, tail_observer},
}; };
/// Настройка частоты кадров /// Настройка частоты кадров
@@ -19,7 +19,7 @@ pub fn setup_frame_rate(mut winit: ResMut<WinitSettings>) {
/// Инициализация игрового мира /// Инициализация игрового мира
pub fn startup(mut commands: Commands) { pub fn startup(mut commands: Commands) {
spawn_snake(&mut commands); spawn_snake(&mut commands);
spawn_grid(&mut commands); crate::debug::spawn_grid(&mut commands);
commands.spawn(Camera2d); commands.spawn(Camera2d);
} }
@@ -28,6 +28,7 @@ fn spawn_snake(commands: &mut Commands) {
use crate::domain::{Direction, HEAD_SIZE, Head, Inert, SNAKE_VELOCITY, Velocity}; use crate::domain::{Direction, HEAD_SIZE, Head, Inert, SNAKE_VELOCITY, Velocity};
commands.insert_resource(SnakePath::default()); commands.insert_resource(SnakePath::default());
commands.spawn(Observer::new(cleanup_snake_path));
let head = commands let head = commands
.spawn(( .spawn((
@@ -83,45 +84,8 @@ fn spawn_snake(commands: &mut Commands) {
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));
commands.entity(tail).observe(end_observer);
}
/// Создать координатную сетку
fn spawn_grid(commands: &mut Commands) {
use crate::domain::GRID_SIZE;
let grid = commands
.spawn((Grid, Transform::default(), Visibility::default()))
.id();
for x in -20..=20 {
commands.spawn((
Sprite::from_color(Color::srgb(1.0, 1.0, 0.5), vec2(0.5, 2000.0)),
Transform {
translation: vec3(x as f32 * GRID_SIZE as f32, 0.0, -1.0),
..Default::default()
},
ChildOf(grid),
));
}
for y in -20..=20 {
commands.spawn((
Sprite::from_color(Color::srgb(1.0, 1.0, 0.5), vec2(2000.0, 0.5)),
Transform {
translation: vec3(0.0, y as f32 * GRID_SIZE as f32, -1.0),
..Default::default()
},
ChildOf(grid),
));
}
}
/// ДЛЯ ОТЛАДКИ - вывод всех сущностей мира
pub fn print_all_entities(mut commands: Commands, query: Query<Entity>) {
for entity in query {
commands.entity(entity).log_components();
}
} }
+37 -48
View File
@@ -1,6 +1,41 @@
use bevy::{ecs::observer::ObservedBy, prelude::*}; /// Инициализация логирования
pub fn define_log_layer() -> Option<bevy::log::BoxedFmtLayer> {
use std::fs::File;
use tracing::Level;
use tracing_subscriber::{Layer, filter::Targets, fmt::layer};
use crate::domain::*; let targets = Targets::new().with_default(Level::TRACE);
let stdout = layer()
.compact()
.with_thread_names(true)
.with_file(false)
.with_line_number(false)
.with_filter(targets.clone())
.boxed();
let file = match File::options()
.write(true)
.create(true)
.append(true)
.open("snake-game.log")
{
Ok(file) => file,
Err(e) => {
eprintln!("No access to log file: {:?}", e);
return Some(stdout);
}
};
let log_file = layer()
.compact()
.with_ansi(false)
.with_thread_names(true)
.with_file(false)
.with_line_number(false)
.with_writer(file)
.with_filter(targets.clone())
.boxed();
Some(Box::new(vec![stdout, log_file]))
}
/// Разбиение изменения координаты по границам сетки /// Разбиение изменения координаты по границам сетки
pub fn split_by_grid(src: f32, dst: f32, grid: f32) -> Vec<(f32, f32)> { pub fn split_by_grid(src: f32, dst: f32, grid: f32) -> Vec<(f32, f32)> {
@@ -40,52 +75,6 @@ pub fn split_by_grid(src: f32, dst: f32, grid: f32) -> Vec<(f32, f32)> {
output output
} }
#[allow(unused)]
/// Слушатель событий [GridBarier] для хвоста, обновляющий направление его движения из ресурса [SnakePath]
pub fn tail_observer(
event: On<GridBarier>,
mut query: Query<(&mut Direction, &Transform), Without<End>>,
snake_path: Res<SnakePath>,
) {
if let Ok((mut direction, transform)) = query.get_mut(event.0) {
if let Some(new_direction) = snake_path.pick(transform) {
trace!("change direction for {:?} to {:?}", event.0, new_direction);
*direction = new_direction;
}
}
}
/// Слушатель событий [GridBarier] для конца змеи, обновляющий направление его движения из ресурса [SnakePath] и удаляющий полученные значения из [SnakePath]
pub fn end_observer(
event: On<GridBarier>,
mut query: Query<(&mut Direction, &Transform), With<End>>,
mut snake_path: ResMut<SnakePath>,
) {
if let Ok((mut direction, transform)) = query.get_mut(event.0) {
if let Some(new_direction) = snake_path.pop(transform) {
trace!(
"change direction for {:?} to {:?} and remove it from SnakePath",
event.0, new_direction
);
*direction = new_direction;
}
}
}
/// Запланировать установку скорости движения [Velocity] при следующем событии [GridBarier]
pub fn schedule_set_velocity(
tail: Entity,
) -> impl Fn(On<GridBarier>, Commands, Query<&mut Velocity, With<Tail>>) {
move |event: On<GridBarier>,
mut commands: Commands,
mut query: Query<&mut Velocity, With<Tail>>| {
if let Ok(mut velocity) = query.get_mut(tail) {
velocity.0 = SNAKE_VELOCITY as f32;
}
commands.entity(event.0).remove::<ObservedBy>();
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::tools::*; use crate::tools::*;