From fca846982457e025080432b95e7b166d00b956a6 Mon Sep 17 00:00:00 2001 From: Mikkel Kongsted Date: Thu, 11 Apr 2024 09:54:31 +0200 Subject: [PATCH] wefactor ^_^ --- src/engine.rs | 353 ---------------------------------------- src/engine/component.rs | 6 + src/engine/context.rs | 193 ++++++++++++++++++++++ src/engine/entity.rs | 3 + src/engine/error.rs | 30 ++++ src/engine/game.rs | 120 ++++++++++++++ src/engine/id.rs | 1 + src/engine/mod.rs | 15 ++ src/engine/sprite.rs | 4 + src/engine/system.rs | 8 + src/main.rs | 173 ++++++++------------ 11 files changed, 446 insertions(+), 460 deletions(-) delete mode 100644 src/engine.rs create mode 100644 src/engine/component.rs create mode 100644 src/engine/context.rs create mode 100644 src/engine/entity.rs create mode 100644 src/engine/error.rs create mode 100644 src/engine/game.rs create mode 100644 src/engine/id.rs create mode 100644 src/engine/mod.rs create mode 100644 src/engine/sprite.rs create mode 100644 src/engine/system.rs diff --git a/src/engine.rs b/src/engine.rs deleted file mode 100644 index dd82e9c..0000000 --- a/src/engine.rs +++ /dev/null @@ -1,353 +0,0 @@ -use std::any::{Any, TypeId}; -use std::collections::HashSet; -use std::rc::Rc; -use std::time::{Duration, Instant}; - -pub use component_macro::Component; -pub use sdl2::keyboard::Keycode; - -use sdl2::{ - event::Event, - image::{self, LoadTexture, Sdl2ImageContext}, - pixels::Color, - rect::Rect, - render::{Canvas, Texture, TextureCreator}, - video::{Window, WindowBuildError, WindowContext}, - IntegerOrSdlError, Sdl, VideoSubsystem, -}; - -#[derive(Debug, Clone)] -pub struct Error(String); - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -macro_rules! impl_from_T_for_Error { - ($t:ty) => { - impl From<$t> for Error { - fn from(value: $t) -> Self { - Self(value.to_string()) - } - } - }; - ($t:ty, $($ts:ty),+) => { - impl From<$t> for Error { - fn from(value: $t) -> Self { - Self(value.to_string()) - } - } - impl_from_T_for_Error!($($ts),+); - }; -} - -impl_from_T_for_Error!(String, WindowBuildError, IntegerOrSdlError, &str); - -pub type Id = u64; - -pub struct Entity(Id, Vec>); - -pub trait Component { - fn inner_type_id(&self) -> TypeId; - fn as_any(&mut self) -> &mut dyn Any; -} - -#[derive(Clone, Copy)] -pub struct Sprite(Id); - -pub struct Context<'context, 'game> -where - 'game: 'context, -{ - id_counter: &'context mut Id, - canvas: &'context mut Canvas, - texture_creator: *const TextureCreator, - entities: &'context mut Vec, - systems: &'context mut Vec>, - textures: &'context mut Vec<(Id, Texture<'game>)>, - currently_pressed_keys: &'context HashSet, -} - -pub struct ComponentQuery(std::marker::PhantomData); - -impl ComponentQuery { - pub fn new() -> Self { - Self(std::marker::PhantomData) - } -} - -pub trait QueryRunner { - fn run(&self, context: &Context) -> Vec; -} - -impl QueryRunner for ComponentQuery -where - T0: 'static + Component, -{ - fn run(&self, context: &Context) -> Vec { - context.entities_with_component::() - } -} - -impl QueryRunner for ComponentQuery<(T0, T1)> -where - T0: 'static + Component, - T1: 'static + Component, -{ - fn run(&self, context: &Context) -> Vec { - let vs0 = context.entities_with_component::(); - let vs1 = context.entities_with_component::(); - vs0.into_iter() - .filter(|v0| vs1.iter().find(|v1| *v0 == **v1).is_some()) - .collect() - } -} - -impl QueryRunner for ComponentQuery<(T0, T1, T2)> -where - T0: 'static + Component, - T1: 'static + Component, - T2: 'static + Component, -{ - fn run(&self, context: &Context) -> Vec { - let vs0 = context.entities_with_component::(); - let vs1 = context.entities_with_component::(); - let vs2 = context.entities_with_component::(); - vs0.into_iter() - .filter(|v0| { - vs1.iter().find(|v1| *v0 == **v1).is_some() - && vs2.iter().find(|v2| *v0 == **v2).is_some() - }) - .collect() - } -} - -#[macro_export] -macro_rules! query { - ($ctx:expr, $t:ty) => { - { - use engine::QueryRunner; - engine::ComponentQuery::<$t>::new().run($ctx) - } - }; - ($ctx:expr, $($ts:ty),+) => { - { - #[allow(unused_imports)] - use engine::QueryRunner; - engine::ComponentQuery::<($($ts),+)>::new().run($ctx) - } - }; -} - -#[macro_export] -macro_rules! spawn { - ($ctx:expr, [$($ts:expr),+ $(,)?]) => { - engine::Context::spawn($ctx, vec![$(Box::new($ts)),+]) - }; - ($ctx:expr, $($ts:expr),+ $(,)?) => { - engine::Context::spawn($ctx, vec![$(Box::new($ts)),+]) - }; -} - -impl<'context, 'game> Context<'context, 'game> { - pub fn entities_with_component(&self) -> Vec { - let entity_type_id = TypeId::of::(); - self.entities - .iter() - .filter_map(|Entity(id, components)| { - let contains_component = components - .iter() - .any(|entity| (*entity).inner_type_id() == entity_type_id); - if contains_component { - Some(*id) - } else { - None - } - }) - .collect() - } - - pub fn entity_component(&mut self, entity_id: u64) -> &mut T { - let entity_type_id = TypeId::of::(); - let Entity(_id, components) = self - .entities - .iter_mut() - .find(|Entity(id, _)| *id == entity_id) - .unwrap(); - - let component = components - .iter_mut() - .find_map(|entity| { - let is_id = (*entity).inner_type_id() == entity_type_id; - if is_id { - Some(entity.as_any().downcast_mut::().unwrap()) - } else { - None - } - }) - .unwrap(); - component - } - - pub fn load_sprite

(&mut self, path: P) -> Result - where - P: AsRef, - { - let path = path.as_ref().to_path_buf(); - let texture: Texture<'game> = unsafe { (*self.texture_creator).load_texture(&path)? }; - let id = *self.id_counter; - *self.id_counter += 1; - self.textures.push((id, texture)); - Ok(Sprite(id)) - } - - pub fn draw_sprite(&mut self, sprite: Sprite, x: i32, y: i32) -> Result<(), Error> { - let texture = self - .textures - .iter() - .find_map(|v| if v.0 == sprite.0 { Some(&v.1) } else { None }) - .ok_or_else(|| "invalid sprite id")?; - self.canvas.copy( - texture, - None, - Rect::new(x, y, texture.query().width, texture.query().height), - )?; - Ok(()) - } - - pub fn spawn(&mut self, components: Vec>) { - let id = *self.id_counter; - *self.id_counter += 1; - self.entities.push(Entity(id, components)); - } - - pub fn despawn(&mut self, entity_id: u64) { - *self.entities = self - .entities - .drain(..) - .filter(|v| v.0 != entity_id) - .collect(); - } - - pub fn add_system(&mut self, system: S) { - system.on_add(self); - self.systems.push(Rc::new(system)) - } - - pub fn key_pressed(&self, keycode: Keycode) -> bool { - self.currently_pressed_keys.contains(&keycode) - } -} - -pub trait System { - fn on_add(&self, _ctx: &mut Context) {} - fn on_update(&self, _ctx: &mut Context, _delta: f64) -> Result<(), Error> { - Ok(()) - } -} - -pub struct Game<'a> { - id_counter: u64, - sdl_context: Sdl, - video_subsystem: VideoSubsystem, - image_context: Sdl2ImageContext, - canvas: Canvas, - texture_creator: TextureCreator, - event_pump: sdl2::EventPump, - entities: Vec, - components: Vec<(u64, Box)>, - systems: Vec>, - textures: Vec<(Id, Texture<'a>)>, - currently_pressed_keys: HashSet, -} - -impl<'game> Game<'game> { - pub fn new() -> Result { - let sdl_context = sdl2::init()?; - let video_subsystem = sdl_context.video()?; - let image_context = image::init(image::InitFlag::PNG)?; - let window = video_subsystem - .window("pvp-game-dilapidation", 1280, 720) - .position_centered() - .build()?; - - let mut canvas = window.into_canvas().build()?; - let texture_creator = canvas.texture_creator(); - - canvas.set_draw_color(Color::BLACK); - canvas.clear(); - canvas.present(); - let event_pump = sdl_context.event_pump()?; - Ok(Self { - id_counter: 0, - sdl_context, - video_subsystem, - image_context, - canvas, - texture_creator, - event_pump, - entities: vec![], - components: vec![], - systems: vec![], - textures: vec![], - currently_pressed_keys: HashSet::new(), - }) - } - - pub fn run(&mut self) { - let mut time_before = Instant::now(); - 'running: loop { - for event in self.event_pump.poll_iter() { - match event { - Event::Quit { .. } - | Event::KeyDown { - keycode: Some(Keycode::Escape), - .. - } => break 'running, - Event::KeyDown { keycode, .. } => { - self.currently_pressed_keys.insert(keycode.unwrap()); - } - Event::KeyUp { keycode, .. } => { - self.currently_pressed_keys.remove(&keycode.unwrap()); - } - _ => {} - } - } - self.canvas.set_draw_color(Color::RGB(60, 180, 180)); - self.canvas.clear(); - let now = Instant::now(); - let delta = (now - time_before).as_nanos() as f64 / 1_000_000_000.0; - time_before = now; - for system in self.systems.clone() { - let Err(err) = system.on_update(&mut self.context(), delta) else { - continue; - }; - println!("error occcurred updating system: {err}"); - } - self.canvas.present(); - std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60)) - } - } - - pub fn context<'context>(&'context mut self) -> Context<'context, 'game> - where - 'game: 'context, - { - Context { - id_counter: &mut self.id_counter, - canvas: &mut self.canvas, - texture_creator: &self.texture_creator, - entities: &mut self.entities, - systems: &mut self.systems, - textures: &mut self.textures, - currently_pressed_keys: &mut self.currently_pressed_keys, - } - } - - pub fn add_system(&mut self, system: S) { - system.on_add(&mut self.context()); - self.systems.push(Rc::new(system)) - } -} diff --git a/src/engine/component.rs b/src/engine/component.rs new file mode 100644 index 0000000..1bdc386 --- /dev/null +++ b/src/engine/component.rs @@ -0,0 +1,6 @@ +use std::any::{Any, TypeId}; + +pub trait Component { + fn inner_type_id(&self) -> TypeId; + fn as_any(&mut self) -> &mut dyn Any; +} diff --git a/src/engine/context.rs b/src/engine/context.rs new file mode 100644 index 0000000..a17fa30 --- /dev/null +++ b/src/engine/context.rs @@ -0,0 +1,193 @@ +use std::{any::TypeId, collections::HashSet, rc::Rc}; + +use sdl2::{ + image::LoadTexture, + keyboard::Keycode, + rect::Rect, + render::{Canvas, Texture, TextureCreator}, + video::{Window, WindowContext}, +}; + +use super::{entity::Entity, id::Id, sprite::Sprite, system::System, Component, Error}; + +pub struct Context<'context, 'game> +where + 'game: 'context, +{ + pub(super) id_counter: &'context mut Id, + pub(super) canvas: &'context mut Canvas, + pub(super) texture_creator: *const TextureCreator, + pub(super) entities: &'context mut Vec, + pub(super) systems: &'context mut Vec>, + pub(super) textures: &'context mut Vec<(Id, Texture<'game>)>, + pub(super) currently_pressed_keys: &'context HashSet, +} + +pub struct ComponentQuery(std::marker::PhantomData); + +impl ComponentQuery { + pub fn new() -> Self { + Self(std::marker::PhantomData) + } +} + +pub trait QueryRunner { + fn run(&self, context: &Context) -> Vec; +} + +impl QueryRunner for ComponentQuery +where + T0: 'static + Component, +{ + fn run(&self, context: &Context) -> Vec { + context.entities_with_component::() + } +} + +impl QueryRunner for ComponentQuery<(T0, T1)> +where + T0: 'static + Component, + T1: 'static + Component, +{ + fn run(&self, context: &Context) -> Vec { + let vs0 = context.entities_with_component::(); + let vs1 = context.entities_with_component::(); + vs0.into_iter() + .filter(|v0| vs1.iter().any(|v1| *v0 == *v1)) + .collect() + } +} + +impl QueryRunner for ComponentQuery<(T0, T1, T2)> +where + T0: 'static + Component, + T1: 'static + Component, + T2: 'static + Component, +{ + fn run(&self, context: &Context) -> Vec { + let vs0 = context.entities_with_component::(); + let vs1 = context.entities_with_component::(); + let vs2 = context.entities_with_component::(); + vs0.into_iter() + .filter(|v0| vs1.iter().any(|v1| *v0 == *v1) && vs2.iter().any(|v2| *v0 == *v2)) + .collect() + } +} + +#[macro_export] +macro_rules! query { + ($ctx:expr, $t:ty) => { + { + #[allow(unused_imports)] + use $crate::engine::QueryRunner; + engine::ComponentQuery::<$t>::new().run($ctx) + } + }; + ($ctx:expr, $($ts:ty),+) => { + { + #[allow(unused_imports)] + use $crate::engine::QueryRunner; + engine::ComponentQuery::<($($ts),+)>::new().run($ctx) + } + }; +} + +#[macro_export] +macro_rules! spawn { + ($ctx:expr, [$($ts:expr),+ $(,)?]) => { + engine::Context::spawn($ctx, vec![$(Box::new($ts)),+]) + }; + ($ctx:expr, $($ts:expr),+ $(,)?) => { + engine::Context::spawn($ctx, vec![$(Box::new($ts)),+]) + }; +} + +impl<'context, 'game> Context<'context, 'game> { + pub fn entities_with_component(&self) -> Vec { + let entity_type_id = TypeId::of::(); + self.entities + .iter() + .filter_map(|Entity(id, components)| { + let contains_component = components + .iter() + .any(|entity| (*entity).inner_type_id() == entity_type_id); + if contains_component { + Some(*id) + } else { + None + } + }) + .collect() + } + + pub fn entity_component(&mut self, entity_id: u64) -> &mut T { + let entity_type_id = TypeId::of::(); + let Entity(_id, components) = self + .entities + .iter_mut() + .find(|Entity(id, _)| *id == entity_id) + .unwrap(); + + let component = components + .iter_mut() + .find_map(|entity| { + let is_id = (*entity).inner_type_id() == entity_type_id; + if is_id { + Some(entity.as_any().downcast_mut::().unwrap()) + } else { + None + } + }) + .unwrap(); + component + } + + pub fn load_sprite

(&mut self, path: P) -> Result + where + P: AsRef, + { + let path = path.as_ref().to_path_buf(); + let texture: Texture<'game> = unsafe { (*self.texture_creator).load_texture(&path)? }; + let id = *self.id_counter; + *self.id_counter += 1; + self.textures.push((id, texture)); + Ok(Sprite(id)) + } + + pub fn draw_sprite(&mut self, sprite: Sprite, x: i32, y: i32) -> Result<(), Error> { + let texture = self + .textures + .iter() + .find_map(|v| if v.0 == sprite.0 { Some(&v.1) } else { None }) + .ok_or("invalid sprite id")?; + self.canvas.copy( + texture, + None, + Rect::new(x, y, texture.query().width, texture.query().height), + )?; + Ok(()) + } + + pub fn spawn(&mut self, components: Vec>) { + let id = *self.id_counter; + *self.id_counter += 1; + self.entities.push(Entity(id, components)); + } + + pub fn despawn(&mut self, entity_id: u64) { + *self.entities = self + .entities + .drain(..) + .filter(|v| v.0 != entity_id) + .collect(); + } + + pub fn add_system(&mut self, system: S) { + system.on_add(self); + self.systems.push(Rc::new(system)) + } + + pub fn key_pressed(&self, keycode: Keycode) -> bool { + self.currently_pressed_keys.contains(&keycode) + } +} diff --git a/src/engine/entity.rs b/src/engine/entity.rs new file mode 100644 index 0000000..0c73227 --- /dev/null +++ b/src/engine/entity.rs @@ -0,0 +1,3 @@ +use super::{id::Id, Component}; + +pub struct Entity(pub Id, pub Vec>); diff --git a/src/engine/error.rs b/src/engine/error.rs new file mode 100644 index 0000000..7208bac --- /dev/null +++ b/src/engine/error.rs @@ -0,0 +1,30 @@ +use sdl2::{video::WindowBuildError, IntegerOrSdlError}; + +#[derive(Debug, Clone)] +pub struct Error(String); + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +macro_rules! impl_from_T_for_Error { + ($t:ty) => { + impl From<$t> for Error { + fn from(value: $t) -> Self { + Self(value.to_string()) + } + } + }; + ($t:ty, $($ts:ty),+) => { + impl From<$t> for Error { + fn from(value: $t) -> Self { + Self(value.to_string()) + } + } + impl_from_T_for_Error!($($ts),+); + }; +} + +impl_from_T_for_Error!(String, WindowBuildError, IntegerOrSdlError, &str); diff --git a/src/engine/game.rs b/src/engine/game.rs new file mode 100644 index 0000000..be3834a --- /dev/null +++ b/src/engine/game.rs @@ -0,0 +1,120 @@ +use std::collections::HashSet; +use std::rc::Rc; +use std::time::{Duration, Instant}; + +use sdl2::keyboard::Keycode; +use sdl2::{ + event::Event, + image::{self, Sdl2ImageContext}, + pixels::Color, + render::{Canvas, Texture, TextureCreator}, + video::{Window, WindowContext}, + Sdl, VideoSubsystem, +}; + +use super::{context::Context, entity::Entity, id::Id, system::System}; +use super::{Component, Error}; + +pub struct Game<'a> { + id_counter: u64, + sdl_context: Sdl, + video_subsystem: VideoSubsystem, + image_context: Sdl2ImageContext, + canvas: Canvas, + texture_creator: TextureCreator, + event_pump: sdl2::EventPump, + entities: Vec, + components: Vec<(u64, Box)>, + systems: Vec>, + textures: Vec<(Id, Texture<'a>)>, + currently_pressed_keys: HashSet, +} + +impl<'game> Game<'game> { + pub fn new() -> Result { + let sdl_context = sdl2::init()?; + let video_subsystem = sdl_context.video()?; + let image_context = image::init(image::InitFlag::PNG)?; + let window = video_subsystem + .window("pvp-game-dilapidation", 1280, 720) + .position_centered() + .build()?; + + let mut canvas = window.into_canvas().build()?; + let texture_creator = canvas.texture_creator(); + + canvas.set_draw_color(Color::BLACK); + canvas.clear(); + canvas.present(); + let event_pump = sdl_context.event_pump()?; + Ok(Self { + id_counter: 0, + sdl_context, + video_subsystem, + image_context, + canvas, + texture_creator, + event_pump, + entities: vec![], + components: vec![], + systems: vec![], + textures: vec![], + currently_pressed_keys: HashSet::new(), + }) + } + + pub fn run(&mut self) { + let mut time_before = Instant::now(); + 'running: loop { + for event in self.event_pump.poll_iter() { + match event { + Event::Quit { .. } + | Event::KeyDown { + keycode: Some(Keycode::Escape), + .. + } => break 'running, + Event::KeyDown { keycode, .. } => { + self.currently_pressed_keys.insert(keycode.unwrap()); + } + Event::KeyUp { keycode, .. } => { + self.currently_pressed_keys.remove(&keycode.unwrap()); + } + _ => {} + } + } + self.canvas.set_draw_color(Color::RGB(60, 180, 180)); + self.canvas.clear(); + let now = Instant::now(); + let delta = (now - time_before).as_nanos() as f64 / 1_000_000_000.0; + time_before = now; + for system in self.systems.clone() { + let Err(err) = system.on_update(&mut self.context(), delta) else { + continue; + }; + println!("error occcurred updating system: {err}"); + } + self.canvas.present(); + std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60)) + } + } + + pub fn context<'context>(&'context mut self) -> Context<'context, 'game> + where + 'game: 'context, + { + Context { + id_counter: &mut self.id_counter, + canvas: &mut self.canvas, + texture_creator: &self.texture_creator, + entities: &mut self.entities, + systems: &mut self.systems, + textures: &mut self.textures, + currently_pressed_keys: &mut self.currently_pressed_keys, + } + } + + pub fn add_system(&mut self, system: S) { + system.on_add(&mut self.context()); + self.systems.push(Rc::new(system)) + } +} diff --git a/src/engine/id.rs b/src/engine/id.rs new file mode 100644 index 0000000..7466adf --- /dev/null +++ b/src/engine/id.rs @@ -0,0 +1 @@ +pub type Id = u64; diff --git a/src/engine/mod.rs b/src/engine/mod.rs new file mode 100644 index 0000000..a5b6060 --- /dev/null +++ b/src/engine/mod.rs @@ -0,0 +1,15 @@ +mod component; +mod context; +mod entity; +mod error; +mod game; +mod id; +mod sprite; +mod system; + +pub use self::{ + component::Component, context::ComponentQuery, context::Context, context::QueryRunner, + error::Error, game::Game, sprite::Sprite, system::System, +}; +pub use component_macro::Component; +pub use sdl2::keyboard::Keycode; diff --git a/src/engine/sprite.rs b/src/engine/sprite.rs new file mode 100644 index 0000000..982729f --- /dev/null +++ b/src/engine/sprite.rs @@ -0,0 +1,4 @@ +use super::id::Id; + +#[derive(Clone, Copy)] +pub struct Sprite(pub Id); diff --git a/src/engine/system.rs b/src/engine/system.rs new file mode 100644 index 0000000..f7373bc --- /dev/null +++ b/src/engine/system.rs @@ -0,0 +1,8 @@ +use super::{context::Context, Error}; + +pub trait System { + fn on_add(&self, _ctx: &mut Context) {} + fn on_update(&self, _ctx: &mut Context, _delta: f64) -> Result<(), Error> { + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index e5974b0..7c00f6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,8 +13,8 @@ struct Sprite { struct RigidBody { pos: (f64, f64), vel: (f64, f64), + rect: (f64, f64), gravity: bool, - cowision: bool, } struct VelocitySystem; @@ -29,48 +29,26 @@ impl System for VelocitySystem { } } -#[derive(Component, Clone)] -struct Rect { - width: f64, - height: f64, +fn rects_collide( + rect: (f64, f64), + pos: (f64, f64), + other_rect: (f64, f64), + other_pos: (f64, f64), +) -> bool { + pos.0 + rect.0 > other_pos.0 + && pos.0 < other_pos.0 + other_rect.0 + && pos.1 + rect.1 > other_pos.1 + && pos.1 < other_pos.1 + other_rect.1 } -impl Rect { - pub fn rect_collides(&self, pos: (f64, f64), other: &Rect, other_pos: (f64, f64)) -> bool { - pos.0 + self.width > other_pos.0 - && pos.0 < other_pos.0 + other.width - && pos.1 + self.height > other_pos.1 - && pos.1 < other_pos.1 + other.height - } - - pub fn point_collides(&self, pos: (f64, f64), point: (f64, f64)) -> bool { - pos.0 + self.width < point.0 - && pos.0 > point.0 - && pos.1 + self.height < point.1 - && pos.1 > point.1 - } +fn rec_point_collides(rect: (f64, f64), pos: (f64, f64), point: (f64, f64)) -> bool { + pos.0 + rect.0 < point.0 && pos.0 > point.0 && pos.1 + rect.1 < point.1 && pos.1 > point.1 } -// -// enum CollisionGroup { -// Player, -// World, -// } -// -// impl CollisionGroup { -// pub fn should_collide(&self, other: &Self) -> bool { -// match (self, other) { -// (CollisionGroup::Player, CollisionGroup::Player) => todo!(), -// (CollisionGroup::Player, CollisionGroup::World) => todo!(), -// (CollisionGroup::World, CollisionGroup::Player) => todo!(), -// (CollisionGroup::World, CollisionGroup::World) => todo!(), -// } -// } -// } -// #[derive(Component, Clone, Default)] struct Collider { resolve: bool, + colliding_surface: Option, } fn horizontal_line_intersect(p0: (f64, f64), vel: (f64, f64), line_y: f64) -> Option<(f64, f64)> { @@ -106,7 +84,7 @@ fn vertical_line_intersect(p0: (f64, f64), vel: (f64, f64), line_x: f64) -> Opti Some((line_x, y)) } -#[derive(Debug)] +#[derive(Clone, Debug)] enum Surface { Top, Bottom, @@ -118,35 +96,35 @@ fn point_distance(a: (f64, f64), b: (f64, f64)) -> f64 { ((a.0 - b.0).abs().powi(2) + (a.1 - b.1).abs().powi(2)).sqrt() } -fn closest_surface_for_point_and_rectangle_and_your_mom( +fn point_rect_closest_surface( p0: (f64, f64), vel: (f64, f64), rect_pos: (f64, f64), - rect: &Rect, + rect: (f64, f64), ) -> Option<(f64, Surface)> { let closest_horizontal_intersect = [ (horizontal_line_intersect(p0, vel, rect_pos.1), Surface::Top), ( - horizontal_line_intersect(p0, vel, rect_pos.1 + rect.height), + horizontal_line_intersect(p0, vel, rect_pos.1 + rect.1), Surface::Bottom, ), ] .into_iter() .filter_map(|(intersect, surface)| intersect.map(|intersect| (intersect, surface))) .map(|(point, surface)| (point_distance(p0, point), surface)) - .min_by(|(dist_a, _), (dist_b, _)| dist_a.total_cmp(&dist_b)); + .min_by(|(dist_a, _), (dist_b, _)| dist_a.total_cmp(dist_b)); let closest_vertical_intersect = [ (vertical_line_intersect(p0, vel, rect_pos.0), Surface::Left), ( - vertical_line_intersect(p0, vel, rect_pos.0 + rect.width), + vertical_line_intersect(p0, vel, rect_pos.0 + rect.0), Surface::Right, ), ] .into_iter() .filter_map(|(intersect, surface)| intersect.map(|intersect| (intersect, surface))) .map(|(point, surface)| (point_distance(p0, point), surface)) - .min_by(|(dist_a, _), (dist_b, _)| dist_a.total_cmp(&dist_b)); + .min_by(|(dist_a, _), (dist_b, _)| dist_a.total_cmp(dist_b)); match (closest_horizontal_intersect, closest_vertical_intersect) { (Some(left), Some(right)) => { @@ -163,60 +141,61 @@ fn closest_surface_for_point_and_rectangle_and_your_mom( struct CollisionSystem; impl System for CollisionSystem { fn on_update(&self, ctx: &mut engine::Context, delta: f64) -> Result<(), engine::Error> { - for id in query!(ctx, RigidBody, Rect, Collider) { + for id in query!(ctx, RigidBody, Collider) { + let collider = ctx.entity_component::(id); + collider.colliding_surface = None; let collider = ctx.entity_component::(id).clone(); if !collider.resolve { continue; } let body = ctx.entity_component::(id).clone(); - let rect = ctx.entity_component::(id).clone(); - let vel = body.vel.clone(); - for other_id in query!(ctx, RigidBody, Rect, Collider) { + for other_id in query!(ctx, RigidBody, Collider) { if id == other_id { continue; } - let other_rect = ctx.entity_component::(other_id).clone(); let other_body = ctx.entity_component::(other_id).clone(); - if rect.rect_collides(body.pos, &other_rect, other_body.pos) { - let last_pos = (body.pos.0 - vel.0 * delta, body.pos.1 - vel.1 * delta); + if rects_collide(body.rect, body.pos, other_body.rect, other_body.pos) { + let last_pos = ( + body.pos.0 - body.vel.0 * delta, + body.pos.1 - body.vel.1 * delta, + ); + if rects_collide(body.rect, last_pos, other_body.rect, other_body.pos) { + panic!() + } let closest_surface = [ (last_pos.0, last_pos.1), - (last_pos.0, last_pos.1 + rect.height), - (last_pos.0 + rect.width, last_pos.1), - (last_pos.0 + rect.width, last_pos.1 + rect.height), + (last_pos.0, last_pos.1 + body.rect.1), + (last_pos.0 + body.rect.0, last_pos.1), + (last_pos.0 + body.rect.0, last_pos.1 + body.rect.1), ] .into_iter() - .map(|p0| { - closest_surface_for_point_and_rectangle_and_your_mom( - p0, - vel, - other_body.pos, - &other_rect, - ) + .filter_map(|p0| { + point_rect_closest_surface(p0, body.vel, other_body.pos, other_body.rect) }) - .filter_map(std::convert::identity) - .min_by(|(dist_a, _), (dist_b, _)| dist_a.total_cmp(&dist_b)) + .min_by(|(dist_a, _), (dist_b, _)| dist_a.total_cmp(dist_b)) .map(|(_, surface)| surface) - .ok_or_else(|| "we already checked if collision happens")?; + .ok_or("we already checked if collision happens")?; + let collider = ctx.entity_component::(id); + collider.colliding_surface = Some(closest_surface.clone()); let body = ctx.entity_component::(id); - println!("closest: {closest_surface:?} | vel: {:?}", vel); + println!("closest: {closest_surface:?} | vel: {:?}", body.vel); match closest_surface { Surface::Top => { body.vel.1 = 0.0; - body.pos.1 = other_body.pos.1 - rect.height; + body.pos.1 = other_body.pos.1 - body.rect.1; } Surface::Bottom => { body.vel.1 = 0.0; - body.pos.1 = other_body.pos.1 + other_rect.height; + body.pos.1 = other_body.pos.1 + other_body.rect.1; } Surface::Left => { body.vel.0 = 0.0; - body.pos.0 = other_body.pos.0 - rect.width; + body.pos.0 = other_body.pos.0 - body.rect.0; } Surface::Right => { body.vel.0 = 0.0; - body.pos.0 = other_body.pos.0 + other_rect.width; + body.pos.0 = other_body.pos.0 + other_body.rect.0; } } } @@ -234,7 +213,7 @@ impl System for GravitySystem { continue; } body.vel.1 = if body.vel.1 < 800.0 { - body.vel.1 + 400.0 * delta + body.vel.1 + 1600.0 * delta } else { body.vel.1 }; @@ -300,11 +279,12 @@ struct PlayerMovement; struct PlayerMovementSystem; impl System for PlayerMovementSystem { - fn on_update(&self, ctx: &mut engine::Context, delta: f64) -> Result<(), engine::Error> { - for id in query!(ctx, PlayerMovement, RigidBody) { + fn on_update(&self, ctx: &mut engine::Context, _delta: f64) -> Result<(), engine::Error> { + for id in query!(ctx, PlayerMovement, RigidBody, Collider) { let d_down = ctx.key_pressed(engine::Keycode::D); let a_down = ctx.key_pressed(engine::Keycode::A); let w_down = ctx.key_pressed(engine::Keycode::W); + let collider = ctx.entity_component::(id).clone(); let body = ctx.entity_component::(id); body.vel.0 = if d_down && !a_down { 400.0 @@ -313,8 +293,8 @@ impl System for PlayerMovementSystem { } else { 0.0 }; - if w_down { - body.vel.1 -= 1000.0 * delta; + if let (Some(Surface::Top), true) = (collider.colliding_surface, w_down) { + body.vel.1 = -1000.0; } } Ok(()) @@ -328,8 +308,8 @@ fn main() { context.add_system(VelocitySystem); context.add_system(CollisionSystem); context.add_system(SpriteRenderer); - context.add_system(GravitySystem); context.add_system(PlayerMovementSystem); + context.add_system(GravitySystem); context.add_system(CloudSystem); let player = context.load_sprite("textures/player.png").unwrap(); let background = context.load_sprite("textures/literally_dprk.png").unwrap(); @@ -346,14 +326,14 @@ fn main() { Sprite { sprite: player }, RigidBody { pos: (400.0, 400.0), + rect: (128.0, 128.0), gravity: true, ..Default::default() }, - Rect { - width: 128.0, - height: 128.0 + Collider { + resolve: true, + ..Default::default() }, - Collider { resolve: true }, PlayerMovement, ); @@ -361,54 +341,33 @@ fn main() { &mut context, RigidBody { pos: (184.0, 540.0), + rect: (960.0, 128.0), ..Default::default() }, - Rect { - width: 960.0, - height: 128.0 - }, - Collider { resolve: false }, + Collider::default(), ); spawn!( &mut context, RigidBody { - pos: (500.0, 200.0), + pos: (300.0, 200.0), + rect: (32.0, 32.0), ..Default::default() }, - Rect { - width: 32.0, - height: 32.0 - }, - Collider { resolve: false }, + Collider::default(), Sprite { sprite: nope }, ); spawn!( &mut context, RigidBody { - pos: (872.0, 450.0), + pos: (900.0, 400.0), + rect: (32.0, 32.0), ..Default::default() }, - Rect { - width: 32.0, - height: 32.0 - }, - Collider { resolve: false }, + Collider::default(), Sprite { sprite: nope }, ); - // spawn!( - // &mut context, - // RigidBody { - // pos: (872.0, 360.0), - // ..Default::default() - // }, - // Rect { - // width: 388.0, - // height: 48.0 - // }, - // Collider { resolve: false }, - // ); game.run(); }