diff --git a/snake-game/examples/bevy_learning.rs b/snake-game/examples/bevy_learning.rs index 8a51ba9..346289b 100644 --- a/snake-game/examples/bevy_learning.rs +++ b/snake-game/examples/bevy_learning.rs @@ -12,7 +12,7 @@ fn main() { .set(bevy::log::LogPlugin { level: Level::INFO, filter: "wgpu_hal=warn,bevy_learning=trace".to_string(), - fmt_layer: |_| snake_game::define_log_layer(), + // fmt_layer: |_| snake_game::define_log_layer(), ..Default::default() }) .set(TaskPoolPlugin { diff --git a/snake-game/src/core.rs b/snake-game/src/core.rs new file mode 100644 index 0000000..f797b14 --- /dev/null +++ b/snake-game/src/core.rs @@ -0,0 +1,87 @@ +use bevy::{color::palettes::tailwind::*, prelude::*}; + +use crate::{ + domain::{End, FIELD_SIZE, GRID_SIZE, Tail}, + observers::schedule_set_velocity, +}; + +/// Создать границу игрового поля +pub fn spawn_bounds(commands: &mut Commands) { + commands.spawn(( + Sprite::from_color(AMBER_500, vec2((FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32, 5.0)), // + Transform { + translation: vec3(0.0, (FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0), + ..Default::default() + }, + )); + commands.spawn(( + Sprite::from_color(AMBER_500, vec2((FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32, 5.0)), // + Transform { + translation: vec3(0.0, -(FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0), + ..Default::default() + }, + )); + commands.spawn(( + Sprite::from_color(AMBER_500, vec2(5.0, (FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32)), // + Transform { + translation: vec3((FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0, 0.0), + ..Default::default() + }, + )); + commands.spawn(( + Sprite::from_color(AMBER_500, vec2(5.0, (FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32)), // + Transform { + translation: vec3(-(FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0, 0.0), + ..Default::default() + }, + )); +} + +/// Создать змейку +pub fn spawn_snake(commands: &mut Commands) { + use crate::domain::{Direction, HEAD_SIZE, Head, SNAKE_VELOCITY, Velocity}; + + let head = commands + .spawn(( + Head(Direction::default()), + Direction::default(), + Velocity(SNAKE_VELOCITY as f32), + Sprite::from_color(Color::srgb(1., 1., 1.), vec2(HEAD_SIZE as f32, HEAD_SIZE as f32)), + Transform { + translation: vec3(0.0, 0.0, 1.0), + ..Default::default() + }, + )) + .id(); + commands.spawn(( + Sprite::from_color(Color::srgb(1., 0., 0.), vec2(HEAD_SIZE as f32 * 0.3, HEAD_SIZE as f32 * 0.3)), + Transform { + translation: vec3(HEAD_SIZE as f32 * -0.25, HEAD_SIZE as f32 * 0.25, 1.0), + ..Default::default() + }, + ChildOf(head), + )); + commands.spawn(( + Sprite::from_color(Color::srgb(0., 0., 1.), vec2(HEAD_SIZE as f32 * 0.2, HEAD_SIZE as f32 * 0.2)), + Transform { + translation: vec3(HEAD_SIZE as f32 * 0.25, HEAD_SIZE as f32 * 0.25, 1.0), + ..Default::default() + }, + ChildOf(head), + )); + + let tail = commands + .spawn(( + Tail(head), + End, + Direction::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() + }, + )) + .id(); + commands.entity(head).observe(schedule_set_velocity(tail)); +} diff --git a/snake-game/src/debug.rs b/snake-game/src/debug.rs index 388fed3..c20cee0 100644 --- a/snake-game/src/debug.rs +++ b/snake-game/src/debug.rs @@ -1,4 +1,5 @@ //! модуль с инструментами отладки +#![allow(unused)] use bevy::{color::palettes::tailwind::*, prelude::*}; diff --git a/snake-game/src/domain/direction.rs b/snake-game/src/domain/direction.rs index fb9ff08..6efb60e 100644 --- a/snake-game/src/domain/direction.rs +++ b/snake-game/src/domain/direction.rs @@ -1,7 +1,5 @@ use bevy::prelude::*; -use crate::domain::Position; - /// Направление движения #[derive(Component, PartialEq, Clone, Copy, Debug)] pub enum Direction { @@ -56,18 +54,6 @@ impl Direction { } } - /// Получить следующую позицию для текущего направления - pub fn dst_pos(&self, transform: &Transform) -> Position { - let mut pos = Position::from(transform); - match self { - Direction::Up => pos.y += 1, - Direction::Left => (), - Direction::Down => (), - Direction::Right => pos.x += 1, - }; - pos - } - /// Получить ориентацию в пространстве pub fn orientation(&self) -> Quat { use std::f32::consts::PI; diff --git a/snake-game/src/domain/position.rs b/snake-game/src/domain/position.rs index a0224da..ac5f0fa 100644 --- a/snake-game/src/domain/position.rs +++ b/snake-game/src/domain/position.rs @@ -5,7 +5,7 @@ use bevy::{ transform::components::Transform, }; -use crate::domain::{Direction, GRID_SIZE}; +use crate::domain::GRID_SIZE; #[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Position { @@ -18,15 +18,6 @@ impl Position { Self { x, y } } - pub fn shift(&mut self, direction: &Direction) { - match direction { - Direction::Up => self.y += 1, - Direction::Left => self.x -= 1, - Direction::Down => self.y -= 1, - Direction::Right => self.x += 1, - } - } - pub fn translation(&self) -> Vec2 { vec2(self.x as f32 * GRID_SIZE as f32, self.y as f32 * GRID_SIZE as f32) } diff --git a/snake-game/src/game.rs b/snake-game/src/game.rs new file mode 100644 index 0000000..cd70f4a --- /dev/null +++ b/snake-game/src/game.rs @@ -0,0 +1,42 @@ +use bevy::prelude::*; + +pub struct SnakeGame; + +impl Plugin for SnakeGame { + fn build(&self, app: &mut bevy::app::App) { + app.add_plugins( + DefaultPlugins + .build() + .set(bevy::log::LogPlugin { + level: tracing::Level::INFO, + filter: "wgpu_hal=warn,snake_game=trace".to_string(), + fmt_layer: |_| crate::tools::define_log_layer(), + ..Default::default() + }) + .set(TaskPoolPlugin { + task_pool_options: TaskPoolOptions::with_num_threads(1), + }), + ); + + app.add_systems(Startup, crate::startup::setup_window); + app.add_systems(Startup, crate::startup::setup_time); + app.add_systems(Startup, crate::startup::startup); + + app.add_systems(Update, crate::gameplay::head_rotation); + + app.add_systems(PostUpdate, crate::gameplay::check_bounds_colisions); + app.add_systems(PostUpdate, crate::gameplay::check_tail_colisions); + app.add_systems(PostUpdate, crate::gameplay::check_meal_collisions); + + app.add_systems(FixedUpdate, crate::gameplay::head_moving); + app.add_systems(FixedUpdate, crate::gameplay::snake_moving); + + // DEBUG: + + // app.add_systems(PostStartup, crate::debug::print_all_entities); + app.add_systems(Startup, crate::debug::set_initial_time_speed_to_slow_and_pause); + app.add_systems(Update, crate::debug::grow_up_on_tab); + app.add_systems(Update, crate::debug::print_all_entities_by_press_enter); + app.add_systems(Update, crate::debug::time_speed); + } +} diff --git a/snake-game/src/game/core.rs b/snake-game/src/game/core.rs new file mode 100644 index 0000000..091891f --- /dev/null +++ b/snake-game/src/game/core.rs @@ -0,0 +1,90 @@ +use bevy::{color::palettes::tailwind::*, prelude::*}; + +use crate::{ + domain::{End, FIELD_SIZE, GRID_SIZE, Tail}, + observers::schedule_set_velocity, +}; + +/// Создать границу игрового поля +pub fn spawn_bounds(commands: &mut Commands) { + commands.spawn(( + Sprite::from_color(AMBER_500, vec2((FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32, 5.0)), // + Transform { + translation: vec3(0.0, (FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0), + ..Default::default() + }, + )); + commands.spawn(( + Sprite::from_color(AMBER_500, vec2((FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32, 5.0)), // + Transform { + translation: vec3(0.0, -(FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0), + ..Default::default() + }, + )); + commands.spawn(( + Sprite::from_color(AMBER_500, vec2(5.0, (FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32)), // + Transform { + translation: vec3((FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0, 0.0), + ..Default::default() + }, + )); + commands.spawn(( + Sprite::from_color(AMBER_500, vec2(5.0, (FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32)), // + Transform { + translation: vec3(-(FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0, 0.0), + ..Default::default() + }, + )); +} + +/// Создать змейку +pub fn spawn_snake(commands: &mut Commands) { + use crate::domain::{Direction, HEAD_SIZE, Head, SNAKE_VELOCITY, Velocity}; + + commands.spawn(Observer::new(crate::debug::trace_snake_moving)); + commands.spawn(Observer::new(crate::observers::handle_collisions)); + + let head = commands + .spawn(( + Head(Direction::default()), + Direction::default(), + Velocity(SNAKE_VELOCITY as f32), + Sprite::from_color(Color::srgb(1., 1., 1.), vec2(HEAD_SIZE as f32, HEAD_SIZE as f32)), + Transform { + translation: vec3(0.0, 0.0, 1.0), + ..Default::default() + }, + )) + .id(); + commands.spawn(( + Sprite::from_color(Color::srgb(1., 0., 0.), vec2(HEAD_SIZE as f32 * 0.3, HEAD_SIZE as f32 * 0.3)), + Transform { + translation: vec3(HEAD_SIZE as f32 * -0.25, HEAD_SIZE as f32 * 0.25, 1.0), + ..Default::default() + }, + ChildOf(head), + )); + commands.spawn(( + Sprite::from_color(Color::srgb(0., 0., 1.), vec2(HEAD_SIZE as f32 * 0.2, HEAD_SIZE as f32 * 0.2)), + Transform { + translation: vec3(HEAD_SIZE as f32 * 0.25, HEAD_SIZE as f32 * 0.25, 1.0), + ..Default::default() + }, + ChildOf(head), + )); + + let tail = commands + .spawn(( + Tail(head), + End, + Direction::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() + }, + )) + .id(); + commands.entity(head).observe(schedule_set_velocity(tail)); +} diff --git a/snake-game/src/gameplay.rs b/snake-game/src/gameplay.rs index 025d37d..fe41740 100644 --- a/snake-game/src/gameplay.rs +++ b/snake-game/src/gameplay.rs @@ -102,7 +102,7 @@ pub fn snake_moving( snake_path: Res, mut commands: Commands, ) { - let _span = trace_span!("off_head_snake_moving").entered(); + let _span = trace_span!("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(); diff --git a/snake-game/src/lib.rs b/snake-game/src/lib.rs index 1f5835e..253fb20 100644 --- a/snake-game/src/lib.rs +++ b/snake-game/src/lib.rs @@ -1,10 +1,10 @@ -pub mod debug; +mod core; +mod debug; mod domain; +mod game; mod gameplay; mod observers; mod startup; mod tools; -pub use gameplay::{check_bounds_colisions, check_meal_collisions, check_tail_colisions, head_moving, head_rotation, snake_moving}; -pub use startup::{setup_window, startup}; -pub use tools::define_log_layer; +pub use game::SnakeGame; diff --git a/snake-game/src/main.rs b/snake-game/src/main.rs index a9af779..9153109 100644 --- a/snake-game/src/main.rs +++ b/snake-game/src/main.rs @@ -1,57 +1,3 @@ -use bevy::prelude::*; -use snake_game::{check_bounds_colisions, check_meal_collisions, check_tail_colisions, head_moving, head_rotation, setup_window, snake_moving, startup}; - fn main() { - App::new() - .add_plugins( - DefaultPlugins - .build() - .set(bevy::log::LogPlugin { - level: tracing::Level::INFO, - filter: "wgpu_hal=warn,snake_game=trace".to_string(), - fmt_layer: |_| snake_game::define_log_layer(), - ..Default::default() - }) - .set(TaskPoolPlugin { - task_pool_options: TaskPoolOptions::with_num_threads(1), - }), - ) - .insert_resource(ClearColor(Color::srgb(0.3, 0.6, 0.8))) - .add_systems( - Startup, - ( - || {}, // - setup_window, - startup, - snake_game::debug::set_initial_time_speed_to_slow_and_pause, - ), - ) - .add_systems( - PostStartup, - ( - || {}, - // snake_game::debug::print_all_entities, - ), - ) - .add_systems( - Update, - ( - || {}, // - head_rotation, - snake_game::debug::grow_up_on_tab, - ), - ) - .add_systems(FixedUpdate, (head_moving, snake_moving)) - .add_systems( - PostUpdate, - ( - || {}, // - check_bounds_colisions, - check_tail_colisions, - check_meal_collisions, - snake_game::debug::print_all_entities_by_press_enter, - snake_game::debug::time_speed, - ), - ) - .run(); + bevy::app::App::new().add_plugins(snake_game::SnakeGame).run(); } diff --git a/snake-game/src/startup.rs b/snake-game/src/startup.rs index caa1c02..f27d5bf 100644 --- a/snake-game/src/startup.rs +++ b/snake-game/src/startup.rs @@ -1,10 +1,8 @@ use bevy::{color::palettes::tailwind::*, prelude::*}; -use crate::{ - domain::{End, FIELD_SIZE, GRID_SIZE, SnakePath, Tail}, - observers::schedule_set_velocity, -}; +use crate::domain::{FIELD_SIZE, GRID_SIZE, SnakePath}; +/// Настройка игрового окна pub fn setup_window(mut window: Single<&mut Window>) { window.resolution.set( (FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 + 20.0, @@ -12,106 +10,30 @@ pub fn setup_window(mut window: Single<&mut Window>) { ); } -/// Инициализация игрового мира -pub fn startup(mut commands: Commands, mut time: ResMut>) { +/// Настройка игрового времени +pub fn setup_time(mut time: ResMut>) { time.set_timestep_hz(32.0); +} +/// Инициализация игрового мира +pub fn startup(mut commands: Commands) { + commands.insert_resource(ClearColor(SKY_600.into())); commands.insert_resource(SnakePath::default()); commands.spawn(Observer::new(crate::observers::clean_snake_path)); commands.spawn(Observer::new(crate::observers::grow_up_snake)); + commands.spawn(Observer::new(crate::observers::handle_collisions)); + + crate::core::spawn_bounds(&mut commands); + crate::core::spawn_snake(&mut commands); + + commands.spawn(Camera2d); + + // DEBUG: + commands.spawn(Observer::new(crate::debug::trace_collisions)); - - spawn_bounds(&mut commands); - - spawn_snake(&mut commands); + commands.spawn(Observer::new(crate::debug::trace_snake_moving)); crate::debug::spawn_grid(&mut commands); crate::debug::spawn_some_meal(&mut commands); - - commands.spawn(Camera2d); -} - -/// Создать границу игрового поля -fn spawn_bounds(commands: &mut Commands) { - commands.spawn(( - Sprite::from_color(AMBER_500, vec2((FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32, 5.0)), // - Transform { - translation: vec3(0.0, (FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0), - ..Default::default() - }, - )); - commands.spawn(( - Sprite::from_color(AMBER_500, vec2((FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32, 5.0)), // - Transform { - translation: vec3(0.0, -(FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0), - ..Default::default() - }, - )); - commands.spawn(( - Sprite::from_color(AMBER_500, vec2(5.0, (FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32)), // - Transform { - translation: vec3((FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0, 0.0), - ..Default::default() - }, - )); - commands.spawn(( - Sprite::from_color(AMBER_500, vec2(5.0, (FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32)), // - Transform { - translation: vec3(-(FIELD_SIZE as f32 + 1.0) * GRID_SIZE as f32 / 2.0, 0.0, 0.0), - ..Default::default() - }, - )); -} - -/// Создать голову змеи -fn spawn_snake(commands: &mut Commands) { - use crate::domain::{Direction, HEAD_SIZE, Head, SNAKE_VELOCITY, Velocity}; - - commands.spawn(Observer::new(crate::debug::trace_snake_moving)); - commands.spawn(Observer::new(crate::observers::handle_collisions)); - - let head = commands - .spawn(( - Head(Direction::default()), - Direction::default(), - Velocity(SNAKE_VELOCITY as f32), - Sprite::from_color(Color::srgb(1., 1., 1.), vec2(HEAD_SIZE as f32, HEAD_SIZE as f32)), - Transform { - translation: vec3(0.0, 0.0, 1.0), - ..Default::default() - }, - )) - .id(); - commands.spawn(( - Sprite::from_color(Color::srgb(1., 0., 0.), vec2(HEAD_SIZE as f32 * 0.3, HEAD_SIZE as f32 * 0.3)), - Transform { - translation: vec3(HEAD_SIZE as f32 * -0.25, HEAD_SIZE as f32 * 0.25, 1.0), - ..Default::default() - }, - ChildOf(head), - )); - commands.spawn(( - Sprite::from_color(Color::srgb(0., 0., 1.), vec2(HEAD_SIZE as f32 * 0.2, HEAD_SIZE as f32 * 0.2)), - Transform { - translation: vec3(HEAD_SIZE as f32 * 0.25, HEAD_SIZE as f32 * 0.25, 1.0), - ..Default::default() - }, - ChildOf(head), - )); - - let tail = commands - .spawn(( - Tail(head), - End, - Direction::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() - }, - )) - .id(); - commands.entity(head).observe(schedule_set_velocity(tail)); }