From 2354971d8e7916c9a341c3492957ddb0656fa081 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 28 Dec 2023 20:19:33 -0800 Subject: [PATCH] Added collisions --- Cargo.lock | 246 +++++++++++++++++++++++++++--- Cargo.toml | 3 + content/ship.toml | 5 +- src/content/ship.rs | 14 +- src/content/system.rs | 2 +- src/game/game.rs | 131 +++++++++++++--- src/game/mod.rs | 4 +- src/game/physics.rs | 60 ++++++++ src/game/ship/mod.rs | 4 +- src/game/ship/ship.rs | 81 ++++++---- src/game/util.rs | 27 ++++ src/main.rs | 2 +- src/physics/body.rs | 48 ------ src/physics/mod.rs | 6 - src/render/mod.rs | 2 +- src/render/sprite.rs | 4 - src/{physics/polar.rs => util.rs} | 0 17 files changed, 497 insertions(+), 142 deletions(-) create mode 100644 src/game/physics.rs create mode 100644 src/game/util.rs delete mode 100644 src/physics/body.rs delete mode 100644 src/physics/mod.rs rename src/{physics/polar.rs => util.rs} (100%) diff --git a/Cargo.lock b/Cargo.lock index 36bf207..7c9ab04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,6 +99,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -273,7 +282,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" dependencies = [ - "approx", + "approx 0.4.0", "num-traits", ] @@ -348,6 +357,30 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eb9105919ca8e40d437fc9cbb8f1975d916f1bd28afe795a48aae32a2cc8920" +dependencies = [ + "cfg-if", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.4" @@ -361,21 +394,30 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.16" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" +dependencies = [ + "cfg-if", + "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" dependencies = [ "cfg-if", ] @@ -538,9 +580,12 @@ dependencies = [ "anyhow", "bytemuck", "cgmath", + "crossbeam", "image", + "nalgebra", "pollster", "rand", + "rapier2d", "serde", "toml", "walkdir", @@ -831,6 +876,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "libredox" version = "0.0.2" @@ -867,6 +918,16 @@ dependencies = [ "libc", ] +[[package]] +name = "matrixmultiply" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "memchr" version = "2.6.4" @@ -891,15 +952,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - [[package]] name = "metal" version = "0.27.0" @@ -957,6 +1009,33 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "nalgebra" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" +dependencies = [ + "approx 0.5.1", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "nanorand" version = "0.7.0" @@ -1004,7 +1083,7 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset 0.6.5", + "memoffset", ] [[package]] @@ -1017,7 +1096,27 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset 0.6.5", + "memoffset", +] + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1048,6 +1147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1193,6 +1293,27 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "parry2d" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104ae65232e20477a98f9f1e75ca9850eae24a2ea846a2b1a0af03ad752136ce" +dependencies = [ + "approx 0.5.1", + "arrayvec", + "bitflags 1.3.2", + "downcast-rs", + "either", + "nalgebra", + "num-derive", + "num-traits", + "rustc-hash", + "simba", + "slab", + "smallvec", + "spade", +] + [[package]] name = "paste" version = "1.0.14" @@ -1321,12 +1442,38 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" +[[package]] +name = "rapier2d" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f94d294a9b96694c14888dd0e8ce77620dcc4f2f49264109ef835fa5e2285b84" +dependencies = [ + "approx 0.5.1", + "arrayvec", + "bit-vec", + "bitflags 1.3.2", + "crossbeam", + "downcast-rs", + "nalgebra", + "num-derive", + "num-traits", + "parry2d", + "rustc-hash", + "simba", +] + [[package]] name = "raw-window-handle" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rayon" version = "1.8.0" @@ -1371,6 +1518,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" +[[package]] +name = "robust" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30" + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1383,6 +1536,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "safe_arch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -1446,12 +1608,34 @@ dependencies = [ "serde", ] +[[package]] +name = "simba" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +dependencies = [ + "approx 0.5.1", + "num-complex", + "num-traits", + "paste", + "wide", +] + [[package]] name = "simd-adler32" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "slotmap" version = "1.0.7" @@ -1486,6 +1670,18 @@ dependencies = [ "wayland-protocols", ] +[[package]] +name = "spade" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd774eb23cff002036706e6ea83c3f4ab4c80dad89da76fe16d49f77ab71682f" +dependencies = [ + "hashbrown", + "num-traits", + "robust", + "smallvec", +] + [[package]] name = "spin" version = "0.9.8" @@ -1655,6 +1851,12 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -1958,6 +2160,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wide" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "widestring" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index f1d508b..d537eab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,3 +41,6 @@ anyhow = "1.0" cgmath = "0.18.0" rand = "0.8.5" walkdir = "2.4.0" +rapier2d = "0.17.2" +nalgebra = "0.32.3" +crossbeam = "0.8.3" diff --git a/content/ship.toml b/content/ship.toml index 1b15216..bd84540 100644 --- a/content/ship.toml +++ b/content/ship.toml @@ -2,5 +2,6 @@ sprite = "ship::gypsum" size = 100 -engines = [{ x = 0.0, y = -105, size = 50.0 }] -guns = [{ x = 0.0, y = 100 }, { x = 10.0, y = 80 }, { x = -10.0, y = 80 }] +engines = [{ x = 0.0, y = -1.05, size = 50.0 }] +guns = [{ x = 0.0, y = 1 }, { x = 0.1, y = 0.80 }, { x = -0.1, y = 0.80 }] + diff --git a/src/content/ship.rs b/src/content/ship.rs index 64ee67e..5b4c583 100644 --- a/src/content/ship.rs +++ b/src/content/ship.rs @@ -61,15 +61,20 @@ impl super::Build for Ship { let mut out = Vec::new(); for (ship_name, ship) in ship { + let size = ship.size; + out.push(Self { name: ship_name.to_owned(), sprite: ship.sprite.to_owned(), - size: ship.size, + size, engines: ship .engines .iter() .map(|e| EnginePoint { - pos: Point2 { x: e.x, y: e.y }, + pos: Point2 { + x: e.x * size, + y: e.y * size, + }, size: e.size, }) .collect(), @@ -77,7 +82,10 @@ impl super::Build for Ship { .guns .iter() .map(|e| GunPoint { - pos: Point2 { x: e.x, y: e.y }, + pos: Point2 { + x: e.x * size, + y: e.y * size, + }, }) .collect(), }); diff --git a/src/content/system.rs b/src/content/system.rs index 14555e4..5512771 100644 --- a/src/content/system.rs +++ b/src/content/system.rs @@ -2,7 +2,7 @@ use anyhow::{bail, Context, Result}; use cgmath::{Deg, Point3}; use std::collections::{HashMap, HashSet}; -use crate::physics::Polar; +use crate::util::Polar; pub(super) mod syntax { use super::HashMap; diff --git a/src/game/game.rs b/src/game/game.rs index 9edd315..e862bd6 100644 --- a/src/game/game.rs +++ b/src/game/game.rs @@ -1,27 +1,39 @@ -use cgmath::{Deg, Point2, Point3, Vector2}; -use std::time::Instant; +use cgmath::Point3; +use crossbeam::{self, channel::Receiver}; +use rapier2d::{ + dynamics::{RigidBodyBuilder, RigidBodyHandle}, + geometry::{ColliderBuilder, ColliderHandle, CollisionEvent}, + na::vector, + pipeline::ChannelEventCollector, +}; +use std::{collections::HashMap, time::Instant}; use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode}; -use super::{ship, Camera, InputStatus, System}; +use super::{camera::Camera, physics::Physics, ship, system::System, util, InputStatus}; use crate::{ consts, content::Content, - render::Spriteable, render::{Sprite, SpriteTexture}, }; -pub struct Projectile { - pub position: Point2, - pub velocity: Vector2, +pub struct ProjectileBuilder { + pub rigid_body: RigidBodyBuilder, + pub collider: ColliderBuilder, + pub sprite: SpriteTexture, + pub lifetime: f32, + pub size: f32, +} + +#[derive(Debug)] +pub struct Projectile { + pub rigid_body: RigidBodyHandle, pub sprite: SpriteTexture, - pub angle: Deg, pub lifetime: f32, pub size: f32, } impl Projectile { pub fn tick(&mut self, t: f32) { - self.position += self.velocity * t; self.lifetime -= t; } @@ -38,16 +50,52 @@ pub struct Game { pub system: System, pub camera: Camera, paused: bool, - pub projectiles: Vec, pub time_scale: f32, + + pub projectiles: HashMap, + + collision_handler: ChannelEventCollector, + physics: Physics, + + collision_recv: Receiver, } impl Game { pub fn new(ct: Content) -> Self { + let r1 = RigidBodyBuilder::dynamic() + .translation(vector![0.0, 0.0]) + .can_sleep(false) + .build(); + let c1 = ColliderBuilder::ball(50.0) + .restitution(0.7) + .mass(1.0) + .build(); + + let r2 = RigidBodyBuilder::dynamic() + .translation(vector![200.0, 200.0]) + .build(); + let c2 = ColliderBuilder::ball(50.0) + .restitution(0.7) + .mass(100.0) + .build(); + + let mut p = Physics::new(); + + let rh1 = p.rigid_body_set.insert(r1); + p.collider_set + .insert_with_parent(c1, rh1, &mut p.rigid_body_set); + + let rh2 = p.rigid_body_set.insert(r2); + p.collider_set + .insert_with_parent(c2, rh2, &mut p.rigid_body_set); + + let (collision_send, collision_recv) = crossbeam::channel::unbounded(); + let (contact_force_send, _) = crossbeam::channel::unbounded(); + Game { last_update: Instant::now(), input: InputStatus::new(), - projectiles: Vec::new(), + projectiles: HashMap::new(), player: ship::Ship::new( &ct.ships[0], vec![ @@ -55,7 +103,7 @@ impl Game { ship::ShipOutfit::Gun(ship::ShipGun::new(ct.guns[0].clone(), 2)), ship::ShipOutfit::Engine(ct.engines[0].clone()), ], - (0.0, 0.0).into(), + rh1, ), camera: Camera { pos: (0.0, 0.0).into(), @@ -65,7 +113,11 @@ impl Game { paused: false, time_scale: 1.0, - test: ship::Ship::new(&ct.ships[0], vec![], (100.0, 100.0).into()), + test: ship::Ship::new(&ct.ships[0], vec![], rh2), + + physics: p, + collision_handler: ChannelEventCollector::new(collision_send, contact_force_send), + collision_recv, } } @@ -93,16 +145,41 @@ impl Game { pub fn update(&mut self) { let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale; - self.projectiles.retain_mut(|p| { + self.physics.step(t, &self.collision_handler); + + self.projectiles.retain(|_, p| { p.tick(t); !p.is_expired() }); // Update player and handle result self.player.update_controls(&self.input); - let mut p = self.player.tick(t); - if p.projectiles.len() != 0 { - self.projectiles.append(&mut p.projectiles); + let p = self.player.tick(&mut self.physics.rigid_body_set, t); + for p in p.projectiles { + let r = self.physics.rigid_body_set.insert(p.rigid_body.build()); + let c = self.physics.collider_set.insert_with_parent( + p.collider.build(), + r, + &mut self.physics.rigid_body_set, + ); + self.projectiles.insert( + c, + Projectile { + rigid_body: r, + sprite: p.sprite, + lifetime: p.lifetime, + size: p.size, + }, + ); + } + + while let Ok(event) = self.collision_recv.try_recv() { + if event.started() { + self.projectiles.remove(&event.collider1()); + self.projectiles.remove(&event.collider2()); + // TODO: remove collider and rigid body, + // cleanup, keep track of ships. + } } if self.input.v_scroll != 0.0 { @@ -111,7 +188,10 @@ impl Game { self.input.v_scroll = 0.0; } - self.camera.pos = self.player.physicsbody.pos; + // TODO: Camera physics + let r = &mut self.physics.rigid_body_set[self.player.rigid_body]; + let ship_pos = util::rigidbody_position(r); + self.camera.pos = ship_pos; self.last_update = Instant::now(); } @@ -120,8 +200,8 @@ impl Game { let mut sprites: Vec = Vec::new(); sprites.append(&mut self.system.get_sprites()); - sprites.push(self.player.get_sprite()); - sprites.push(self.test.get_sprite()); + sprites.push(self.player.get_sprite(&self.physics.rigid_body_set)); + sprites.push(self.test.get_sprite(&self.physics.rigid_body_set)); // Make sure sprites are drawn in the correct order // (note the reversed a, b in the comparator) @@ -131,16 +211,19 @@ impl Game { sprites.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z)); // Don't waste time sorting these, they should always be on top. - for p in &self.projectiles { + for (_, p) in &self.projectiles { + let r = &self.physics.rigid_body_set[p.rigid_body]; + let pos = util::rigidbody_position(r); + let ang = util::rigidbody_angle(r); sprites.push(Sprite { texture: p.sprite.clone(), pos: Point3 { - x: p.position.x, - y: p.position.y, + x: pos.x, + y: pos.y, z: 1.0, }, size: p.size, - angle: p.angle, + angle: ang, children: None, }) } diff --git a/src/game/mod.rs b/src/game/mod.rs index 7b24d87..220431b 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,12 +1,12 @@ mod camera; mod game; mod inputstatus; +mod physics; mod ship; mod system; mod systemobject; +mod util; -pub use camera::Camera; pub use game::Game; pub use inputstatus::InputStatus; -pub use system::System; pub use systemobject::SystemObject; diff --git a/src/game/physics.rs b/src/game/physics.rs new file mode 100644 index 0000000..2d468d4 --- /dev/null +++ b/src/game/physics.rs @@ -0,0 +1,60 @@ +use rapier2d::{ + dynamics::{ + CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, + RigidBodySet, + }, + geometry::{BroadPhase, ColliderSet, NarrowPhase}, + na::vector, + pipeline::{EventHandler, PhysicsPipeline}, +}; + +pub struct Physics { + pub rigid_body_set: RigidBodySet, + pub collider_set: ColliderSet, + + ip: IntegrationParameters, + pp: PhysicsPipeline, + im: IslandManager, + bp: BroadPhase, + np: NarrowPhase, + ijs: ImpulseJointSet, + mjs: MultibodyJointSet, + ccd: CCDSolver, +} + +impl Physics { + pub fn new() -> Self { + Self { + rigid_body_set: RigidBodySet::new(), + collider_set: ColliderSet::new(), + + ip: IntegrationParameters::default(), + pp: PhysicsPipeline::new(), + im: IslandManager::new(), + bp: BroadPhase::new(), + np: NarrowPhase::new(), + ijs: ImpulseJointSet::new(), + mjs: MultibodyJointSet::new(), + ccd: CCDSolver::new(), + } + } + + pub fn step(&mut self, t: f32, handler: &dyn EventHandler) { + self.ip.dt = t; + self.pp.step( + &vector![0.0, 0.0], + &self.ip, + &mut self.im, + &mut self.bp, + &mut self.np, + &mut self.rigid_body_set, + &mut self.collider_set, + &mut self.ijs, + &mut self.mjs, + &mut self.ccd, + None, + &(), + handler, + ); + } +} diff --git a/src/game/ship/mod.rs b/src/game/ship/mod.rs index 23f0740..9a987b2 100644 --- a/src/game/ship/mod.rs +++ b/src/game/ship/mod.rs @@ -4,7 +4,7 @@ mod ship; pub use outfits::{ShipGun, ShipOutfit, ShipOutfits}; pub use ship::Ship; -use super::{game::Projectile, InputStatus}; +use super::{game::ProjectileBuilder, InputStatus}; pub struct ShipControls { pub left: bool, @@ -25,5 +25,5 @@ impl ShipControls { } pub struct ShipTickResult { - pub projectiles: Vec, + pub projectiles: Vec, } diff --git a/src/game/ship/ship.rs b/src/game/ship/ship.rs index df85617..2d96402 100644 --- a/src/game/ship/ship.rs +++ b/src/game/ship/ship.rs @@ -1,17 +1,20 @@ -use cgmath::{Deg, EuclideanSpace, Matrix2, Point2, Vector2}; +use cgmath::{Deg, EuclideanSpace, Matrix2, Rad, Vector2}; +use nalgebra::vector; use rand::Rng; +use rapier2d::dynamics::{RigidBodyBuilder, RigidBodyHandle, RigidBodySet}; +use rapier2d::geometry::ColliderBuilder; +use rapier2d::pipeline::ActiveEvents; -use super::super::game::Projectile; -use super::ShipOutfit; -use super::{outfits::ShipOutfits, InputStatus, ShipControls, ShipTickResult}; +use super::super::util; +use super::{outfits::ShipOutfits, InputStatus, ShipControls, ShipOutfit, ShipTickResult}; +use crate::game::game::ProjectileBuilder; use crate::{ content, - physics::PhysicsBody, - render::{Sprite, SpriteTexture, Spriteable}, + render::{Sprite, SpriteTexture}, }; pub struct Ship { - pub physicsbody: PhysicsBody, + pub rigid_body: RigidBodyHandle, pub controls: ShipControls, outfits: ShipOutfits, @@ -20,14 +23,14 @@ pub struct Ship { } impl Ship { - pub fn new(ct: &content::Ship, outfits: Vec, pos: Point2) -> Self { + pub fn new(ct: &content::Ship, outfits: Vec, rigid_body: RigidBodyHandle) -> Self { let mut o = ShipOutfits::new(ct.engines.clone(), ct.guns.clone()); for x in outfits.into_iter() { o.add(x) } Ship { - physicsbody: PhysicsBody::new(pos), + rigid_body, controls: ShipControls::new(), outfits: o, @@ -43,9 +46,9 @@ impl Ship { self.controls.guns = input.key_guns; } - pub fn fire_guns(&mut self) -> Vec { + pub fn fire_guns(&mut self, rigid_body_set: &RigidBodySet) -> Vec { + let r = &rigid_body_set[self.rigid_body]; let mut rng = rand::thread_rng(); - let mut out = Vec::new(); for (g, p) in self.outfits.iter_guns_points() { @@ -55,23 +58,34 @@ impl Ship { g.cooldown = g.kind.rate + rng.gen_range(-g.kind.rate_rng..=g.kind.rate_rng); - let pos = self.physicsbody.pos - + (Matrix2::from_angle(self.physicsbody.angle) * p.pos.to_vec()); + let ship_pos = util::rigidbody_position(r); + let ship_ang: Deg = util::rigidbody_angle(r); + let ship_ang_rad: Rad = ship_ang.into(); + let ship_vel = util::rigidbody_velocity(r); - let vel = self.physicsbody.vel + let pos = ship_pos + (Matrix2::from_angle(ship_ang) * p.pos.to_vec()); + + let vel = ship_vel + (Matrix2::from_angle( - self.physicsbody.angle - + Deg(rng.gen_range(-(g.kind.spread.0 / 2.0)..=g.kind.spread.0 / 2.0)), + ship_ang + Deg(rng.gen_range(-(g.kind.spread.0 / 2.0)..=g.kind.spread.0 / 2.0)), ) * Vector2 { x: 0.0, y: g.kind.projectile.speed + rng.gen_range(-g.kind.projectile.speed_rng..=g.kind.projectile.speed_rng), }); - out.push(Projectile { - position: pos, - velocity: vel, - angle: self.physicsbody.angle, + let p_r = RigidBodyBuilder::kinematic_velocity_based() + .translation(vector![pos.x, pos.y]) + .rotation(-ship_ang_rad.0) + .linvel(vector![vel.x, vel.y]); + + let p_c = ColliderBuilder::ball(5.0) + .sensor(true) + .active_events(ActiveEvents::COLLISION_EVENTS); + + out.push(ProjectileBuilder { + rigid_body: p_r, + collider: p_c, sprite: SpriteTexture(g.kind.projectile.sprite.clone()), lifetime: g.kind.projectile.lifetime + rng.gen_range( @@ -84,42 +98,47 @@ impl Ship { return out; } - pub fn tick(&mut self, t: f32) -> ShipTickResult { + pub fn tick(&mut self, rigid_body_set: &mut RigidBodySet, t: f32) -> ShipTickResult { + let r = &mut rigid_body_set[self.rigid_body]; + let ship_ang = util::rigidbody_angle(r); + + let engine_force = Matrix2::from_angle(ship_ang) * Vector2 { x: 0.0, y: 1.0 } * t; if self.controls.thrust { for e in self.outfits.iter_engines() { - self.physicsbody.thrust(e.thrust * t); + r.apply_impulse(vector![engine_force.x, engine_force.y] * e.thrust, true); } } if self.controls.right { - self.physicsbody.rot(Deg(35.0) * t); + r.apply_torque_impulse(500.0 * t, true); } if self.controls.left { - self.physicsbody.rot(Deg(-35.0) * t); + r.apply_torque_impulse(-500.0 * t, true); } let p = if self.controls.guns { - self.fire_guns() + self.fire_guns(rigid_body_set) } else { Vec::new() }; - self.physicsbody.tick(t); for i in self.outfits.iter_guns() { i.cooldown -= t; } return ShipTickResult { projectiles: p }; } -} -impl Spriteable for Ship { - fn get_sprite(&self) -> Sprite { + pub fn get_sprite(&self, rigid_body_set: &RigidBodySet) -> Sprite { + let r = &rigid_body_set[self.rigid_body]; + let ship_pos = util::rigidbody_position(r); + let ship_ang = util::rigidbody_angle(r); + Sprite { - pos: (self.physicsbody.pos.x, self.physicsbody.pos.y, 1.0).into(), + pos: (ship_pos.x, ship_pos.y, 1.0).into(), texture: self.sprite.clone(), // TODO: sprite texture should be easy to clone - angle: self.physicsbody.angle, + angle: ship_ang, size: self.size, children: if self.controls.thrust { diff --git a/src/game/util.rs b/src/game/util.rs new file mode 100644 index 0000000..e241033 --- /dev/null +++ b/src/game/util.rs @@ -0,0 +1,27 @@ +use cgmath::{Deg, InnerSpace, Point2, Vector2}; +use nalgebra; +use rapier2d::dynamics::RigidBody; + +pub fn rigidbody_position(r: &RigidBody) -> cgmath::Point2 { + Point2 { + x: r.translation()[0], + y: r.translation()[1], + } +} + +pub fn rigidbody_angle(r: &RigidBody) -> Deg { + Vector2 { + x: r.rotation().re, + y: r.rotation().im, + } + .angle(Vector2 { x: 1.0, y: 0.0 }) + .into() +} + +pub fn rigidbody_velocity(r: &RigidBody) -> cgmath::Vector2 { + let v = r.velocity_at_point(&nalgebra::Point2::new( + r.translation()[0], + r.translation()[1], + )); + Vector2 { x: v.x, y: v.y } +} diff --git a/src/main.rs b/src/main.rs index d043708..6e63bae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ mod consts; mod content; mod game; -mod physics; mod render; +mod util; use anyhow::Result; use winit::{ diff --git a/src/physics/body.rs b/src/physics/body.rs deleted file mode 100644 index 8b68ddd..0000000 --- a/src/physics/body.rs +++ /dev/null @@ -1,48 +0,0 @@ -use cgmath::{Angle, Deg, Point2, Vector2}; - -pub struct PhysicsBody { - pub pos: Point2, - pub vel: Vector2, - pub mass: f32, - pub angle: Deg, -} - -impl PhysicsBody { - pub fn new(pos: Point2) -> Self { - return PhysicsBody { - pos, - vel: (0.0, 0.0).into(), - mass: 0.3, - angle: Deg(0.0), - }; - } - - /// Calculate the position of this body after t seconds. - pub fn tick(&mut self, t: f32) { - self.pos += self.vel * t; - } - - /// Apply an instantaneous force to this object - pub fn force(&mut self, v: Vector2) { - self.vel += v / self.mass; - } - - /// Apply a force in the direction this object is pointing. - pub fn thrust(&mut self, f: f32) { - let l = Vector2 { - x: -self.angle.sin(), - y: self.angle.cos(), - } * f; - self.force(l); - } - - // Rotate this object - pub fn rot(&mut self, a: Deg) { - self.angle -= a; - - // Wrap angles - if self.angle.0.abs() > 180.0 { - self.angle -= Deg(self.angle.0.signum() * 360.0); - } - } -} diff --git a/src/physics/mod.rs b/src/physics/mod.rs deleted file mode 100644 index 99738e4..0000000 --- a/src/physics/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod body; -mod polar; - -// What kind of float shoud we use for physics? -pub use body::PhysicsBody; -pub use polar::Polar; diff --git a/src/render/mod.rs b/src/render/mod.rs index 8524775..79a5c48 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -7,7 +7,7 @@ mod texturearray; mod vertexbuffer; pub use gpustate::GPUState; -pub use sprite::{Sprite, Spriteable, SubSprite}; +pub use sprite::{Sprite, SubSprite}; /// A handle to a sprite texture /// TODO: This should be easy to copy, diff --git a/src/render/sprite.rs b/src/render/sprite.rs index aae3256..853e8c7 100644 --- a/src/render/sprite.rs +++ b/src/render/sprite.rs @@ -41,7 +41,3 @@ pub struct SubSprite { /// subsprite's parent sprite. pub angle: Deg, } - -pub trait Spriteable { - fn get_sprite(&self) -> Sprite; -} diff --git a/src/physics/polar.rs b/src/util.rs similarity index 100% rename from src/physics/polar.rs rename to src/util.rs