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