attempt to not be twash

This commit is contained in:
Mikkel Kongsted 2024-04-11 10:54:41 +02:00
parent fca8469824
commit 35dfc37fcb
2 changed files with 89 additions and 47 deletions

76
src/cuwusions.rs Normal file
View File

@ -0,0 +1,76 @@
// assume collision resolution takes place before velocity resolution
pub fn rects_collision_resolution_pos(
pos: (f64, f64),
vel: (f64, f64),
rect: (f64, f64),
other_pos: (f64, f64),
other_rect: (f64, f64),
) -> (f64, f64) {
'top_left: {
let Some((intersection, dist)) =
closest_point_rect_intersection(pos, vel, other_pos, other_rect)
else {
let (x, y) = pos;
break 'top_left (x + vel.0, y + vel.1);
};
intersection
}
}
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 horizontal_line_intersect(p0: (f64, f64), vel: (f64, f64), line_y: f64) -> Option<(f64, f64)> {
if vel.1 == 0.0 {
// exclusively going left or right, ergo there's no collision with a horizontal line
return None;
} else if vel.0 == 0.0 {
// no change in x, ergo the intersect must be at p0_x
return Some((p0.0, line_y));
}
// y = ax + b
let a = vel.1 / vel.0;
let b = p0.1 - p0.0 * a;
let x = (line_y - b) / a;
Some((x, line_y))
}
fn vertical_line_intersect(p0: (f64, f64), vel: (f64, f64), line_x: f64) -> Option<(f64, f64)> {
if vel.0 == 0.0 {
// exclusively going up or down, ergo there's no collision with a vertical line
return None;
} else if vel.1 == 0.0 {
// no change in y, ergo the intersect must be at p0_y
return Some((line_x, p0.1));
}
//
// y = ax + b
let a = vel.1 / vel.0;
let b = p0.1 - p0.0 * a;
let y = a * line_x + b;
Some((line_x, y))
}
fn closest_point_rect_intersection(
p0: (f64, f64),
vel: (f64, f64),
pos: (f64, f64),
rect: (f64, f64),
) -> Option<((f64, f64), f64)> {
[
horizontal_line_intersect(p0, vel, pos.1),
horizontal_line_intersect(p0, vel, pos.1 + rect.1),
vertical_line_intersect(p0, vel, pos.0 + rect.0),
vertical_line_intersect(p0, vel, pos.0),
]
.into_iter()
.flatten()
.map(|point| (point, point_distance(p0, point)))
.filter(|(_, dist)| *dist <= point_distance((0.0, 0.0), vel))
.min_by(|(_, dist_a), (_, dist_b)| dist_a.total_cmp(dist_b))
}

View File

@ -1,5 +1,6 @@
#![allow(dead_code)] #![allow(dead_code)]
mod cuwusions;
mod engine; mod engine;
use engine::{Component, System}; use engine::{Component, System};
@ -9,7 +10,7 @@ struct Sprite {
sprite: engine::Sprite, sprite: engine::Sprite,
} }
#[derive(Component, Default, Clone)] #[derive(Component, Default, Clone, Debug)]
struct RigidBody { struct RigidBody {
pos: (f64, f64), pos: (f64, f64),
vel: (f64, f64), vel: (f64, f64),
@ -22,8 +23,8 @@ 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 query!(ctx, RigidBody) { for id in query!(ctx, RigidBody) {
let body = ctx.entity_component::<RigidBody>(id); let body = ctx.entity_component::<RigidBody>(id);
body.pos.0 += body.vel.0 * delta; // body.pos.0 += body.vel.0 * delta;
body.pos.1 += body.vel.1 * delta; // body.pos.1 += body.vel.1 * delta;
} }
Ok(()) Ok(())
} }
@ -154,51 +155,16 @@ impl System for CollisionSystem {
continue; continue;
} }
let other_body = ctx.entity_component::<RigidBody>(other_id).clone(); let other_body = ctx.entity_component::<RigidBody>(other_id).clone();
if rects_collide(body.rect, body.pos, other_body.rect, other_body.pos) { let new_pos = cuwusions::rects_collision_resolution_pos(
let last_pos = ( body.pos,
body.pos.0 - body.vel.0 * delta, (body.vel.0 * delta, body.vel.1 * delta),
body.pos.1 - body.vel.1 * delta, body.rect,
); other_body.pos,
if rects_collide(body.rect, last_pos, other_body.rect, other_body.pos) { other_body.rect,
panic!() );
}
let closest_surface = [
(last_pos.0, last_pos.1),
(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()
.filter_map(|p0| {
point_rect_closest_surface(p0, body.vel, other_body.pos, other_body.rect)
})
.min_by(|(dist_a, _), (dist_b, _)| dist_a.total_cmp(dist_b))
.map(|(_, surface)| surface)
.ok_or("we already checked if collision happens")?;
let collider = ctx.entity_component::<Collider>(id); let body = ctx.entity_component::<RigidBody>(id);
collider.colliding_surface = Some(closest_surface.clone()); body.pos = new_pos;
let body = ctx.entity_component::<RigidBody>(id);
println!("closest: {closest_surface:?} | vel: {:?}", body.vel);
match closest_surface {
Surface::Top => {
body.vel.1 = 0.0;
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_body.rect.1;
}
Surface::Left => {
body.vel.0 = 0.0;
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_body.rect.0;
}
}
}
} }
} }
Ok(()) Ok(())