wip: started implementing terrain system
parent
954ac51bd6
commit
cf66379a23
|
|
@ -2061,6 +2061,7 @@ dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy-inspector-egui",
|
"bevy-inspector-egui",
|
||||||
"bevy_rapier2d",
|
"bevy_rapier2d",
|
||||||
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ edition = "2021"
|
||||||
bevy = { version = "0.9.0", features = ["dynamic"] }
|
bevy = { version = "0.9.0", features = ["dynamic"] }
|
||||||
bevy-inspector-egui = "0.14.0"
|
bevy-inspector-egui = "0.14.0"
|
||||||
bevy_rapier2d = { path = "../bevy_rapier/bevy_rapier2d" }
|
bevy_rapier2d = { path = "../bevy_rapier/bevy_rapier2d" }
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
# Enable a small amount of optimization in debug mode
|
# Enable a small amount of optimization in debug mode
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,12 @@ pub fn init() {
|
||||||
.add_plugin(WorldInspectorPlugin::new())
|
.add_plugin(WorldInspectorPlugin::new())
|
||||||
.add_plugin(KinematicPlugin)
|
.add_plugin(KinematicPlugin)
|
||||||
.add_plugin(GameCameraPlugin)
|
.add_plugin(GameCameraPlugin)
|
||||||
.add_plugin(PlayerPlugin)
|
// .add_plugin(PlayerPlugin)
|
||||||
.add_startup_system(setup)
|
// .add_startup_system(setup_debug_ground)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(mut commands: Commands) {
|
fn setup_debug_ground(mut commands: Commands) {
|
||||||
// Static ground
|
// Static ground
|
||||||
commands
|
commands
|
||||||
.spawn(())
|
.spawn(())
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,10 @@ fn camera_setup(mut commands: Commands) {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Name::new("Camera"),
|
Name::new("Camera"),
|
||||||
Camera2dBundle {
|
Camera2dBundle {
|
||||||
projection: OrthographicProjection {
|
// projection: OrthographicProjection {
|
||||||
scaling_mode: ScalingMode::FixedHorizontal(320.0),
|
// scaling_mode: ScalingMode::FixedHorizontal(320.0),
|
||||||
..default()
|
// ..default()
|
||||||
},
|
// },
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ pub struct KinematicBundle {
|
||||||
pub events: ActiveEvents,
|
pub events: ActiveEvents,
|
||||||
pub collisions: ActiveCollisionTypes,
|
pub collisions: ActiveCollisionTypes,
|
||||||
pub properties: KinematicProperties,
|
pub properties: KinematicProperties,
|
||||||
#[bundle]
|
|
||||||
pub transform: TransformBundle,
|
pub transform: TransformBundle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,10 +70,7 @@ impl KinematicState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_jump(&self) -> bool {
|
pub fn can_jump(&self) -> bool {
|
||||||
if self.on_ground && !self.did_jump {
|
self.on_ground && !self.did_jump
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ pub fn player_system(
|
||||||
};
|
};
|
||||||
|
|
||||||
kinematic_input.movement = movement;
|
kinematic_input.movement = movement;
|
||||||
kinematic_input.want_jump = input.pressed(KeyCode::Space)
|
kinematic_input.want_jump = input.just_pressed(KeyCode::Space)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_to_axis(negative: bool, positive: bool) -> f32 {
|
fn input_to_axis(negative: bool, positive: bool) -> f32 {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod game;
|
pub mod game;
|
||||||
|
pub mod terrain2d;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
use box2d_rs::{
|
|
||||||
b2_body::BodyPtr,
|
|
||||||
b2_math::B2vec2,
|
|
||||||
b2_world::{B2world, B2worldPtr},
|
|
||||||
b2rs_common::UserDataType,
|
|
||||||
};
|
|
||||||
use unsafe_send_sync::UnsafeSendSync;
|
|
||||||
|
|
||||||
pub type UnsafeBox2D = UnsafeSendSync<Box2D>;
|
|
||||||
pub type UnsafeBody = UnsafeSendSync<BodyPtr<UserData>>;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
|
||||||
pub struct UserData;
|
|
||||||
impl UserDataType for UserData {
|
|
||||||
type Body = Option<u32>;
|
|
||||||
type Fixture = u32;
|
|
||||||
type Joint = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Box2D {
|
|
||||||
pub gravity: B2vec2,
|
|
||||||
pub world_ptr: B2worldPtr<UserData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Box2D {
|
|
||||||
pub const METERS_TO_TEXELS: f32 = 4.0;
|
|
||||||
pub const TEXELS_TO_METERS: f32 = 1.0 / Self::METERS_TO_TEXELS;
|
|
||||||
pub const INIT_POS: B2vec2 = B2vec2 {
|
|
||||||
x: -1000.0,
|
|
||||||
y: -1000.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn new() -> Box2D {
|
|
||||||
let gravity: B2vec2 = B2vec2 { x: 0.0, y: 100.0 };
|
|
||||||
// let gravity: B2vec2 = B2vec2 { x: 0.0, y: 1.0 };
|
|
||||||
// let gravity: B2vec2 = B2vec2 { x: 0.0, y: 0.0 };
|
|
||||||
let world_ptr: B2worldPtr<UserData> = B2world::new(gravity);
|
|
||||||
|
|
||||||
Box2D { gravity, world_ptr }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_unsafe() -> UnsafeBox2D {
|
|
||||||
UnsafeBox2D::new(Self::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Box2D {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
use std::collections::{
|
||||||
|
hash_map::{Iter, IterMut},
|
||||||
|
HashMap,
|
||||||
|
};
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
mod chunk2d;
|
||||||
|
mod texel2d;
|
||||||
|
|
||||||
|
pub use chunk2d::*;
|
||||||
|
pub use texel2d::*;
|
||||||
|
|
||||||
|
use crate::util::{math::*, Vector2I};
|
||||||
|
|
||||||
|
pub struct Terrain2DPlugin;
|
||||||
|
|
||||||
|
impl Plugin for Terrain2DPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.insert_resource(Terrain2D::new())
|
||||||
|
.add_system(emit_terrain_events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_terrain_events(
|
||||||
|
mut terrain: ResMut<Terrain2D>,
|
||||||
|
mut terrain_events: EventWriter<TerrainEvent>,
|
||||||
|
) {
|
||||||
|
for event in terrain.events.drain(..) {
|
||||||
|
terrain_events.send(event)
|
||||||
|
}
|
||||||
|
for (chunk_index, mut chunk) in terrain.chunk_iter_mut() {
|
||||||
|
if let Some(rect) = &chunk.dirty_rect {
|
||||||
|
terrain_events.send(TerrainEvent::TexelsUpdated(*chunk_index, *rect));
|
||||||
|
chunk.dirty_rect = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TerrainEvent {
|
||||||
|
ChunkAdded(ChunkIndex),
|
||||||
|
ChunkRemoved(ChunkIndex),
|
||||||
|
TexelsUpdated(ChunkIndex, ChunkRect),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Resource)]
|
||||||
|
pub struct Terrain2D {
|
||||||
|
chunk_map: HashMap<ChunkIndex, Chunk>,
|
||||||
|
events: Vec<TerrainEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Terrain2D {
|
||||||
|
pub fn new() -> Terrain2D {
|
||||||
|
Terrain2D {
|
||||||
|
chunk_map: HashMap::new(),
|
||||||
|
events: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_chunk(&mut self, index: ChunkIndex, chunk: Chunk) {
|
||||||
|
self.chunk_map.insert(index, chunk);
|
||||||
|
self.events.push(TerrainEvent::ChunkAdded(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_chunk(&mut self, index: ChunkIndex) {
|
||||||
|
self.events.push(TerrainEvent::ChunkRemoved(index));
|
||||||
|
self.chunk_map.remove(&index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chunk_iter(&self) -> Iter<ChunkIndex, Chunk> {
|
||||||
|
self.chunk_map.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chunk_iter_mut(&mut self) -> IterMut<ChunkIndex, Chunk> {
|
||||||
|
self.chunk_map.iter_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index_to_chunk(&self, index: &ChunkIndex) -> Option<&Chunk> {
|
||||||
|
self.chunk_map.get(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index_to_chunk_mut(&mut self, index: &ChunkIndex) -> Option<&mut Chunk> {
|
||||||
|
self.chunk_map.get_mut(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global_to_chunk(&self, global: &Vector2I) -> Option<&Chunk> {
|
||||||
|
self.index_to_chunk(&global_to_chunk_index(global))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global_to_chunk_mut(&mut self, global: &Vector2I) -> Option<&mut Chunk> {
|
||||||
|
self.index_to_chunk_mut(&global_to_chunk_index(global))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global_to_texel(&self, global: &Vector2I) -> Option<Texel> {
|
||||||
|
match self.global_to_chunk(global) {
|
||||||
|
Some(chunk) => chunk.get_texel(&global_to_local(global)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global_to_texel_mut(&mut self, global: &Vector2I) -> Option<Texel> {
|
||||||
|
match self.global_to_chunk(global) {
|
||||||
|
Some(chunk) => chunk.get_texel(&global_to_local(global)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_texel(&mut self, global: &Vector2I, id: TexelID) {
|
||||||
|
let index = global_to_chunk_index(global);
|
||||||
|
match self.index_to_chunk_mut(&index) {
|
||||||
|
Some(chunk) => chunk.set_texel(&global_to_local(global), id),
|
||||||
|
None => {
|
||||||
|
let mut chunk = Chunk::new();
|
||||||
|
chunk.set_texel(&global_to_local(global), id);
|
||||||
|
self.add_chunk(index, chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_to_texel_index(position: &Vector2I) -> Option<usize> {
|
||||||
|
match position.x >= 0
|
||||||
|
&& position.y >= 0
|
||||||
|
&& position.x < Chunk::SIZE.x
|
||||||
|
&& position.y < Chunk::SIZE.y
|
||||||
|
{
|
||||||
|
true => Some(position.y as usize * Chunk::SIZE_X + position.x as usize),
|
||||||
|
false => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn texel_index_to_local(i: usize) -> Vector2I {
|
||||||
|
Vector2I {
|
||||||
|
x: i as i32 % Chunk::SIZE.x,
|
||||||
|
y: i as i32 / Chunk::SIZE.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global_to_local(position: &Vector2I) -> Vector2I {
|
||||||
|
Vector2I {
|
||||||
|
x: wrapping_remainder(position.x, Chunk::SIZE.x),
|
||||||
|
y: wrapping_remainder(position.y, Chunk::SIZE.y),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global_to_chunk_index(position: &Vector2I) -> ChunkIndex {
|
||||||
|
Vector2I {
|
||||||
|
x: wrapping_quotient(position.x, Chunk::SIZE.x),
|
||||||
|
y: wrapping_quotient(position.y, Chunk::SIZE.y),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chunk_index_to_global(chunk_pos: &ChunkIndex) -> Vector2I {
|
||||||
|
*chunk_pos * Chunk::SIZE
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
use super::{local_to_texel_index, Texel, TexelID, NEIGHBOUR_INDEX_MAP};
|
||||||
|
use crate::util::Vector2I;
|
||||||
|
|
||||||
|
pub type ChunkIndex = Vector2I;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ChunkRect {
|
||||||
|
pub min: Vector2I,
|
||||||
|
pub max: Vector2I,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Chunk {
|
||||||
|
pub texels: [Texel; (Self::SIZE_X * Self::SIZE_Y) as usize],
|
||||||
|
// TODO: handle multiple dirty rects
|
||||||
|
pub dirty_rect: Option<ChunkRect>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chunk {
|
||||||
|
pub const SIZE_X: usize = 64;
|
||||||
|
pub const SIZE_Y: usize = 64;
|
||||||
|
pub const SIZE: Vector2I = Vector2I {
|
||||||
|
x: Self::SIZE_X as i32,
|
||||||
|
y: Self::SIZE_Y as i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn new() -> Chunk {
|
||||||
|
Chunk {
|
||||||
|
texels: Self::new_texel_array(),
|
||||||
|
dirty_rect: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_texel_array() -> [Texel; Self::SIZE_X * Self::SIZE_Y] {
|
||||||
|
[Texel::default(); Self::SIZE_X * Self::SIZE_Y]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_all_dirty(&mut self) {
|
||||||
|
self.dirty_rect = Some(ChunkRect {
|
||||||
|
min: Vector2I::ZERO,
|
||||||
|
max: Self::SIZE,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_dirty(&mut self, position: &Vector2I) {
|
||||||
|
match &self.dirty_rect {
|
||||||
|
Some(rect) => {
|
||||||
|
self.dirty_rect = Some(ChunkRect {
|
||||||
|
min: Vector2I::min(&rect.min, position),
|
||||||
|
max: Vector2I::max(&rect.max, position),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.dirty_rect = Some(ChunkRect {
|
||||||
|
min: *position,
|
||||||
|
max: *position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_texel(&self, position: &Vector2I) -> Option<Texel> {
|
||||||
|
local_to_texel_index(position).map(|i| self.texels[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_texel_option_mut(&mut self, position: &Vector2I) -> Option<&mut Texel> {
|
||||||
|
local_to_texel_index(position).map(|i| &mut self.texels[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_texel(&mut self, position: &Vector2I, id: TexelID) {
|
||||||
|
let i = local_to_texel_index(position).expect("Texel index out of range");
|
||||||
|
if self.texels[i].id != id {
|
||||||
|
self.mark_dirty(position);
|
||||||
|
}
|
||||||
|
let update_neighbours = self.texels[i].is_empty()
|
||||||
|
!= (Texel {
|
||||||
|
id,
|
||||||
|
..self.texels[i]
|
||||||
|
})
|
||||||
|
.is_empty();
|
||||||
|
self.texels[i].id = id;
|
||||||
|
// Update neighbour mask
|
||||||
|
if update_neighbours {
|
||||||
|
for offset in Texel::NEIGHBOUR_OFFSET_VECTORS {
|
||||||
|
// Flip neighbour's bit
|
||||||
|
match self.get_texel_option_mut(&(*position + offset)) {
|
||||||
|
Some(mut neighbour) => {
|
||||||
|
neighbour.neighbour_mask ^= 1 << NEIGHBOUR_INDEX_MAP[&-offset];
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub use u8 as TexelID;
|
||||||
|
pub use u8 as NeighbourMask;
|
||||||
|
|
||||||
|
use crate::util::Vector2I;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct Texel {
|
||||||
|
pub id: TexelID,
|
||||||
|
/// bitmask of empty/non-empty neighbours, see NEIGHBOUR_OFFSET_VECTORS for the order
|
||||||
|
pub neighbour_mask: NeighbourMask,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref NEIGHBOUR_INDEX_MAP: HashMap<Vector2I, u8> = {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
for i in 0..Texel::NEIGHBOUR_OFFSET_VECTORS.len() {
|
||||||
|
map.insert(Texel::NEIGHBOUR_OFFSET_VECTORS[i], i as u8);
|
||||||
|
}
|
||||||
|
map
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Texel {
|
||||||
|
pub const EMPTY: TexelID = 0;
|
||||||
|
pub const NEIGHBOUR_OFFSET_VECTORS: [Vector2I; 4] = [
|
||||||
|
Vector2I { x: 0, y: 1 },
|
||||||
|
Vector2I { x: 1, y: 0 },
|
||||||
|
Vector2I { x: 0, y: -1 },
|
||||||
|
Vector2I { x: -1, y: 0 },
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.id == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub mod math;
|
||||||
|
mod vector2;
|
||||||
|
mod vector2_i32;
|
||||||
|
|
||||||
|
pub use vector2::*;
|
||||||
|
pub use vector2_i32::*;
|
||||||
|
|
||||||
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
|
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
|
||||||
a * (1.0 - t) + b * t
|
a * (1.0 - t) + b * t
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
|
||||||
|
a * (1.0 - t) + b * t
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate quotient, but take into account negative values so that they continue the cycle seamlessly.
|
||||||
|
/// e.g. (0, 4) -> 0, (-4, 4) -> -1, (-5, 4) -> -2
|
||||||
|
pub fn wrapping_quotient(dividend: i32, divisor: i32) -> i32 {
|
||||||
|
let res = (if dividend < 0 { dividend + 1 } else { dividend }) / divisor;
|
||||||
|
if dividend < 0 {
|
||||||
|
res - 1
|
||||||
|
} else {
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate remainder, but take into account negative values so that they continue the cycle seamlessly.
|
||||||
|
/// e.g. (0, 4) -> 0, (-4, 4) -> 0, (-5, 4) -> 3
|
||||||
|
pub fn wrapping_remainder(dividend: i32, divisor: i32) -> i32 {
|
||||||
|
let res = dividend % divisor;
|
||||||
|
if dividend < 0 {
|
||||||
|
(divisor + res) % divisor
|
||||||
|
} else {
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
use core::{fmt, ops};
|
||||||
|
|
||||||
|
pub trait VectorComponent:
|
||||||
|
Sized
|
||||||
|
+ Copy
|
||||||
|
+ Ord
|
||||||
|
+ fmt::Display
|
||||||
|
+ ops::Add<Output = Self>
|
||||||
|
+ ops::Neg<Output = Self>
|
||||||
|
+ ops::Sub<Output = Self>
|
||||||
|
+ ops::Mul<Output = Self>
|
||||||
|
+ ops::Div<Output = Self>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> VectorComponent for T where
|
||||||
|
T: Sized
|
||||||
|
+ Copy
|
||||||
|
+ Ord
|
||||||
|
+ fmt::Display
|
||||||
|
+ ops::Neg<Output = T>
|
||||||
|
+ ops::Add<Output = T>
|
||||||
|
+ ops::Sub<Output = T>
|
||||||
|
+ ops::Mul<Output = T>
|
||||||
|
+ ops::Div<Output = T>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, Clone, Copy, Default, Debug)]
|
||||||
|
pub struct Vector2<T: VectorComponent> {
|
||||||
|
pub x: T,
|
||||||
|
pub y: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VectorComponent> Vector2<T> {
|
||||||
|
pub fn min(&self, other: &Vector2<T>) -> Vector2<T> {
|
||||||
|
Vector2 {
|
||||||
|
x: Ord::min(self.x, other.x),
|
||||||
|
y: Ord::min(self.y, other.y),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max(&self, other: &Vector2<T>) -> Vector2<T> {
|
||||||
|
Vector2 {
|
||||||
|
x: Ord::max(self.x, other.x),
|
||||||
|
y: Ord::max(self.y, other.y),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VectorComponent> fmt::Display for Vector2<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "({}, {})", self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VectorComponent> ops::Add<Vector2<T>> for Vector2<T> {
|
||||||
|
type Output = Vector2<T>;
|
||||||
|
fn add(self, rhs: Vector2<T>) -> Self::Output {
|
||||||
|
Vector2 {
|
||||||
|
x: self.x + rhs.x,
|
||||||
|
y: self.y + rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VectorComponent> ops::Neg for Vector2<T> {
|
||||||
|
type Output = Vector2<T>;
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
Vector2 {
|
||||||
|
x: -self.x,
|
||||||
|
y: -self.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VectorComponent> ops::Sub<Vector2<T>> for Vector2<T> {
|
||||||
|
type Output = Vector2<T>;
|
||||||
|
fn sub(self, rhs: Vector2<T>) -> Self::Output {
|
||||||
|
self + (-rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VectorComponent> ops::Mul<Vector2<T>> for Vector2<T> {
|
||||||
|
type Output = Vector2<T>;
|
||||||
|
fn mul(self, rhs: Vector2<T>) -> Self::Output {
|
||||||
|
Vector2 {
|
||||||
|
x: self.x * rhs.x,
|
||||||
|
y: self.y * rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VectorComponent> ops::Mul<T> for Vector2<T> {
|
||||||
|
type Output = Vector2<T>;
|
||||||
|
fn mul(self, rhs: T) -> Self::Output {
|
||||||
|
Vector2 {
|
||||||
|
x: self.x * rhs,
|
||||||
|
y: self.y * rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VectorComponent> ops::Div<Vector2<T>> for Vector2<T> {
|
||||||
|
type Output = Vector2<T>;
|
||||||
|
fn div(self, rhs: Vector2<T>) -> Self::Output {
|
||||||
|
Vector2 {
|
||||||
|
x: self.x / rhs.x,
|
||||||
|
y: self.y / rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VectorComponent> ops::Div<T> for Vector2<T> {
|
||||||
|
type Output = Vector2<T>;
|
||||||
|
fn div(self, rhs: T) -> Self::Output {
|
||||||
|
Vector2 {
|
||||||
|
x: self.x / rhs,
|
||||||
|
y: self.y / rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
use bevy::prelude::Vec2;
|
||||||
|
|
||||||
|
use super::Vector2;
|
||||||
|
|
||||||
|
pub type Vector2I = Vector2<i32>;
|
||||||
|
|
||||||
|
impl Vector2I {
|
||||||
|
pub const ZERO: Vector2I = Vector2I { x: 0, y: 0 };
|
||||||
|
pub const ONE: Vector2I = Vector2I { x: 1, y: 1 };
|
||||||
|
pub const UP: Vector2I = Vector2I { x: 0, y: 1 };
|
||||||
|
pub const DOWN: Vector2I = Vector2I { x: 0, y: -1 };
|
||||||
|
pub const LEFT: Vector2I = Vector2I { x: -1, y: 0 };
|
||||||
|
pub const RIGHT: Vector2I = Vector2I { x: 1, y: 0 };
|
||||||
|
|
||||||
|
pub fn angle(&self) -> f32 {
|
||||||
|
(self.y as f32).atan2(self.x as f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vector2I> for Vec2 {
|
||||||
|
fn from(vec: Vector2I) -> Self {
|
||||||
|
Vec2 {
|
||||||
|
x: vec.x as f32,
|
||||||
|
y: vec.y as f32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue