Проектная работа - WIP
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugins(HelloPlugin)
|
||||
.run();
|
||||
App::new().add_plugins(DefaultPlugins).add_plugins(HelloPlugin).run();
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
|
||||
@@ -99,11 +99,7 @@ fn draw(
|
||||
) {
|
||||
if *i == 0 {
|
||||
// Generate a random color on first run.
|
||||
*draw_color = Color::linear_rgb(
|
||||
seeded_rng.0.random(),
|
||||
seeded_rng.0.random(),
|
||||
seeded_rng.0.random(),
|
||||
);
|
||||
*draw_color = Color::linear_rgb(seeded_rng.0.random(), seeded_rng.0.random(), seeded_rng.0.random());
|
||||
}
|
||||
|
||||
// Get the image from Bevy's asset storage.
|
||||
@@ -126,17 +122,11 @@ fn draw(
|
||||
// If the old color is our current color, change our drawing color.
|
||||
let tolerance = 1.0 / 255.0;
|
||||
if old_color.distance(&draw_color) <= tolerance {
|
||||
*draw_color = Color::linear_rgb(
|
||||
seeded_rng.0.random(),
|
||||
seeded_rng.0.random(),
|
||||
seeded_rng.0.random(),
|
||||
);
|
||||
*draw_color = Color::linear_rgb(seeded_rng.0.random(), seeded_rng.0.random(), seeded_rng.0.random());
|
||||
}
|
||||
|
||||
// Set the new color, but keep old alpha value from image.
|
||||
image
|
||||
.set_color_at(x, y, draw_color.with_alpha(old_color.alpha()))
|
||||
.unwrap();
|
||||
image.set_color_at(x, y, draw_color.with_alpha(old_color.alpha())).unwrap();
|
||||
|
||||
*i += 1;
|
||||
}
|
||||
|
||||
@@ -14,10 +14,7 @@ fn main() {
|
||||
// Observers are systems that run when an event is "triggered". This observer runs whenever
|
||||
// `ExplodeMines` is triggered.
|
||||
.add_observer(
|
||||
|explode_mines: On<ExplodeMines>,
|
||||
mines: Query<&Mine>,
|
||||
index: Res<SpatialIndex>,
|
||||
mut commands: Commands| {
|
||||
|explode_mines: On<ExplodeMines>, mines: Query<&Mine>, index: Res<SpatialIndex>, mut commands: Commands| {
|
||||
// Access resources
|
||||
for entity in index.get_nearby(explode_mines.pos) {
|
||||
// Run queries
|
||||
@@ -47,10 +44,7 @@ struct Mine {
|
||||
impl Mine {
|
||||
fn random(rand: &mut ChaCha8Rng) -> Self {
|
||||
Mine {
|
||||
pos: Vec2::new(
|
||||
(rand.random::<f32>() - 0.5) * 1200.0,
|
||||
(rand.random::<f32>() - 0.5) * 600.0,
|
||||
),
|
||||
pos: Vec2::new((rand.random::<f32>() - 0.5) * 1200.0, (rand.random::<f32>() - 0.5) * 600.0),
|
||||
size: 4.0 + rand.random::<f32>() * 16.0,
|
||||
}
|
||||
}
|
||||
@@ -116,20 +110,14 @@ fn setup(mut commands: Commands) {
|
||||
|
||||
fn on_add_mine(add: On<Add, Mine>, query: Query<&Mine>, mut index: ResMut<SpatialIndex>) {
|
||||
let mine = query.get(add.entity).unwrap();
|
||||
let tile = (
|
||||
(mine.pos.x / CELL_SIZE).floor() as i32,
|
||||
(mine.pos.y / CELL_SIZE).floor() as i32,
|
||||
);
|
||||
let tile = ((mine.pos.x / CELL_SIZE).floor() as i32, (mine.pos.y / CELL_SIZE).floor() as i32);
|
||||
index.map.entry(tile).or_default().insert(add.entity);
|
||||
}
|
||||
|
||||
// Remove despawned mines from our index
|
||||
fn on_remove_mine(remove: On<Remove, Mine>, query: Query<&Mine>, mut index: ResMut<SpatialIndex>) {
|
||||
let mine = query.get(remove.entity).unwrap();
|
||||
let tile = (
|
||||
(mine.pos.x / CELL_SIZE).floor() as i32,
|
||||
(mine.pos.y / CELL_SIZE).floor() as i32,
|
||||
);
|
||||
let tile = ((mine.pos.x / CELL_SIZE).floor() as i32, (mine.pos.y / CELL_SIZE).floor() as i32);
|
||||
index.map.entry(tile).and_modify(|set| {
|
||||
set.remove(&remove.entity);
|
||||
});
|
||||
@@ -153,11 +141,7 @@ fn explode_mine(explode: On<Explode>, query: Query<&Mine>, mut commands: Command
|
||||
// Draw a circle for each mine using `Gizmos`
|
||||
fn draw_shapes(mut gizmos: Gizmos, mines: Query<&Mine>) {
|
||||
for mine in &mines {
|
||||
gizmos.circle_2d(
|
||||
mine.pos,
|
||||
mine.size,
|
||||
Color::hsl((mine.size - 4.0) / 16.0 * 360.0, 1.0, 0.8),
|
||||
);
|
||||
gizmos.circle_2d(mine.pos, mine.size, Color::hsl((mine.size - 4.0) / 16.0 * 360.0, 1.0, 0.8));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,10 +178,7 @@ const CELL_SIZE: f32 = 64.0;
|
||||
impl SpatialIndex {
|
||||
// Lookup all entities within adjacent cells of our spatial index
|
||||
fn get_nearby(&self, pos: Vec2) -> Vec<Entity> {
|
||||
let tile = (
|
||||
(pos.x / CELL_SIZE).floor() as i32,
|
||||
(pos.y / CELL_SIZE).floor() as i32,
|
||||
);
|
||||
let tile = ((pos.x / CELL_SIZE).floor() as i32, (pos.y / CELL_SIZE).floor() as i32);
|
||||
let mut nearby = Vec::new();
|
||||
for x in -1..2 {
|
||||
for y in -1..2 {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
use bevy::math::bounding::{Aabb2d, BoundingCircle, BoundingVolume, IntersectsVolume};
|
||||
use bevy::prelude::*;
|
||||
|
||||
@@ -7,10 +6,7 @@ fn main() {
|
||||
.add_plugins(DefaultPlugins)
|
||||
.insert_resource(ClearColor(Color::srgb(0.8, 0.8, 1.0)))
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(
|
||||
FixedUpdate,
|
||||
(move_paddle, apply_velocity, check_for_collisions),
|
||||
)
|
||||
.add_systems(FixedUpdate, (move_paddle, apply_velocity, check_for_collisions))
|
||||
.run();
|
||||
}
|
||||
|
||||
@@ -40,11 +36,7 @@ struct Ball;
|
||||
#[derive(Component)]
|
||||
struct Collider;
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
) {
|
||||
fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<ColorMaterial>>) {
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
let paddle_y = BOTTOM_WALL + PADDLE_GAP_Y;
|
||||
@@ -77,11 +69,7 @@ fn setup(
|
||||
commands.spawn(Wall::new(WallLocation::Bottom));
|
||||
}
|
||||
|
||||
fn move_paddle(
|
||||
input: Res<ButtonInput<KeyCode>>,
|
||||
time: Res<Time>,
|
||||
mut paddle_transform: Single<&mut Transform, With<Paddle>>,
|
||||
) {
|
||||
fn move_paddle(input: Res<ButtonInput<KeyCode>>, time: Res<Time>, mut paddle_transform: Single<&mut Transform, With<Paddle>>) {
|
||||
let direction = if input.pressed(KeyCode::ArrowLeft) {
|
||||
-1.0
|
||||
} else if input.pressed(KeyCode::ArrowRight) {
|
||||
@@ -107,19 +95,13 @@ fn apply_velocity(time: Res<Time>, mut query: Query<(&mut Transform, &Velocity)>
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_collisions(
|
||||
ball_query: Single<(&mut Velocity, &Transform), With<Ball>>,
|
||||
collider_query: Query<&Transform, With<Collider>>,
|
||||
) {
|
||||
fn check_for_collisions(ball_query: Single<(&mut Velocity, &Transform), With<Ball>>, collider_query: Query<&Transform, With<Collider>>) {
|
||||
let (mut ball_velocity, ball_transform) = ball_query.into_inner();
|
||||
|
||||
for collider_transform in &collider_query {
|
||||
let collision = ball_collision(
|
||||
BoundingCircle::new(ball_transform.translation.truncate(), BALL_DIAMETER / 2.0),
|
||||
Aabb2d::new(
|
||||
collider_transform.translation.truncate(),
|
||||
collider_transform.scale.truncate() / 2.0,
|
||||
),
|
||||
Aabb2d::new(collider_transform.translation.truncate(), collider_transform.scale.truncate() / 2.0),
|
||||
);
|
||||
|
||||
if let Some(collision) = collision {
|
||||
@@ -160,11 +142,7 @@ fn ball_collision(circle: BoundingCircle, rect: Aabb2d) -> Option<Collision> {
|
||||
let offset = circle.center() - closest;
|
||||
|
||||
let side = if offset.x.abs() > offset.y.abs() {
|
||||
if offset.x < 0.0 {
|
||||
Collision::Left
|
||||
} else {
|
||||
Collision::Right
|
||||
}
|
||||
if offset.x < 0.0 { Collision::Left } else { Collision::Right }
|
||||
} else if offset.y > 0.0 {
|
||||
Collision::Top
|
||||
} else {
|
||||
@@ -212,12 +190,8 @@ impl WallLocation {
|
||||
|
||||
fn size(&self) -> Vec2 {
|
||||
match self {
|
||||
WallLocation::Left | WallLocation::Right => {
|
||||
Vec2::new(WALL_WIDTH, TOP_WALL - BOTTOM_WALL)
|
||||
}
|
||||
WallLocation::Top | WallLocation::Bottom => {
|
||||
Vec2::new(RIGHT_WALL - LEFT_WALL, WALL_WIDTH)
|
||||
}
|
||||
WallLocation::Left | WallLocation::Right => Vec2::new(WALL_WIDTH, TOP_WALL - BOTTOM_WALL),
|
||||
WallLocation::Top | WallLocation::Bottom => Vec2::new(RIGHT_WALL - LEFT_WALL, WALL_WIDTH),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,21 +47,15 @@ fn runner(mut app: App) -> AppExit {
|
||||
}
|
||||
"f" => {
|
||||
println!("FAST: setting relative speed to 2x");
|
||||
app.world_mut()
|
||||
.resource_mut::<Time<Virtual>>()
|
||||
.set_relative_speed(2.0);
|
||||
app.world_mut().resource_mut::<Time<Virtual>>().set_relative_speed(2.0);
|
||||
}
|
||||
"n" => {
|
||||
println!("NORMAL: setting relative speed to 1x");
|
||||
app.world_mut()
|
||||
.resource_mut::<Time<Virtual>>()
|
||||
.set_relative_speed(1.0);
|
||||
app.world_mut().resource_mut::<Time<Virtual>>().set_relative_speed(1.0);
|
||||
}
|
||||
"s" => {
|
||||
println!("SLOW: setting relative speed to 0.5x");
|
||||
app.world_mut()
|
||||
.resource_mut::<Time<Virtual>>()
|
||||
.set_relative_speed(0.5);
|
||||
app.world_mut().resource_mut::<Time<Virtual>>().set_relative_speed(0.5);
|
||||
}
|
||||
"p" => {
|
||||
println!("PAUSE: pausing virtual clock");
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
max_width = 160
|
||||
newline_style = "Unix"
|
||||
fn_single_line = true
|
||||
@@ -8,11 +8,7 @@ pub fn print_all_entities(mut commands: Commands, query: Query<Entity>) {
|
||||
}
|
||||
|
||||
/// ДЛЯ ОТЛАДКИ - вывод всех сущностей мира по нажатию Enter
|
||||
pub fn print_all_entities_by_press_enter(
|
||||
input: Res<ButtonInput<KeyCode>>,
|
||||
mut commands: Commands,
|
||||
query: Query<Entity>,
|
||||
) {
|
||||
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();
|
||||
@@ -28,9 +24,7 @@ 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();
|
||||
let grid = commands.spawn((Grid, Transform::default(), Visibility::default())).id();
|
||||
|
||||
for x in -20..=20 {
|
||||
commands.spawn((
|
||||
|
||||
+32
-21
@@ -1,7 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, fmt::Debug};
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::tools::Position;
|
||||
|
||||
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;
|
||||
@@ -20,7 +22,19 @@ pub struct End;
|
||||
|
||||
/// Событие прохождения границы сетки
|
||||
#[derive(EntityEvent)]
|
||||
pub struct GridBarier(pub Entity);
|
||||
pub struct GridBarier {
|
||||
pub entity: Entity,
|
||||
#[allow(unused)]
|
||||
pub src_pos: Position,
|
||||
#[allow(unused)]
|
||||
pub dst_pos: Position,
|
||||
}
|
||||
|
||||
impl GridBarier {
|
||||
pub fn new(entity: Entity, src_pos: Position, dst_pos: Position) -> Self {
|
||||
Self { entity, src_pos, dst_pos }
|
||||
}
|
||||
}
|
||||
|
||||
/// Направление движения
|
||||
#[derive(Component, PartialEq, Clone, Copy, Debug)]
|
||||
@@ -93,30 +107,27 @@ pub struct Inert(pub Direction);
|
||||
|
||||
/// Путь змеи
|
||||
#[derive(Resource, Default, Debug)]
|
||||
pub struct SnakePath(HashMap<(i16, i16), Direction>);
|
||||
pub struct SnakePath(HashMap<Position, 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) -> (i16, i16, Direction) {
|
||||
let key = SnakePath::to_key(transform);
|
||||
pub fn push(&mut self, transform: &Transform, direction: Direction) -> (Position, Direction) {
|
||||
let key = transform.into();
|
||||
self.0.insert(key, direction.clone());
|
||||
(key.0, key.1, direction.clone())
|
||||
(key, direction.clone())
|
||||
}
|
||||
|
||||
pub fn pick(&self, transform: &Transform) -> Option<(i16, i16, Direction)> {
|
||||
let key = SnakePath::to_key(transform);
|
||||
self.0.get(&key).cloned().map(|d| (key.0, key.1, d))
|
||||
pub fn pick(&self, transform: &Transform) -> Option<(Position, Direction)> {
|
||||
let key = transform.into();
|
||||
self.0.get(&key).cloned().map(|d| (key, d))
|
||||
}
|
||||
|
||||
pub fn drop(&mut self, transform: &Transform) -> Option<(i16, i16, Direction)> {
|
||||
let key = SnakePath::to_key(transform);
|
||||
self.0.remove(&key).map(|d| (key.0, key.1, d))
|
||||
pub fn drop(&mut self, transform: &Transform) -> Option<(Position, Direction)> {
|
||||
let key = transform.into();
|
||||
self.0.remove(&key).map(|d| (key, d))
|
||||
}
|
||||
|
||||
pub fn debug(&self) -> impl Debug {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,10 +143,10 @@ mod test {
|
||||
snake_path.push(&Transform::default(), Direction::default());
|
||||
assert_eq!(snake_path.0.len(), 1);
|
||||
|
||||
assert!(snake_path.pick(&Transform::default()) == Some((0, 0, Direction::default())));
|
||||
assert!(snake_path.pick(&Transform::default()) == Some((Position::default(), Direction::default())));
|
||||
assert_eq!(snake_path.0.len(), 1);
|
||||
|
||||
assert!(snake_path.drop(&Transform::default()) == Some((0, 0, Direction::default())));
|
||||
assert!(snake_path.drop(&Transform::default()) == Some((Position::default(), Direction::default())));
|
||||
assert_eq!(snake_path.0.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,7 @@ use crate::{
|
||||
};
|
||||
|
||||
/// Система поворота головы змеи
|
||||
pub fn head_rotation(
|
||||
input: Res<ButtonInput<KeyCode>>,
|
||||
query: Single<(&mut Transform, &mut Direction), With<Head>>,
|
||||
mut snake_path: ResMut<SnakePath>,
|
||||
) {
|
||||
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();
|
||||
@@ -33,17 +29,14 @@ pub fn head_rotation(
|
||||
}
|
||||
|
||||
/// Система движения для всех движущихся сущностей
|
||||
pub fn moving(
|
||||
time: Res<Time<Fixed>>,
|
||||
query: Query<(&mut Transform, &mut Inert, &Direction, &Velocity, Entity)>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
pub fn moving(time: Res<Time<Fixed>>, 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 src_transftorm = transform.clone();
|
||||
let delta = velocity * time.delta_secs();
|
||||
let src = inert.0.get_coord(&transform);
|
||||
let sign = inert.0.get_change_sign();
|
||||
let dst = src + sign * delta;
|
||||
let mut splited = _split_by_grid_tracing(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));
|
||||
inert.0.set_coord(&mut transform, first.0);
|
||||
if let Some(second) = splited.next() {
|
||||
@@ -53,16 +46,13 @@ pub fn moving(
|
||||
let sign = inert.0.get_change_sign();
|
||||
let dst = src + sign * delta;
|
||||
inert.0.set_coord(&mut transform, dst);
|
||||
commands.trigger(GridBarier(entity));
|
||||
commands.trigger(GridBarier::new(entity, (&src_transftorm).into(), transform.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _split_by_grid_tracing(src: f32, dst: f32, grid: f32) -> Vec<(f32, f32)> {
|
||||
let result = split_by_grid(src, dst, grid);
|
||||
trace!(
|
||||
"\tsplit_by_grid({:?}, {:?}, {:?}) -> {:?}",
|
||||
src, dst, grid, result
|
||||
);
|
||||
trace!("\tsplit_by_grid({:?}, {:?}, {:?}) -> {:?}", src, dst, grid, result);
|
||||
result
|
||||
}
|
||||
|
||||
+19
-30
@@ -4,18 +4,12 @@ use bevy::prelude::*;
|
||||
|
||||
#[allow(unused)]
|
||||
/// Слушатель событий [GridBarier] для хвоста, обновляющий направление его движения из ресурса [SnakePath] и позволяющий следовать за головой
|
||||
pub fn tail_observer(
|
||||
event: On<GridBarier>,
|
||||
mut query: Query<(&mut Direction, &Transform)>,
|
||||
snake_path: Res<SnakePath>,
|
||||
) {
|
||||
if event.observer() == event.0 {
|
||||
if let Ok((mut direction, transform)) = query.get_mut(event.0) {
|
||||
if let Some((x, y, new_direction)) = snake_path.pick(transform) {
|
||||
trace!(
|
||||
"change direction for {:?} in ({:?}, {:?}) to {:?}",
|
||||
event.0, x, y, new_direction
|
||||
);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -23,33 +17,28 @@ pub fn tail_observer(
|
||||
}
|
||||
|
||||
/// Слушатель событий [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) {
|
||||
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((x, y, dir)) = snake_path.drop(&transform) {
|
||||
trace!(
|
||||
"removed entry from SnakePath: ({:?}, {:?}) => {:?}\n\tsnake_path: {:?}",
|
||||
x, y, dir, *snake_path
|
||||
);
|
||||
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) {
|
||||
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();
|
||||
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() {
|
||||
velocity.0 = SNAKE_VELOCITY as f32;
|
||||
commands.entity(event.observer()).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-17
@@ -1,7 +1,7 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
domain::{End, SnakePath, Tail},
|
||||
domain::{End, GridBarier, SnakePath, Tail},
|
||||
observers::{cleanup_snake_path, schedule_set_velocity, tail_observer},
|
||||
};
|
||||
|
||||
@@ -20,16 +20,20 @@ fn spawn_snake(commands: &mut Commands) {
|
||||
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 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),
|
||||
),
|
||||
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()
|
||||
@@ -37,10 +41,7 @@ fn spawn_snake(commands: &mut Commands) {
|
||||
))
|
||||
.id();
|
||||
commands.spawn((
|
||||
Sprite::from_color(
|
||||
Color::srgb(1., 0., 0.),
|
||||
vec2(HEAD_SIZE as f32 * 0.3, HEAD_SIZE as f32 * 0.3),
|
||||
),
|
||||
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()
|
||||
@@ -48,10 +49,7 @@ fn spawn_snake(commands: &mut Commands) {
|
||||
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),
|
||||
),
|
||||
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()
|
||||
@@ -66,10 +64,7 @@ fn spawn_snake(commands: &mut Commands) {
|
||||
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),
|
||||
),
|
||||
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()
|
||||
|
||||
+22
-48
@@ -13,12 +13,7 @@ pub fn define_log_layer() -> Option<bevy::log::BoxedFmtLayer> {
|
||||
.with_line_number(false)
|
||||
.with_filter(targets.clone())
|
||||
.boxed();
|
||||
let file = match File::options()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open("snake-game.log")
|
||||
{
|
||||
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);
|
||||
@@ -54,9 +49,9 @@ pub 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;
|
||||
if step != src {
|
||||
output.push((step, step - src));
|
||||
}
|
||||
// if step != src {
|
||||
output.push((step, step - src));
|
||||
// }
|
||||
src = step;
|
||||
}
|
||||
output.push((dst, dst - src));
|
||||
@@ -64,9 +59,9 @@ pub 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;
|
||||
if step != dst {
|
||||
output.push((dst, dst - step));
|
||||
}
|
||||
// if step != dst {
|
||||
output.push((dst, dst - step));
|
||||
// }
|
||||
dst = step;
|
||||
}
|
||||
output.push((dst, dst - src));
|
||||
@@ -86,53 +81,32 @@ mod test {
|
||||
assert_eq!(split_by_grid(3.0, 5.0, 5.0), vec![(5.0, 2.0)]);
|
||||
assert_eq!(split_by_grid(3.0, 6.0, 5.0), vec![(5.0, 2.0), (6.0, 1.0)]);
|
||||
assert_eq!(split_by_grid(3.0, 10.0, 5.0), vec![(5.0, 2.0), (10.0, 5.0)]);
|
||||
assert_eq!(
|
||||
split_by_grid(3.0, 11.0, 5.0),
|
||||
vec![(5.0, 2.0), (10.0, 5.0), (11.0, 1.0)]
|
||||
);
|
||||
assert_eq!(split_by_grid(3.0, 11.0, 5.0), vec![(5.0, 2.0), (10.0, 5.0), (11.0, 1.0)]);
|
||||
assert_eq!(split_by_grid(-2.0, 3.0, 5.0), vec![(0.0, 2.0), (3.0, 3.0)]);
|
||||
assert_eq!(
|
||||
split_by_grid(-6.0, 7.0, 5.0),
|
||||
vec![(-5.0, 1.0), (0.0, 5.0), (5.0, 5.0), (7.0, 2.0)]
|
||||
);
|
||||
assert_eq!(
|
||||
split_by_grid(-6.0, -2.0, 5.0),
|
||||
vec![(-5.0, 1.0), (-2.0, 3.0)]
|
||||
);
|
||||
assert_eq!(split_by_grid(-6.0, 7.0, 5.0), vec![(-5.0, 1.0), (0.0, 5.0), (5.0, 5.0), (7.0, 2.0)]);
|
||||
assert_eq!(split_by_grid(-6.0, -2.0, 5.0), vec![(-5.0, 1.0), (-2.0, 3.0)]);
|
||||
|
||||
// движение влево
|
||||
assert_eq!(split_by_grid(4.0, 1.0, 5.0), vec![(1.0, -3.0)]);
|
||||
assert_eq!(split_by_grid(5.0, 3.0, 5.0), vec![(3.0, -2.0)]);
|
||||
assert_eq!(split_by_grid(6.0, 3.0, 5.0), vec![(5.0, -1.0), (3.0, -2.0)]);
|
||||
assert_eq!(
|
||||
split_by_grid(10.0, 3.0, 5.0),
|
||||
vec![(5.0, -5.0), (3.0, -2.0)]
|
||||
);
|
||||
assert_eq!(
|
||||
split_by_grid(11.0, 3.0, 5.0),
|
||||
vec![(10.0, -1.0), (5.0, -5.0), (3.0, -2.0)]
|
||||
);
|
||||
assert_eq!(
|
||||
split_by_grid(3.0, -2.0, 5.0),
|
||||
vec![(0.0, -3.0), (-2.0, -2.0)]
|
||||
);
|
||||
assert_eq!(
|
||||
split_by_grid(7.0, -6.0, 5.0),
|
||||
vec![(5.0, -2.0), (0.0, -5.0), (-5.0, -5.0), (-6.0, -1.0)]
|
||||
);
|
||||
assert_eq!(
|
||||
split_by_grid(-2.0, -6.0, 5.0),
|
||||
vec![(-5.0, -3.0), (-6.0, -1.0)]
|
||||
);
|
||||
assert_eq!(split_by_grid(10.0, 3.0, 5.0), vec![(5.0, -5.0), (3.0, -2.0)]);
|
||||
assert_eq!(split_by_grid(11.0, 3.0, 5.0), vec![(10.0, -1.0), (5.0, -5.0), (3.0, -2.0)]);
|
||||
assert_eq!(split_by_grid(3.0, -2.0, 5.0), vec![(0.0, -3.0), (-2.0, -2.0)]);
|
||||
assert_eq!(split_by_grid(7.0, -6.0, 5.0), vec![(5.0, -2.0), (0.0, -5.0), (-5.0, -5.0), (-6.0, -1.0)]);
|
||||
assert_eq!(split_by_grid(-2.0, -6.0, 5.0), vec![(-5.0, -3.0), (-6.0, -1.0)]);
|
||||
|
||||
// сценарии с нулевой сеткой
|
||||
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)]);
|
||||
assert_eq!(split_by_grid(0.0, 1.5, 5.0), vec![(0.0, 0.0), (1.5, 1.5)]);
|
||||
assert_eq!(split_by_grid(5.0, 7.0, 5.0), vec![(5.0, 0.0), (7.0, 2.0)]);
|
||||
assert_eq!(split_by_grid(0.0, -1.5, 5.0), vec![(0.0, 0.0), (-1.5, -1.5)]);
|
||||
assert_eq!(split_by_grid(5.0, 3.0, 5.0), vec![(5.0, 0.0), (3.0, -2.0)]);
|
||||
}
|
||||
}
|
||||
|
||||
mod position;
|
||||
pub use position::Position;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
use std::{fmt::Display, ops::Deref};
|
||||
|
||||
use bevy::transform::components::Transform;
|
||||
|
||||
use crate::domain::GRID_SIZE;
|
||||
|
||||
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Position {
|
||||
pub x: i8,
|
||||
pub y: i8,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn new(x: i8, y: i8) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Position {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "(x:{}, y:{})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Deref<Target = Transform>> From<T> for Position {
|
||||
fn from(value: T) -> Self {
|
||||
Self {
|
||||
x: (value.translation.x / GRID_SIZE as f32) as i8,
|
||||
y: (value.translation.y / GRID_SIZE as f32) as i8,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user