Cleaned up physics, added basic ship damage

master
Mark 2023-12-29 12:21:56 -08:00
parent f91ab7f3c5
commit c6f22557f2
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
14 changed files with 338 additions and 185 deletions

View File

@ -20,3 +20,4 @@ projectile.speed_rng = 10.0
# Lifetime of projectile, in seconds # Lifetime of projectile, in seconds
projectile.lifetime = 2.0 projectile.lifetime = 2.0
projectile.lifetime_rng = 0.2 projectile.lifetime_rng = 0.2
projectile.damage = 10.0

View File

@ -1,7 +1,7 @@
[ship."Gypsum"] [ship."Gypsum"]
sprite = "ship::gypsum" sprite = "ship::gypsum"
size = 100 size = 100
hull = 200
engines = [{ x = 0.0, y = -1.05, size = 50.0 }] 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 }] guns = [{ x = 0.0, y = 1 }, { x = 0.1, y = 0.80 }, { x = -0.1, y = 0.80 }]

View File

@ -23,6 +23,7 @@ pub(super) mod syntax {
pub speed_rng: f32, pub speed_rng: f32,
pub lifetime: f32, pub lifetime: f32,
pub lifetime_rng: f32, pub lifetime_rng: f32,
pub damage: f32,
} }
} }
@ -44,6 +45,7 @@ pub struct Projectile {
pub speed_rng: f32, pub speed_rng: f32,
pub lifetime: f32, pub lifetime: f32,
pub lifetime_rng: f32, pub lifetime_rng: f32,
pub damage: f32,
} }
impl super::Build for Gun { impl super::Build for Gun {
@ -69,6 +71,7 @@ impl super::Build for Gun {
speed_rng: gun.projectile.speed_rng, speed_rng: gun.projectile.speed_rng,
lifetime: gun.projectile.lifetime, lifetime: gun.projectile.lifetime,
lifetime_rng: gun.projectile.lifetime_rng, lifetime_rng: gun.projectile.lifetime_rng,
damage: gun.projectile.damage,
}, },
}); });
} }

View File

@ -12,6 +12,7 @@ pub(super) mod syntax {
pub size: f32, pub size: f32,
pub engines: Vec<Engine>, pub engines: Vec<Engine>,
pub guns: Vec<Gun>, pub guns: Vec<Gun>,
pub hull: f32,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -38,6 +39,7 @@ pub struct Ship {
pub size: f32, pub size: f32,
pub engines: Vec<EnginePoint>, pub engines: Vec<EnginePoint>,
pub guns: Vec<GunPoint>, pub guns: Vec<GunPoint>,
pub hull: f32,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -67,6 +69,7 @@ impl super::Build for Ship {
name: ship_name.to_owned(), name: ship_name.to_owned(),
sprite: ship.sprite.to_owned(), sprite: ship.sprite.to_owned(),
size, size,
hull: ship.hull,
engines: ship engines: ship
.engines .engines
.iter() .iter()

View File

@ -1,110 +1,47 @@
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::{camera::Camera, physics::Physics, ship, system::System, util, InputStatus}; use super::{
use crate::{ camera::Camera,
consts, system::System,
content::Content, util,
render::{Sprite, SpriteTexture}, world::{ShipGun, ShipHandle, ShipOutfit, World},
InputStatus,
}; };
use crate::{consts, content::Content, render::Sprite};
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 lifetime: f32,
pub size: f32,
}
impl Projectile {
pub fn tick(&mut self, t: f32) {
self.lifetime -= t;
}
pub fn is_expired(&self) -> bool {
return self.lifetime < 0.0;
}
}
pub struct Game { pub struct Game {
pub input: InputStatus, pub input: InputStatus,
pub last_update: Instant, pub last_update: Instant,
pub player: ship::Ship, pub player: ShipHandle,
pub test: ship::Ship,
pub system: System, pub system: System,
pub camera: Camera, pub camera: Camera,
paused: bool, paused: bool,
pub time_scale: f32, pub time_scale: f32,
pub projectiles: HashMap<ColliderHandle, Projectile>, world: World,
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() let mut world = World::new();
.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() let c = world.add_ship(
.translation(vector![200.0, 200.0]) &ct.ships[0],
.build(); vec![
let c2 = ColliderBuilder::ball(50.0) ShipOutfit::Gun(ShipGun::new(ct.guns[0].clone(), 1)),
.restitution(0.7) ShipOutfit::Gun(ShipGun::new(ct.guns[0].clone(), 2)),
.mass(100.0) ShipOutfit::Engine(ct.engines[0].clone()),
.build(); ],
);
let mut p = Physics::new(); world.add_ship(&ct.ships[0], vec![]);
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: HashMap::new(), player: c,
player: ship::Ship::new(
&ct.ships[0],
vec![
ship::ShipOutfit::Gun(ship::ShipGun::new(ct.guns[0].clone(), 1)),
ship::ShipOutfit::Gun(ship::ShipGun::new(ct.guns[0].clone(), 2)),
ship::ShipOutfit::Engine(ct.engines[0].clone()),
],
rh1,
),
camera: Camera { camera: Camera {
pos: (0.0, 0.0).into(), pos: (0.0, 0.0).into(),
zoom: 500.0, zoom: 500.0,
@ -113,11 +50,7 @@ impl Game {
paused: false, paused: false,
time_scale: 1.0, time_scale: 1.0,
test: ship::Ship::new(&ct.ships[0], vec![], rh2), world,
physics: p,
collision_handler: ChannelEventCollector::new(collision_send, contact_force_send),
collision_recv,
} }
} }
@ -145,42 +78,11 @@ 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.physics.step(t, &self.collision_handler); self.world
.get_ship_mut(&self.player)
.update_controls(&self.input);
self.projectiles.retain(|_, p| { self.world.tick(t);
p.tick(t);
!p.is_expired()
});
// Update player and handle result
self.player.update_controls(&self.input);
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 { if self.input.v_scroll != 0.0 {
self.camera.zoom = self.camera.zoom =
@ -189,10 +91,10 @@ impl Game {
} }
// TODO: Camera physics // TODO: Camera physics
let r = &mut self.physics.rigid_body_set[self.player.rigid_body]; let r = self.world.get_ship_mut(&self.player).physics_handle;
let r = self.world.get_rigid_body(r.0); // TODO: r.0 shouldn't be public
let ship_pos = util::rigidbody_position(r); let ship_pos = util::rigidbody_position(r);
self.camera.pos = ship_pos; self.camera.pos = ship_pos;
self.last_update = Instant::now(); self.last_update = Instant::now();
} }
@ -200,8 +102,7 @@ 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(&self.physics.rigid_body_set)); sprites.extend(self.world.get_ship_sprites());
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)
@ -211,22 +112,7 @@ 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 { sprites.extend(self.world.get_projectile_sprites());
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: pos.x,
y: pos.y,
z: 1.0,
},
size: p.size,
angle: ang,
children: None,
})
}
return sprites; return sprites;
} }

View File

@ -1,11 +1,10 @@
mod camera; mod camera;
mod game; mod game;
mod inputstatus; mod inputstatus;
mod physics;
mod ship;
mod system; mod system;
mod systemobject; mod systemobject;
mod util; mod util;
mod world;
pub use game::Game; pub use game::Game;
pub use inputstatus::InputStatus; pub use inputstatus::InputStatus;

13
src/game/world/mod.rs Normal file
View File

@ -0,0 +1,13 @@
mod physicswrapper;
mod projectile;
mod ship;
mod world;
pub use projectile::{Projectile, ProjectileBuilder};
pub use ship::{Ship, ShipControls, ShipGun, ShipOutfit, ShipOutfits, ShipTickResult};
pub use world::World;
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
#[derive(Debug, Copy, Clone)]
pub struct ShipHandle(pub(in super::super) RigidBodyHandle, ColliderHandle);

View File

@ -8,21 +8,21 @@ use rapier2d::{
pipeline::{EventHandler, PhysicsPipeline}, pipeline::{EventHandler, PhysicsPipeline},
}; };
pub struct Physics { pub(super) struct PhysicsWrapper {
pub rigid_body_set: RigidBodySet, pub rigid_body_set: RigidBodySet,
pub collider_set: ColliderSet, pub collider_set: ColliderSet,
ip: IntegrationParameters, pub ip: IntegrationParameters,
pp: PhysicsPipeline, pub pp: PhysicsPipeline,
im: IslandManager, pub im: IslandManager,
bp: BroadPhase, pub bp: BroadPhase,
np: NarrowPhase, pub np: NarrowPhase,
ijs: ImpulseJointSet, pub ij: ImpulseJointSet,
mjs: MultibodyJointSet, pub mj: MultibodyJointSet,
ccd: CCDSolver, pub ccd: CCDSolver,
} }
impl Physics { impl PhysicsWrapper {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
rigid_body_set: RigidBodySet::new(), rigid_body_set: RigidBodySet::new(),
@ -33,8 +33,8 @@ impl Physics {
im: IslandManager::new(), im: IslandManager::new(),
bp: BroadPhase::new(), bp: BroadPhase::new(),
np: NarrowPhase::new(), np: NarrowPhase::new(),
ijs: ImpulseJointSet::new(), ij: ImpulseJointSet::new(),
mjs: MultibodyJointSet::new(), mj: MultibodyJointSet::new(),
ccd: CCDSolver::new(), ccd: CCDSolver::new(),
} }
} }
@ -49,8 +49,8 @@ impl Physics {
&mut self.np, &mut self.np,
&mut self.rigid_body_set, &mut self.rigid_body_set,
&mut self.collider_set, &mut self.collider_set,
&mut self.ijs, &mut self.ij,
&mut self.mjs, &mut self.mj,
&mut self.ccd, &mut self.ccd,
None, None,
&(), &(),

View File

@ -0,0 +1,66 @@
use cgmath::Point3;
use rapier2d::{
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
geometry::{ColliderBuilder, ColliderHandle},
};
use super::super::util;
use crate::render::{Sprite, SpriteTexture};
pub struct ProjectileBuilder {
pub rigid_body: RigidBodyBuilder,
pub collider: ColliderBuilder,
pub sprite: SpriteTexture,
pub lifetime: f32,
pub size: f32,
pub damage: f32,
}
impl ProjectileBuilder {
pub fn build(self, r: RigidBodyHandle, c: ColliderHandle) -> Projectile {
Projectile {
rigid_body: r,
collider: c,
sprite: self.sprite,
lifetime: self.lifetime,
size: self.size,
damage: self.damage,
}
}
}
#[derive(Debug)]
pub struct Projectile {
pub rigid_body: RigidBodyHandle,
pub collider: ColliderHandle,
pub sprite: SpriteTexture,
pub lifetime: f32,
pub size: f32,
pub damage: f32,
}
impl Projectile {
pub fn tick(&mut self, t: f32) {
self.lifetime -= t;
}
pub fn is_expired(&self) -> bool {
return self.lifetime < 0.0;
}
pub fn get_sprite(&self, r: &RigidBody) -> Sprite {
let pos = util::rigidbody_position(r);
let ang = util::rigidbody_angle(r);
Sprite {
texture: self.sprite.clone(),
pos: Point3 {
x: pos.x,
y: pos.y,
z: 1.0,
},
size: self.size,
angle: ang,
children: None,
}
}
}

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::ProjectileBuilder, InputStatus}; use super::{super::InputStatus, ProjectileBuilder};
pub struct ShipControls { pub struct ShipControls {
pub left: bool, pub left: bool,

View File

@ -55,11 +55,11 @@ pub struct ShipOutfits {
} }
impl<'a> ShipOutfits { impl<'a> ShipOutfits {
pub fn new(enginepoints: Vec<content::EnginePoint>, gunpoints: Vec<content::GunPoint>) -> Self { pub fn new(content: &content::Ship) -> Self {
Self { Self {
outfits: Vec::new(), outfits: Vec::new(),
enginepoints, enginepoints: content.engines.clone(),
gunpoints, gunpoints: content.guns.clone(),
engine_flare_sprites: vec![], engine_flare_sprites: vec![],
} }
} }

View File

@ -1,41 +1,44 @@
use cgmath::{Deg, EuclideanSpace, Matrix2, Rad, Vector2}; use cgmath::{Deg, EuclideanSpace, Matrix2, Rad, Vector2};
use nalgebra::vector; use nalgebra::vector;
use rand::Rng; use rand::Rng;
use rapier2d::dynamics::{RigidBodyBuilder, RigidBodyHandle, RigidBodySet}; use rapier2d::dynamics::{RigidBody, RigidBodyBuilder};
use rapier2d::geometry::ColliderBuilder; use rapier2d::geometry::ColliderBuilder;
use rapier2d::pipeline::ActiveEvents; use rapier2d::pipeline::ActiveEvents;
use super::super::util; use super::ProjectileBuilder;
use super::{outfits::ShipOutfits, InputStatus, ShipControls, ShipOutfit, ShipTickResult}; use super::{outfits::ShipOutfits, InputStatus, ShipControls, ShipOutfit, ShipTickResult};
use crate::game::game::ProjectileBuilder; use crate::game::{util, world::ShipHandle};
use crate::{ use crate::{
content, content,
render::{Sprite, SpriteTexture}, render::{Sprite, SpriteTexture},
}; };
pub struct Ship { pub struct Ship {
pub rigid_body: RigidBodyHandle, pub physics_handle: ShipHandle,
pub controls: ShipControls,
outfits: ShipOutfits, outfits: ShipOutfits,
sprite: SpriteTexture, sprite: SpriteTexture,
size: f32, size: f32,
pub hull: f32,
// TODO: replace with AI enum
pub controls: ShipControls,
} }
impl Ship { impl Ship {
pub fn new(ct: &content::Ship, outfits: Vec<ShipOutfit>, rigid_body: RigidBodyHandle) -> Self { pub fn new(c: &content::Ship, outfits: Vec<ShipOutfit>, physics_handle: ShipHandle) -> Self {
let mut o = ShipOutfits::new(ct.engines.clone(), ct.guns.clone()); let mut o = ShipOutfits::new(c);
for x in outfits.into_iter() { for x in outfits.into_iter() {
o.add(x) o.add(x)
} }
Ship { Ship {
rigid_body, physics_handle,
controls: ShipControls::new(),
outfits: o, outfits: o,
sprite: SpriteTexture(ct.sprite.clone()), controls: ShipControls::new(),
size: ct.size, sprite: SpriteTexture(c.sprite.clone()),
size: c.size,
hull: c.hull,
} }
} }
@ -46,8 +49,7 @@ impl Ship {
self.controls.guns = input.key_guns; self.controls.guns = input.key_guns;
} }
pub fn fire_guns(&mut self, rigid_body_set: &RigidBodySet) -> Vec<ProjectileBuilder> { pub fn fire_guns(&mut self, r: &RigidBody) -> 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();
@ -93,13 +95,13 @@ impl Ship {
), ),
size: g.kind.projectile.size size: g.kind.projectile.size
+ rng.gen_range(-g.kind.projectile.size_rng..=g.kind.projectile.size_rng), + rng.gen_range(-g.kind.projectile.size_rng..=g.kind.projectile.size_rng),
damage: g.kind.projectile.damage, // TODO: kind as param to builder
}) })
} }
return out; return out;
} }
pub fn tick(&mut self, rigid_body_set: &mut RigidBodySet, t: f32) -> ShipTickResult { pub fn tick(&mut self, r: &mut RigidBody, t: f32) -> ShipTickResult {
let r = &mut rigid_body_set[self.rigid_body];
let ship_ang = util::rigidbody_angle(r); let ship_ang = util::rigidbody_angle(r);
let engine_force = Matrix2::from_angle(ship_ang) * Vector2 { x: 0.0, y: 1.0 } * t; let engine_force = Matrix2::from_angle(ship_ang) * Vector2 { x: 0.0, y: 1.0 } * t;
@ -118,7 +120,7 @@ impl Ship {
} }
let p = if self.controls.guns { let p = if self.controls.guns {
self.fire_guns(rigid_body_set) self.fire_guns(r)
} else { } else {
Vec::new() Vec::new()
}; };
@ -130,8 +132,7 @@ impl Ship {
return ShipTickResult { projectiles: p }; return ShipTickResult { projectiles: p };
} }
pub fn get_sprite(&self, rigid_body_set: &RigidBodySet) -> Sprite { pub fn get_sprite(&self, r: &RigidBody) -> Sprite {
let r = &rigid_body_set[self.rigid_body];
let ship_pos = util::rigidbody_position(r); let ship_pos = util::rigidbody_position(r);
let ship_ang = util::rigidbody_angle(r); let ship_ang = util::rigidbody_angle(r);

180
src/game/world/world.rs Normal file
View File

@ -0,0 +1,180 @@
use crossbeam::channel::Receiver;
use nalgebra::vector;
use rapier2d::{
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
geometry::{ColliderBuilder, ColliderHandle, CollisionEvent},
pipeline::ChannelEventCollector,
};
use std::collections::HashMap;
use super::{
physicswrapper::PhysicsWrapper, Projectile, ProjectileBuilder, Ship, ShipHandle, ShipOutfit,
};
use crate::{content, render::Sprite};
/// Keeps track of all objects in the world that we can interact with.
/// Also wraps our physics engine
pub struct World {
physics: PhysicsWrapper,
projectiles: HashMap<ColliderHandle, Projectile>,
ships: HashMap<ColliderHandle, Ship>,
collision_handler: ChannelEventCollector,
collision_queue: Receiver<CollisionEvent>,
}
// Private methods
impl World {
fn remove_projectile(&mut self, c: ColliderHandle) {
let p = match self.projectiles.remove(&c) {
Some(p) => p,
None => return,
};
self.physics.rigid_body_set.remove(
p.rigid_body,
&mut self.physics.im,
&mut self.physics.collider_set,
&mut self.physics.ij,
&mut self.physics.mj,
true,
);
}
fn remove_ship(&mut self, h: ShipHandle) {
self.physics.rigid_body_set.remove(
h.0,
&mut self.physics.im,
&mut self.physics.collider_set,
&mut self.physics.ij,
&mut self.physics.mj,
true,
);
self.ships.remove(&h.1);
}
fn add_projectile(&mut self, pb: ProjectileBuilder) -> ColliderHandle {
let r = self.physics.rigid_body_set.insert(pb.rigid_body.build());
let c = self.physics.collider_set.insert_with_parent(
pb.collider.build(),
r,
&mut self.physics.rigid_body_set,
);
self.projectiles.insert(c, pb.build(r, c));
return c;
}
}
// Public methods
impl World {
pub fn new() -> Self {
let (collision_send, collision_queue) = crossbeam::channel::unbounded();
let (contact_force_send, _) = crossbeam::channel::unbounded();
Self {
physics: PhysicsWrapper::new(),
projectiles: HashMap::new(),
ships: HashMap::new(),
collision_handler: ChannelEventCollector::new(collision_send, contact_force_send),
collision_queue,
}
}
pub fn add_ship(&mut self, ct: &content::Ship, outfits: Vec<ShipOutfit>) -> ShipHandle {
let rb = RigidBodyBuilder::dynamic()
.translation(vector![0.0, 0.0])
.can_sleep(false);
let cl = ColliderBuilder::ball(50.0).restitution(0.7).mass(1.0);
let r = self.physics.rigid_body_set.insert(rb.build());
let c = self.physics.collider_set.insert_with_parent(
cl.build(),
r,
&mut self.physics.rigid_body_set,
);
let h = ShipHandle(r, c);
self.ships.insert(c, Ship::new(ct, outfits, h));
return h;
}
pub fn tick(&mut self, t: f32) {
// Run ship updates
let mut res = Vec::new();
let mut to_remove = Vec::new();
for (_, s) in &mut self.ships {
if s.hull <= 0.0 {
to_remove.push(s.physics_handle);
continue;
}
let r = &mut self.physics.rigid_body_set[s.physics_handle.0];
res.push(s.tick(r, t));
}
for r in to_remove {
self.remove_ship(r);
}
for r in res {
for p in r.projectiles {
self.add_projectile(p);
}
}
// Update physics
self.physics.step(t, &self.collision_handler);
// Handle collision events
while let Ok(event) = &self.collision_queue.try_recv() {
if event.started() {
let a = &event.collider1();
let b = &event.collider2();
// If a projectile is part of the collision, make sure
// a is a projectile.
let (a, b) = if self.projectiles.contains_key(b) {
(b, a)
} else {
(a, b)
};
if let Some(p) = self.projectiles.get(a) {
if let Some(s) = self.ships.get_mut(b) {
s.hull -= p.damage;
}
self.remove_projectile(*a);
self.remove_projectile(*b);
}
}
}
// Delete projectiles
let mut to_remove = Vec::new();
for (_, p) in &mut self.projectiles {
p.tick(t);
if p.is_expired() {
to_remove.push(p.collider);
}
}
for i in to_remove {
self.remove_projectile(i);
}
}
pub fn get_rigid_body(&self, r: RigidBodyHandle) -> &RigidBody {
&self.physics.rigid_body_set[r]
}
pub fn get_ship_mut(&mut self, s: &ShipHandle) -> &mut Ship {
self.ships.get_mut(&s.1).unwrap()
}
pub fn get_ship_sprites(&self) -> impl Iterator<Item = Sprite> + '_ {
self.ships
.values()
.map(|x| x.get_sprite(&self.physics.rigid_body_set[x.physics_handle.0]))
}
pub fn get_projectile_sprites(&self) -> impl Iterator<Item = Sprite> + '_ {
self.projectiles
.values()
.map(|x| x.get_sprite(&self.physics.rigid_body_set[x.rigid_body]))
}
}

View File

@ -12,6 +12,7 @@ use winit::{
}; };
fn main() -> Result<()> { fn main() -> Result<()> {
// TODO: error if missing
let content = content::Content::load_dir(consts::CONTENT_ROOT)?; let content = content::Content::load_dir(consts::CONTENT_ROOT)?;
let game = game::Game::new(content); let game = game::Game::new(content);