diff --git a/src/main.rs b/src/main.rs index 745ae4b..a473154 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ mod player; use bevy::{image::Image, math::I64Vec2, prelude::*}; use crate::{ - physics::{Physics2DPlugin, PhysicsBody2D}, + physics::{AABBCollider, Physics2DPlugin, PhysicsBody2D}, player::Player, }; @@ -158,12 +158,14 @@ fn levelload( let mut spawned_ent = commands.spawn(( Name::from(ei.identifier.clone()), Transform::from_xyz(pos.x as f32, pos.y as f32, 100.), + AABBCollider::new(16 * UNITS_TO_PX_SCALE as u32, 16 * UNITS_TO_PX_SCALE as u32), )); match ei.identifier.as_str() { "Player" => { spawned_ent.insert(Player::default()); spawned_ent.insert(PhysicsBody2D { pos: pos * UNITS_TO_PX_SCALE, + immovable: false, ..default() }); spawned_ent.insert(Sprite::from_atlas_image( @@ -176,6 +178,12 @@ fn levelload( } "Parsnip" => { spawned_ent.insert(Sprite::from_image(parsnip.clone())); + + spawned_ent.insert(PhysicsBody2D { + pos: pos * UNITS_TO_PX_SCALE, + immovable: true, + ..default() + }); } unk => { spawned_ent.insert(Sprite::from_color(Color::srgb(1., 0., 1.), vec2(16., 16.))); diff --git a/src/physics.rs b/src/physics.rs index 37519e5..95d3289 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -1,3 +1,5 @@ +use std::ops::DerefMut; + /// This module is intended to be used as a fixed-point physics system. /// The hope is that this will make deterministic physics easier, /// and therefore networking a bit less of a pain. @@ -16,7 +18,7 @@ pub struct Physics2DWorld { impl Plugin for Physics2DPlugin { fn build(&self, app: &mut bevy::app::App) { app.insert_resource(Physics2DWorld::default()) - .add_systems(Update, tick_physics); + .add_systems(Update, (tick_physics, resolve_aabb_collisions).chain()); } } @@ -39,10 +41,100 @@ fn tick_physics( }); } -#[derive(Default, Debug, Component)] +fn resolve_aabb_collisions(mut query: Query<(&mut PhysicsBody2D, &AABBCollider)>) { + let bcs: Vec<_> = query.iter_mut().collect(); + + let len = bcs.len(); + + let mut updated_bcs: Vec<(usize, PhysicsBody2D)> = Vec::new(); + + for i in 0..len { + let (b, c) = &bcs[i]; + + // don't bother collision checking immovables, + // instead movable entities will check against them + if b.immovable { + continue; + } + + for j in 0..len { + if i == j { + continue; + } + let (other_b, other_c) = &bcs[j]; + + let bminx = b.pos.x; + let bmaxx = b.pos.x + c.width as i64; + let bminy = b.pos.y; + let bmaxy = b.pos.y + c.height as i64; + + let obminx = other_b.pos.x; + let obmaxx = other_b.pos.x + other_c.width as i64; + let obminy = other_b.pos.y; + let obmaxy = other_b.pos.y + other_c.height as i64; + + if bminx >= obmaxx || bminy >= obmaxy || bmaxx <= obminx || bmaxy <= obminy { + // no collision + continue; + } + + info!("Collide!"); + + let dx = b.pos.x - other_b.pos.x; + let dy = b.pos.y - other_b.pos.y; + + if other_b.immovable { + let new_x = match dx { + ..0 => other_b.pos.x - (c.width / 2 + other_c.width / 2) as i64, + 0 => b.pos.x, + 1.. => other_b.pos.x + (c.width / 2 + other_c.width / 2) as i64, + }; + let new_y = match dy { + ..0 => other_b.pos.y - (c.height / 2 + other_c.height / 2) as i64, + 0 => b.pos.y, + 1.. => other_b.pos.y + (c.height / 2 + other_c.height / 2) as i64, + }; + + let updated_b = PhysicsBody2D { + pos: I64Vec2 { x: new_x, y: new_y }, + vel: b.vel, + immovable: false, + }; + updated_bcs.push((i, updated_b)); + } else { + let updated_b = PhysicsBody2D { + pos: b.pos - I64Vec2::new(dx / 2, dy / 2), + vel: b.vel, + immovable: false, + }; + + let other_updated_b = PhysicsBody2D { + pos: other_b.pos + I64Vec2::new(dx / 2, dy / 2), + vel: other_b.vel, + immovable: false, + }; + + updated_bcs.push((i, updated_b)); + + updated_bcs.push((i, other_updated_b)); + } + } + } + + for (i, (mut b, c)) in query.iter_mut().enumerate() { + let Some((_, updateb)) = updated_bcs.iter().find(|(j, _)| i == *j) else { + continue; + }; + + *b = updateb.clone(); + } +} + +#[derive(Default, Debug, Component, Clone, Copy)] pub struct PhysicsBody2D { pub pos: I64Vec2, pub vel: I64Vec2, + pub immovable: bool, } impl PhysicsBody2D { @@ -72,3 +164,24 @@ impl PhysicsBody2D { self.vel = vel; } } + +#[derive(Default, Component)] +pub struct Collideable; + +#[derive(Default, Debug, Component)] +#[require(Collideable)] +pub struct AABBCollider { + pub width: u32, + pub height: u32, + pub offset: IVec2, +} + +impl AABBCollider { + pub fn new(w: u32, h: u32) -> Self { + Self { + width: w, + height: h, + offset: IVec2::ZERO, + } + } +}