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

This commit is contained in:
8 changed files with 403 additions and 30 deletions
+64 -2
View File
@@ -1,3 +1,5 @@
use std::collections::HashMap;
use bevy::prelude::*;
pub const FRAMES_PER_SECOND: u8 = 24;
@@ -5,16 +7,28 @@ pub const GRID_SIZE: u8 = 48;
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;
/// Координатная сетка
#[derive(Component)]
pub struct Grid;
/// Голова змеи
#[derive(Component)]
pub struct Head;
/// Сегмент змеиного хвоста
#[derive(Component)]
pub struct Tail(pub u16);
pub struct Tail;
/// Конец змеиного хвоста
#[derive(Component)]
pub struct End;
/// Событие прохождения границы сетки
#[derive(EntityEvent)]
pub struct GridBarier(pub Entity);
/// Направление движения
#[derive(Component, PartialEq, Clone, Copy)]
#[derive(Component, PartialEq, Clone, Copy, Debug)]
pub enum Direction {
Up,
Left,
@@ -81,3 +95,51 @@ pub struct Velocity(pub f32);
/// Инерция
#[derive(Component, Default)]
pub struct Inert(pub Direction);
/// Путь змеи
#[derive(Resource, Default, Debug)]
pub struct SnakePath(HashMap<(i16, i16), Direction>);
impl SnakePath {
fn to_key(transform: &Transform) -> (i16, i16) {
(
(transform.translation.x / GRID_SIZE as f32).floor() as i16,
(transform.translation.y / GRID_SIZE as f32).floor() as i16,
)
}
pub fn push(&mut self, transform: &Transform, direction: Direction) {
let key = SnakePath::to_key(transform);
self.0.insert(key, direction.clone());
}
pub fn pick(&self, transform: &Transform) -> Option<Direction> {
let key = SnakePath::to_key(transform);
self.0.get(&key).cloned()
}
pub fn pop(&mut self, transform: &Transform) -> Option<Direction> {
let key = SnakePath::to_key(transform);
self.0.remove(&key)
}
}
#[cfg(test)]
mod test {
use crate::domain::*;
use std::collections::HashMap;
#[test]
fn test_snake_path() {
let mut snake_path = SnakePath(HashMap::default());
snake_path.push(&Transform::default(), Direction::default());
assert_eq!(snake_path.0.len(), 1);
assert!(snake_path.pick(&Transform::default()) == Some(Direction::default()));
assert_eq!(snake_path.0.len(), 1);
assert!(snake_path.pop(&Transform::default()) == Some(Direction::default()));
assert_eq!(snake_path.0.len(), 0);
}
}
+27 -7
View File
@@ -1,27 +1,36 @@
use bevy::prelude::*;
use crate::domain::{Direction, GRID_SIZE, Head, Inert, Velocity};
use crate::domain::{Direction, GRID_SIZE, GridBarier, Head, Inert, SnakePath, Velocity};
/// Система поворота головы змеи
pub fn head_rotation(
input: Res<ButtonInput<KeyCode>>,
query: Single<(&mut Transform, &mut Direction), With<Head>>,
mut snake_path: ResMut<SnakePath>,
) {
use core::f32::consts::PI;
let (mut transform, mut direction) = query.into_inner();
if input.just_pressed(KeyCode::ArrowLeft) {
transform.rotate_z(PI / 2.);
transform.rotate_z(PI / 2.0);
direction.rotate_left();
snake_path.push(&transform, direction.clone());
}
if input.just_pressed(KeyCode::ArrowRight) {
transform.rotate_z(-PI / 2.);
transform.rotate_z(-PI / 2.0);
direction.rotate_right();
snake_path.push(&transform, direction.clone());
}
}
pub fn moving(time: Res<Time>, query: Query<(&mut Transform, &mut Inert, &Direction, &Velocity)>) {
for (mut transform, mut inert, direction, Velocity(velocity)) in query.into_iter() {
/// Системе движения для всех движущихся сущностей
pub fn moving(
time: Res<Time>,
query: Query<(&mut Transform, &mut Inert, &Direction, &Velocity, Entity)>,
mut commands: Commands,
) {
for (mut transform, mut inert, direction, Velocity(velocity), entity) in query.into_iter() {
let delta = velocity * time.delta_secs();
let src = inert.0.get_coord(&transform);
let sign = inert.0.get_change_sign();
@@ -36,6 +45,7 @@ pub fn moving(time: Res<Time>, query: Query<(&mut Transform, &mut Inert, &Direct
let sign = inert.0.get_change_sign();
let dst = src + sign * delta;
inert.0.set_coord(&mut transform, dst);
commands.trigger(GridBarier(entity));
}
}
}
@@ -57,7 +67,9 @@ fn split_by_grid(src: f32, dst: f32, grid: f32) -> Vec<(f32, f32)> {
let mut src: f32 = src;
for step in old_grid_right as i32..new_grid_right as i32 {
let step = step as f32 * grid;
output.push((step, step - src));
if step != src {
output.push((step, step - src));
}
src = step;
}
output.push((dst, dst - src));
@@ -65,7 +77,9 @@ fn split_by_grid(src: f32, dst: f32, grid: f32) -> Vec<(f32, f32)> {
let mut dst: f32 = dst;
for step in new_grid_right as i32..old_grid_right as i32 {
let step = step as f32 * grid;
output.push((dst, dst - step));
if step != dst {
output.push((dst, dst - step));
}
dst = step;
}
output.push((dst, dst - src));
@@ -127,5 +141,11 @@ mod test {
// сценарии с нулевой сеткой
assert_eq!(split_by_grid(1.0, 4.0, 0.0), vec![(4.0, 3.0)]);
assert_eq!(split_by_grid(4.0, 1.0, 0.0), vec![(1.0, -3.0)]);
// Сценарии со стартом в узле сетки
assert_eq!(split_by_grid(0.0, 1.5, 5.0), vec![(1.5, 1.5)]);
assert_eq!(split_by_grid(5.0, 7.0, 5.0), vec![(7.0, 2.0)]);
assert_eq!(split_by_grid(0.0, -1.5, 5.0), vec![(-1.5, -1.5)]);
assert_eq!(split_by_grid(5.0, 3.0, 5.0), vec![(3.0, -2.0)]);
}
}
+1 -1
View File
@@ -42,4 +42,4 @@ mod gameplay;
mod startup;
pub use gameplay::{head_rotation, moving};
pub use startup::{setup_frame_rate, startup};
pub use startup::{print_all_entities, setup_frame_rate, startup};
+1
View File
@@ -19,6 +19,7 @@ fn main() {
.insert_resource(bevy::winit::WinitSettings::desktop_app())
.insert_resource(ClearColor(Color::srgb(0.3, 0.6, 0.8)))
.add_systems(Startup, (startup, setup_frame_rate))
// .add_systems(PostStartup, snake_game::print_all_entities)
.add_systems(Update, (head_rotation, moving))
.run();
}
+46 -19
View File
@@ -1,8 +1,7 @@
use bevy::{ecs::observer::ObservedBy, prelude::*, winit::WinitSettings};
use std::time::Duration;
use bevy::{prelude::*, winit::WinitSettings};
use crate::domain::Tail;
use crate::domain::{End, Grid, GridBarier, SnakePath, Tail};
/// Настройка частоты кадров
pub fn setup_frame_rate(mut winit: ResMut<WinitSettings>) {
@@ -25,6 +24,8 @@ pub fn startup(mut commands: Commands) {
fn spawn_snake(commands: &mut Commands) {
use crate::domain::{Direction, HEAD_SIZE, Head, Inert, SNAKE_VELOCITY, Velocity};
commands.insert_resource(SnakePath::default());
let head = commands
.spawn((
Head,
@@ -64,43 +65,69 @@ fn spawn_snake(commands: &mut Commands) {
ChildOf(head),
));
commands.spawn((
Tail(0),
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()
let tail = commands
.spawn((
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()
},
))
.id();
commands.entity(head).observe(
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>();
},
// ChildOf(head),
));
);
}
/// Создать координатную сетку
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(1.0, 2000.0)),
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, 1.0)),
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();
}
}