Added collisions

master
Mark 2023-12-28 20:19:33 -08:00
parent ece82a8c1b
commit 2354971d8e
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
17 changed files with 497 additions and 142 deletions

246
Cargo.lock generated
View File

@ -99,6 +99,15 @@ dependencies = [
"num-traits", "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]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.7" version = "0.3.7"
@ -273,7 +282,7 @@ version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
dependencies = [ dependencies = [
"approx", "approx 0.4.0",
"num-traits", "num-traits",
] ]
@ -348,6 +357,30 @@ dependencies = [
"cfg-if", "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]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.4" version = "0.8.4"
@ -361,21 +394,30 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.9.16" version = "0.9.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cfg-if", "cfg-if",
"crossbeam-utils", "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]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.17" version = "0.8.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@ -538,9 +580,12 @@ dependencies = [
"anyhow", "anyhow",
"bytemuck", "bytemuck",
"cgmath", "cgmath",
"crossbeam",
"image", "image",
"nalgebra",
"pollster", "pollster",
"rand", "rand",
"rapier2d",
"serde", "serde",
"toml", "toml",
"walkdir", "walkdir",
@ -831,6 +876,12 @@ dependencies = [
"windows-sys 0.48.0", "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]] [[package]]
name = "libredox" name = "libredox"
version = "0.0.2" version = "0.0.2"
@ -867,6 +918,16 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.6.4" version = "2.6.4"
@ -891,15 +952,6 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "metal" name = "metal"
version = "0.27.0" version = "0.27.0"
@ -957,6 +1009,33 @@ dependencies = [
"unicode-xid", "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]] [[package]]
name = "nanorand" name = "nanorand"
version = "0.7.0" version = "0.7.0"
@ -1004,7 +1083,7 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"cfg-if", "cfg-if",
"libc", "libc",
"memoffset 0.6.5", "memoffset",
] ]
[[package]] [[package]]
@ -1017,7 +1096,27 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"cfg-if", "cfg-if",
"libc", "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]] [[package]]
@ -1048,6 +1147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"libm",
] ]
[[package]] [[package]]
@ -1193,6 +1293,27 @@ dependencies = [
"windows-targets 0.48.5", "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]] [[package]]
name = "paste" name = "paste"
version = "1.0.14" version = "1.0.14"
@ -1321,12 +1442,38 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" 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]] [[package]]
name = "raw-window-handle" name = "raw-window-handle"
version = "0.5.2" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.8.0" version = "1.8.0"
@ -1371,6 +1518,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b"
[[package]]
name = "robust"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30"
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"
@ -1383,6 +1536,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 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]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -1446,12 +1608,34 @@ dependencies = [
"serde", "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]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.7" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "slotmap" name = "slotmap"
version = "1.0.7" version = "1.0.7"
@ -1486,6 +1670,18 @@ dependencies = [
"wayland-protocols", "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]] [[package]]
name = "spin" name = "spin"
version = "0.9.8" version = "0.9.8"
@ -1655,6 +1851,12 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4"
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.12"
@ -1958,6 +2160,16 @@ dependencies = [
"web-sys", "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]] [[package]]
name = "widestring" name = "widestring"
version = "1.0.2" version = "1.0.2"

View File

@ -41,3 +41,6 @@ anyhow = "1.0"
cgmath = "0.18.0" cgmath = "0.18.0"
rand = "0.8.5" rand = "0.8.5"
walkdir = "2.4.0" walkdir = "2.4.0"
rapier2d = "0.17.2"
nalgebra = "0.32.3"
crossbeam = "0.8.3"

View File

@ -2,5 +2,6 @@
sprite = "ship::gypsum" sprite = "ship::gypsum"
size = 100 size = 100
engines = [{ x = 0.0, y = -105, size = 50.0 }] engines = [{ x = 0.0, y = -1.05, size = 50.0 }]
guns = [{ x = 0.0, y = 100 }, { x = 10.0, y = 80 }, { x = -10.0, y = 80 }] guns = [{ x = 0.0, y = 1 }, { x = 0.1, y = 0.80 }, { x = -0.1, y = 0.80 }]

View File

@ -61,15 +61,20 @@ impl super::Build for Ship {
let mut out = Vec::new(); let mut out = Vec::new();
for (ship_name, ship) in ship { for (ship_name, ship) in ship {
let size = ship.size;
out.push(Self { out.push(Self {
name: ship_name.to_owned(), name: ship_name.to_owned(),
sprite: ship.sprite.to_owned(), sprite: ship.sprite.to_owned(),
size: ship.size, size,
engines: ship engines: ship
.engines .engines
.iter() .iter()
.map(|e| EnginePoint { .map(|e| EnginePoint {
pos: Point2 { x: e.x, y: e.y }, pos: Point2 {
x: e.x * size,
y: e.y * size,
},
size: e.size, size: e.size,
}) })
.collect(), .collect(),
@ -77,7 +82,10 @@ impl super::Build for Ship {
.guns .guns
.iter() .iter()
.map(|e| GunPoint { .map(|e| GunPoint {
pos: Point2 { x: e.x, y: e.y }, pos: Point2 {
x: e.x * size,
y: e.y * size,
},
}) })
.collect(), .collect(),
}); });

View File

@ -2,7 +2,7 @@ use anyhow::{bail, Context, Result};
use cgmath::{Deg, Point3}; use cgmath::{Deg, Point3};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use crate::physics::Polar; use crate::util::Polar;
pub(super) mod syntax { pub(super) mod syntax {
use super::HashMap; use super::HashMap;

View File

@ -1,27 +1,39 @@
use cgmath::{Deg, Point2, Point3, Vector2}; use cgmath::Point3;
use std::time::Instant; 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 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::{ use crate::{
consts, consts,
content::Content, content::Content,
render::Spriteable,
render::{Sprite, SpriteTexture}, render::{Sprite, SpriteTexture},
}; };
pub struct Projectile { pub struct ProjectileBuilder {
pub position: Point2<f32>, pub rigid_body: RigidBodyBuilder,
pub velocity: Vector2<f32>, 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 sprite: SpriteTexture,
pub angle: Deg<f32>,
pub lifetime: f32, pub lifetime: f32,
pub size: f32, pub size: f32,
} }
impl Projectile { impl Projectile {
pub fn tick(&mut self, t: f32) { pub fn tick(&mut self, t: f32) {
self.position += self.velocity * t;
self.lifetime -= t; self.lifetime -= t;
} }
@ -38,16 +50,52 @@ pub struct Game {
pub system: System, pub system: System,
pub camera: Camera, pub camera: Camera,
paused: bool, paused: bool,
pub projectiles: Vec<Projectile>,
pub time_scale: f32, pub time_scale: f32,
pub projectiles: HashMap<ColliderHandle, Projectile>,
collision_handler: ChannelEventCollector,
physics: Physics,
collision_recv: Receiver<CollisionEvent>,
} }
impl Game { impl Game {
pub fn new(ct: Content) -> Self { 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 { Game {
last_update: Instant::now(), last_update: Instant::now(),
input: InputStatus::new(), input: InputStatus::new(),
projectiles: Vec::new(), projectiles: HashMap::new(),
player: ship::Ship::new( player: ship::Ship::new(
&ct.ships[0], &ct.ships[0],
vec![ vec![
@ -55,7 +103,7 @@ impl Game {
ship::ShipOutfit::Gun(ship::ShipGun::new(ct.guns[0].clone(), 2)), ship::ShipOutfit::Gun(ship::ShipGun::new(ct.guns[0].clone(), 2)),
ship::ShipOutfit::Engine(ct.engines[0].clone()), ship::ShipOutfit::Engine(ct.engines[0].clone()),
], ],
(0.0, 0.0).into(), rh1,
), ),
camera: Camera { camera: Camera {
pos: (0.0, 0.0).into(), pos: (0.0, 0.0).into(),
@ -65,7 +113,11 @@ impl Game {
paused: false, paused: false,
time_scale: 1.0, 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) { pub fn update(&mut self) {
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale; 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.tick(t);
!p.is_expired() !p.is_expired()
}); });
// Update player and handle result // Update player and handle result
self.player.update_controls(&self.input); self.player.update_controls(&self.input);
let mut p = self.player.tick(t); let p = self.player.tick(&mut self.physics.rigid_body_set, t);
if p.projectiles.len() != 0 { for p in p.projectiles {
self.projectiles.append(&mut 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 { if self.input.v_scroll != 0.0 {
@ -111,7 +188,10 @@ impl Game {
self.input.v_scroll = 0.0; 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(); self.last_update = Instant::now();
} }
@ -120,8 +200,8 @@ impl Game {
let mut sprites: Vec<Sprite> = Vec::new(); let mut sprites: Vec<Sprite> = Vec::new();
sprites.append(&mut self.system.get_sprites()); sprites.append(&mut self.system.get_sprites());
sprites.push(self.player.get_sprite()); sprites.push(self.player.get_sprite(&self.physics.rigid_body_set));
sprites.push(self.test.get_sprite()); sprites.push(self.test.get_sprite(&self.physics.rigid_body_set));
// Make sure sprites are drawn in the correct order // Make sure sprites are drawn in the correct order
// (note the reversed a, b in the comparator) // (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)); 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. // 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 { sprites.push(Sprite {
texture: p.sprite.clone(), texture: p.sprite.clone(),
pos: Point3 { pos: Point3 {
x: p.position.x, x: pos.x,
y: p.position.y, y: pos.y,
z: 1.0, z: 1.0,
}, },
size: p.size, size: p.size,
angle: p.angle, angle: ang,
children: None, children: None,
}) })
} }

View File

@ -1,12 +1,12 @@
mod camera; mod camera;
mod game; mod game;
mod inputstatus; mod inputstatus;
mod physics;
mod ship; mod ship;
mod system; mod system;
mod systemobject; mod systemobject;
mod util;
pub use camera::Camera;
pub use game::Game; pub use game::Game;
pub use inputstatus::InputStatus; pub use inputstatus::InputStatus;
pub use system::System;
pub use systemobject::SystemObject; pub use systemobject::SystemObject;

60
src/game/physics.rs Normal file
View File

@ -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,
);
}
}

View File

@ -4,7 +4,7 @@ mod ship;
pub use outfits::{ShipGun, ShipOutfit, ShipOutfits}; pub use outfits::{ShipGun, ShipOutfit, ShipOutfits};
pub use ship::Ship; pub use ship::Ship;
use super::{game::Projectile, InputStatus}; use super::{game::ProjectileBuilder, InputStatus};
pub struct ShipControls { pub struct ShipControls {
pub left: bool, pub left: bool,
@ -25,5 +25,5 @@ impl ShipControls {
} }
pub struct ShipTickResult { pub struct ShipTickResult {
pub projectiles: Vec<Projectile>, pub projectiles: Vec<ProjectileBuilder>,
} }

View File

@ -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 rand::Rng;
use rapier2d::dynamics::{RigidBodyBuilder, RigidBodyHandle, RigidBodySet};
use rapier2d::geometry::ColliderBuilder;
use rapier2d::pipeline::ActiveEvents;
use super::super::game::Projectile; use super::super::util;
use super::ShipOutfit; use super::{outfits::ShipOutfits, InputStatus, ShipControls, ShipOutfit, ShipTickResult};
use super::{outfits::ShipOutfits, InputStatus, ShipControls, ShipTickResult}; use crate::game::game::ProjectileBuilder;
use crate::{ use crate::{
content, content,
physics::PhysicsBody, render::{Sprite, SpriteTexture},
render::{Sprite, SpriteTexture, Spriteable},
}; };
pub struct Ship { pub struct Ship {
pub physicsbody: PhysicsBody, pub rigid_body: RigidBodyHandle,
pub controls: ShipControls, pub controls: ShipControls,
outfits: ShipOutfits, outfits: ShipOutfits,
@ -20,14 +23,14 @@ pub struct Ship {
} }
impl Ship { impl Ship {
pub fn new(ct: &content::Ship, outfits: Vec<ShipOutfit>, pos: Point2<f32>) -> Self { pub fn new(ct: &content::Ship, outfits: Vec<ShipOutfit>, rigid_body: RigidBodyHandle) -> Self {
let mut o = ShipOutfits::new(ct.engines.clone(), ct.guns.clone()); let mut o = ShipOutfits::new(ct.engines.clone(), ct.guns.clone());
for x in outfits.into_iter() { for x in outfits.into_iter() {
o.add(x) o.add(x)
} }
Ship { Ship {
physicsbody: PhysicsBody::new(pos), rigid_body,
controls: ShipControls::new(), controls: ShipControls::new(),
outfits: o, outfits: o,
@ -43,9 +46,9 @@ impl Ship {
self.controls.guns = input.key_guns; self.controls.guns = input.key_guns;
} }
pub fn fire_guns(&mut self) -> Vec<Projectile> { pub fn fire_guns(&mut self, rigid_body_set: &RigidBodySet) -> Vec<ProjectileBuilder> {
let r = &rigid_body_set[self.rigid_body];
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let mut out = Vec::new(); let mut out = Vec::new();
for (g, p) in self.outfits.iter_guns_points() { 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); g.cooldown = g.kind.rate + rng.gen_range(-g.kind.rate_rng..=g.kind.rate_rng);
let pos = self.physicsbody.pos let ship_pos = util::rigidbody_position(r);
+ (Matrix2::from_angle(self.physicsbody.angle) * p.pos.to_vec()); let ship_ang: Deg<f32> = util::rigidbody_angle(r);
let ship_ang_rad: Rad<f32> = 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( + (Matrix2::from_angle(
self.physicsbody.angle ship_ang + Deg(rng.gen_range(-(g.kind.spread.0 / 2.0)..=g.kind.spread.0 / 2.0)),
+ Deg(rng.gen_range(-(g.kind.spread.0 / 2.0)..=g.kind.spread.0 / 2.0)),
) * Vector2 { ) * Vector2 {
x: 0.0, x: 0.0,
y: g.kind.projectile.speed y: g.kind.projectile.speed
+ rng.gen_range(-g.kind.projectile.speed_rng..=g.kind.projectile.speed_rng), + rng.gen_range(-g.kind.projectile.speed_rng..=g.kind.projectile.speed_rng),
}); });
out.push(Projectile { let p_r = RigidBodyBuilder::kinematic_velocity_based()
position: pos, .translation(vector![pos.x, pos.y])
velocity: vel, .rotation(-ship_ang_rad.0)
angle: self.physicsbody.angle, .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()), sprite: SpriteTexture(g.kind.projectile.sprite.clone()),
lifetime: g.kind.projectile.lifetime lifetime: g.kind.projectile.lifetime
+ rng.gen_range( + rng.gen_range(
@ -84,42 +98,47 @@ impl Ship {
return out; 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 { if self.controls.thrust {
for e in self.outfits.iter_engines() { 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 { if self.controls.right {
self.physicsbody.rot(Deg(35.0) * t); r.apply_torque_impulse(500.0 * t, true);
} }
if self.controls.left { 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 { let p = if self.controls.guns {
self.fire_guns() self.fire_guns(rigid_body_set)
} else { } else {
Vec::new() Vec::new()
}; };
self.physicsbody.tick(t);
for i in self.outfits.iter_guns() { for i in self.outfits.iter_guns() {
i.cooldown -= t; i.cooldown -= t;
} }
return ShipTickResult { projectiles: p }; return ShipTickResult { projectiles: p };
} }
}
impl Spriteable for Ship { pub fn get_sprite(&self, rigid_body_set: &RigidBodySet) -> Sprite {
fn get_sprite(&self) -> Sprite { let r = &rigid_body_set[self.rigid_body];
let ship_pos = util::rigidbody_position(r);
let ship_ang = util::rigidbody_angle(r);
Sprite { 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 texture: self.sprite.clone(), // TODO: sprite texture should be easy to clone
angle: self.physicsbody.angle, angle: ship_ang,
size: self.size, size: self.size,
children: if self.controls.thrust { children: if self.controls.thrust {

27
src/game/util.rs Normal file
View File

@ -0,0 +1,27 @@
use cgmath::{Deg, InnerSpace, Point2, Vector2};
use nalgebra;
use rapier2d::dynamics::RigidBody;
pub fn rigidbody_position(r: &RigidBody) -> cgmath::Point2<f32> {
Point2 {
x: r.translation()[0],
y: r.translation()[1],
}
}
pub fn rigidbody_angle(r: &RigidBody) -> Deg<f32> {
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<f32> {
let v = r.velocity_at_point(&nalgebra::Point2::new(
r.translation()[0],
r.translation()[1],
));
Vector2 { x: v.x, y: v.y }
}

View File

@ -1,8 +1,8 @@
mod consts; mod consts;
mod content; mod content;
mod game; mod game;
mod physics;
mod render; mod render;
mod util;
use anyhow::Result; use anyhow::Result;
use winit::{ use winit::{

View File

@ -1,48 +0,0 @@
use cgmath::{Angle, Deg, Point2, Vector2};
pub struct PhysicsBody {
pub pos: Point2<f32>,
pub vel: Vector2<f32>,
pub mass: f32,
pub angle: Deg<f32>,
}
impl PhysicsBody {
pub fn new(pos: Point2<f32>) -> 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<f32>) {
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<f32>) {
self.angle -= a;
// Wrap angles
if self.angle.0.abs() > 180.0 {
self.angle -= Deg(self.angle.0.signum() * 360.0);
}
}
}

View File

@ -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;

View File

@ -7,7 +7,7 @@ mod texturearray;
mod vertexbuffer; mod vertexbuffer;
pub use gpustate::GPUState; pub use gpustate::GPUState;
pub use sprite::{Sprite, Spriteable, SubSprite}; pub use sprite::{Sprite, SubSprite};
/// A handle to a sprite texture /// A handle to a sprite texture
/// TODO: This should be easy to copy, /// TODO: This should be easy to copy,

View File

@ -41,7 +41,3 @@ pub struct SubSprite {
/// subsprite's parent sprite. /// subsprite's parent sprite.
pub angle: Deg<f32>, pub angle: Deg<f32>,
} }
pub trait Spriteable {
fn get_sprite(&self) -> Sprite;
}