collision so hawd >~<

This commit is contained in:
SimonFJ20 2024-04-10 15:15:59 +02:00
parent ccec00fcc0
commit 7609eb3e4b
3 changed files with 292 additions and 82 deletions

View File

@ -3,6 +3,7 @@ use std::collections::HashSet;
use std::rc::Rc; use std::rc::Rc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
pub use component_macro::Component;
pub use sdl2::keyboard::Keycode; pub use sdl2::keyboard::Keycode;
use sdl2::{ use sdl2::{
@ -69,19 +70,19 @@ where
currently_pressed_keys: &'context HashSet<Keycode>, currently_pressed_keys: &'context HashSet<Keycode>,
} }
pub struct Quwi<T>(std::marker::PhantomData<T>); pub struct ComponentQuery<T>(std::marker::PhantomData<T>);
impl<T> Quwi<T> { impl<T> ComponentQuery<T> {
pub fn new() -> Self { pub fn new() -> Self {
Self(std::marker::PhantomData) Self(std::marker::PhantomData)
} }
} }
pub trait QuwiQuwi { pub trait QueryRunner {
fn run(&self, context: &Context) -> Vec<u64>; fn run(&self, context: &Context) -> Vec<u64>;
} }
impl<T0> QuwiQuwi for Quwi<T0> impl<T0> QueryRunner for ComponentQuery<T0>
where where
T0: 'static + Component, T0: 'static + Component,
{ {
@ -90,7 +91,7 @@ where
} }
} }
impl<T0, T1> QuwiQuwi for Quwi<(T0, T1)> impl<T0, T1> QueryRunner for ComponentQuery<(T0, T1)>
where where
T0: 'static + Component, T0: 'static + Component,
T1: 'static + Component, T1: 'static + Component,
@ -104,7 +105,7 @@ where
} }
} }
impl<T0, T1, T2> QuwiQuwi for Quwi<(T0, T1, T2)> impl<T0, T1, T2> QueryRunner for ComponentQuery<(T0, T1, T2)>
where where
T0: 'static + Component, T0: 'static + Component,
T1: 'static + Component, T1: 'static + Component,
@ -123,6 +124,33 @@ where
} }
} }
#[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> { impl<'context, 'game> Context<'context, 'game> {
pub fn entities_with_component<T: 'static + Component>(&self) -> Vec<u64> { pub fn entities_with_component<T: 'static + Component>(&self) -> Vec<u64> {
let entity_type_id = TypeId::of::<T>(); let entity_type_id = TypeId::of::<T>();
@ -203,9 +231,9 @@ impl<'context, 'game> Context<'context, 'game> {
.collect(); .collect();
} }
pub fn add_system(&mut self, system: Rc<dyn System>) { pub fn add_system<S: 'static + System>(&mut self, system: S) {
system.on_add(self); system.on_add(self);
self.systems.push(system) self.systems.push(Rc::new(system))
} }
pub fn key_pressed(&self, keycode: Keycode) -> bool { pub fn key_pressed(&self, keycode: Keycode) -> bool {
@ -248,7 +276,7 @@ impl<'game> Game<'game> {
let mut canvas = window.into_canvas().build()?; let mut canvas = window.into_canvas().build()?;
let texture_creator = canvas.texture_creator(); let texture_creator = canvas.texture_creator();
canvas.set_draw_color(Color::RGB(60, 180, 180)); canvas.set_draw_color(Color::BLACK);
canvas.clear(); canvas.clear();
canvas.present(); canvas.present();
let event_pump = sdl_context.event_pump()?; let event_pump = sdl_context.event_pump()?;
@ -317,4 +345,9 @@ impl<'game> Game<'game> {
currently_pressed_keys: &mut self.currently_pressed_keys, currently_pressed_keys: &mut self.currently_pressed_keys,
} }
} }
pub fn add_system<S: 'static + System>(&mut self, system: S) {
system.on_add(&mut self.context());
self.systems.push(Rc::new(system))
}
} }

View File

@ -2,63 +2,210 @@
mod engine; mod engine;
use component_macro::Component; use engine::{Component, System};
use engine::Component;
use engine::QuwiQuwi;
use engine::System;
use std::rc::Rc;
#[derive(Component)] #[derive(Component)]
struct Sprite { struct Sprite {
sprite: engine::Sprite, sprite: engine::Sprite,
} }
macro_rules! tuplify { #[derive(Component, Default, Clone)]
($t:ty) => { struct RigidBody {
$t pos: (f64, f64),
}; vel: (f64, f64),
($t:ty, $($ts:ty),+) => { gravity: bool,
$t, tuplify!($($ts),+) cowision: bool,
};
} }
macro_rules! run_quwi {
($ctx:expr, $t:ty) => {
engine::Quwi::<$t>::new().run($ctx)
};
($ctx:expr, $t:ty, $($ts:ty),+) => {
engine::Quwi::<($t, tuplify!($($ts),+))>::new().run($ctx)
};
}
#[derive(Component)]
struct Position(f64, f64);
#[derive(Clone, Component)]
struct Velocity(f64, f64);
struct VelocitySystem; struct VelocitySystem;
impl System for VelocitySystem { impl System for VelocitySystem {
fn on_update(&self, ctx: &mut engine::Context, delta: f64) -> Result<(), engine::Error> { fn on_update(&self, ctx: &mut engine::Context, delta: f64) -> Result<(), engine::Error> {
for id in run_quwi!(ctx, Velocity, Position) { for id in query!(ctx, RigidBody) {
let vel = ctx.entity_component::<Velocity>(id).clone(); let body = ctx.entity_component::<RigidBody>(id);
let Position(x, y) = ctx.entity_component::<Position>(id); body.pos.0 += body.vel.0 * delta;
*x += vel.0 * delta; body.pos.1 += body.vel.1 * delta;
*y += vel.1 * delta;
} }
Ok(()) Ok(())
} }
} }
#[derive(Component)] #[derive(Component, Clone)]
struct Gravity; struct Rect {
width: f64,
height: f64,
}
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
}
}
//
// 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,
}
fn horizontal_line_intersect(p0: (f64, f64), vel: (f64, f64), line_y: f64) -> (f64, f64) {
// y = ax + b
let a = vel.1 / vel.0;
let b = p0.1 - p0.0 * a;
let x = (line_y - b) / a;
(x, line_y)
}
fn vertical_line_intersect(p0: (f64, f64), vel: (f64, f64), line_x: f64) -> (f64, f64) {
// y = ax + b
let a = vel.1 / vel.0;
let b = p0.1 - p0.0 * a;
let y = a * line_x + b;
(line_x, y)
}
enum Surface {
Top,
Bottom,
Left,
Right,
}
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(
p0: (f64, f64),
vel: (f64, f64),
rect_pos: (f64, f64),
rect: &Rect,
) -> Option<(f64, Surface)> {
[
(horizontal_line_intersect(p0, vel, rect_pos.1), Surface::Top),
(
horizontal_line_intersect(p0, vel, rect_pos.1 + rect.height),
Surface::Bottom,
),
(vertical_line_intersect(p0, vel, rect_pos.0), Surface::Left),
(
horizontal_line_intersect(p0, vel, rect_pos.0 + rect.width),
Surface::Right,
),
]
.into_iter()
.filter(|(point, _)| rect.point_collides(rect_pos, *point))
.map(|(point, surface)| (point_distance(p0, point), surface))
.min_by(|(dist_a, _), (dist_b, _)| dist_a.total_cmp(&dist_b))
}
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) {
let collider = ctx.entity_component::<Collider>(id).clone();
if !collider.resolve {
continue;
}
let body = ctx.entity_component::<RigidBody>(id).clone();
let rect = ctx.entity_component::<Rect>(id).clone();
for other_id in query!(ctx, RigidBody, Rect, Collider) {
if id == other_id {
continue;
}
let other_rect = ctx.entity_component::<Rect>(id).clone();
let other_body = ctx.entity_component::<RigidBody>(id).clone();
if rect.rect_collides(body.pos, &other_rect, other_body.pos) {
println!("collider vi?");
let last_pos = (
body.pos.0 - body.vel.0 * delta,
body.pos.1 - body.vel.1 * delta,
);
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),
]
.into_iter()
.map(|p0| {
closest_surface_for_point_and_rectangle_and_your_mom(
p0,
body.vel,
other_body.pos,
&other_rect,
)
})
.filter_map(std::convert::identity)
.min_by(|(dist_a, _), (dist_b, _)| dist_a.total_cmp(&dist_b))
.map(|(_, surface)| surface)
.ok_or_else(|| "we already checked if collision happens")?;
let body = ctx.entity_component::<RigidBody>(id);
match closest_surface {
Surface::Top => {
body.vel.1 = 0.0;
body.pos.1 = other_body.pos.1 - rect.height;
}
Surface::Bottom => {
body.vel.1 = 0.0;
body.pos.1 = other_body.pos.1 + other_rect.height;
}
Surface::Left => {
body.vel.0 = 0.0;
body.pos.0 = other_body.pos.0 + other_rect.width;
}
Surface::Right => {
body.vel.0 = 0.0;
body.pos.0 = other_body.pos.0 - rect.width;
}
}
}
}
}
Ok(())
}
}
struct GravitySystem; struct GravitySystem;
impl System for GravitySystem { impl System for GravitySystem {
fn on_update(&self, ctx: &mut engine::Context, delta: f64) -> Result<(), engine::Error> { fn on_update(&self, ctx: &mut engine::Context, delta: f64) -> Result<(), engine::Error> {
for id in run_quwi!(ctx, Gravity, Velocity) { for id in query!(ctx, RigidBody) {
let Velocity(_, y) = ctx.entity_component::<Velocity>(id); let body = ctx.entity_component::<RigidBody>(id);
*y = if *y < 800.0 { *y + 400.0 * delta } else { *y }; if !body.gravity {
continue;
}
body.vel.1 = if body.vel.1 < 800.0 {
body.vel.1 + 400.0 * delta
} else {
body.vel.1
};
} }
Ok(()) Ok(())
} }
@ -68,28 +215,34 @@ impl System for GravitySystem {
struct Cloud; struct Cloud;
struct CloudSystem; struct CloudSystem;
impl System for CloudSystem { impl System for CloudSystem {
fn on_update(&self, ctx: &mut engine::Context, delta: f64) -> Result<(), engine::Error> { fn on_update(&self, ctx: &mut engine::Context, delta: f64) -> Result<(), engine::Error> {
let cloud_amount = ctx.entities_with_component::<Cloud>().len(); let cloud_amount = ctx.entities_with_component::<Cloud>().len();
if cloud_amount < 1 { if cloud_amount < 1 {
let cloud = ctx.load_sprite("textures/clouds.png").unwrap(); let cloud = ctx.load_sprite("textures/clouds.png").unwrap();
ctx.spawn(vec![ spawn!(
Box::new(Cloud), ctx,
Box::new(Sprite { sprite: cloud }), Cloud,
Box::new(Position(-100.0, 150.0)), Sprite { sprite: cloud },
Box::new(Velocity(0.0, 0.0)), RigidBody {
]); pos: (-100.0, 150.0),
..Default::default()
},
);
} }
for id in run_quwi!(ctx, Cloud, Velocity) { for id in query!(ctx, Cloud, RigidBody) {
let Velocity(x, _) = ctx.entity_component::<Velocity>(id); let body = ctx.entity_component::<RigidBody>(id);
*x = if *x < 200.0 { *x + 200.0 * delta } else { *x }; body.vel.0 = if body.vel.0 < 200.0 {
body.vel.0 + 200.0 * delta
} else {
body.vel.0
};
} }
for id in run_quwi!(ctx, Cloud, Velocity) { for id in query!(ctx, Cloud, RigidBody) {
let Position(x, _) = ctx.entity_component::<Position>(id); let body = ctx.entity_component::<RigidBody>(id);
if *x > 1400.0 { if body.pos.0 > 1400.0 {
ctx.despawn(id); ctx.despawn(id);
} }
} }
@ -100,11 +253,11 @@ impl System for CloudSystem {
struct SpriteRenderer; struct SpriteRenderer;
impl System for SpriteRenderer { impl System for SpriteRenderer {
fn on_update(&self, ctx: &mut engine::Context, _delta: f64) -> Result<(), engine::Error> { fn on_update(&self, ctx: &mut engine::Context, _delta: f64) -> Result<(), engine::Error> {
for id in run_quwi!(ctx, Sprite, Position) { for id in query!(ctx, Sprite, RigidBody) {
let &mut Position(x, y) = ctx.entity_component::<Position>(id); let body = ctx.entity_component::<RigidBody>(id).clone();
let sprite = ctx.entity_component::<Sprite>(id).sprite; let sprite = ctx.entity_component::<Sprite>(id).sprite;
ctx.draw_sprite(sprite, x as i32, y as i32)?; ctx.draw_sprite(sprite, body.pos.0 as i32, body.pos.1 as i32)?;
} }
Ok(()) Ok(())
} }
@ -116,11 +269,11 @@ struct PlayerMovement;
struct PlayerMovementSystem; struct PlayerMovementSystem;
impl System for PlayerMovementSystem { impl System for PlayerMovementSystem {
fn on_update(&self, ctx: &mut engine::Context, _delta: f64) -> Result<(), engine::Error> { fn on_update(&self, ctx: &mut engine::Context, _delta: f64) -> Result<(), engine::Error> {
for id in run_quwi!(ctx, PlayerMovement, Velocity) { for id in query!(ctx, PlayerMovement, RigidBody) {
let d_down = ctx.key_pressed(engine::Keycode::D); let d_down = ctx.key_pressed(engine::Keycode::D);
let a_down = ctx.key_pressed(engine::Keycode::A); let a_down = ctx.key_pressed(engine::Keycode::A);
let Velocity(x, _) = ctx.entity_component::<Velocity>(id); let body = ctx.entity_component::<RigidBody>(id);
*x = if d_down && !a_down { body.vel.0 = if d_down && !a_down {
400.0 400.0
} else if !d_down && a_down { } else if !d_down && a_down {
-400.0 -400.0
@ -136,25 +289,49 @@ fn main() {
let mut game = engine::Game::new().unwrap(); let mut game = engine::Game::new().unwrap();
let mut context = game.context(); let mut context = game.context();
context.add_system(Rc::new(VelocitySystem)); context.add_system(VelocitySystem);
context.add_system(Rc::new(SpriteRenderer)); context.add_system(CollisionSystem);
context.add_system(Rc::new(GravitySystem)); context.add_system(SpriteRenderer);
context.add_system(Rc::new(PlayerMovementSystem)); context.add_system(GravitySystem);
context.add_system(Rc::new(CloudSystem)); context.add_system(PlayerMovementSystem);
context.add_system(CloudSystem);
let player = context.load_sprite("textures/player.png").unwrap(); let player = context.load_sprite("textures/player.png").unwrap();
let background = context.load_sprite("textures/mountains.png").unwrap(); let background = context.load_sprite("textures/literally_dprk.png").unwrap();
context.spawn(vec![ spawn!(
Box::new(Sprite { sprite: background }), &mut context,
Box::new(Position(0.0, 0.0)), Sprite { sprite: background },
]); RigidBody::default(),
);
spawn!(
&mut context,
Sprite { sprite: player },
RigidBody {
pos: (400.0, 400.0),
gravity: true,
..Default::default()
},
Rect {
width: 256.0,
height: 256.0
},
Collider { resolve: true },
PlayerMovement,
);
spawn!(
&mut context,
RigidBody {
pos: (0.0, 650.0),
..Default::default()
},
Rect {
width: 1000.0,
height: 25.0
},
Collider { resolve: false },
);
context.spawn(vec![
Box::new(Sprite { sprite: player }),
Box::new(Position(16.0, 500.0)),
Box::new(Velocity(0.0, -600.0)),
Box::new(Gravity),
Box::new(PlayerMovement),
]);
game.run(); game.run();
} }

BIN
textures/literally_dprk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB