Cleaned up physics, added basic ship damage
parent
f91ab7f3c5
commit
c6f22557f2
|
@ -20,3 +20,4 @@ projectile.speed_rng = 10.0
|
|||
# Lifetime of projectile, in seconds
|
||||
projectile.lifetime = 2.0
|
||||
projectile.lifetime_rng = 0.2
|
||||
projectile.damage = 10.0
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[ship."Gypsum"]
|
||||
sprite = "ship::gypsum"
|
||||
size = 100
|
||||
hull = 200
|
||||
|
||||
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 }]
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ pub(super) mod syntax {
|
|||
pub speed_rng: f32,
|
||||
pub lifetime: f32,
|
||||
pub lifetime_rng: f32,
|
||||
pub damage: f32,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +45,7 @@ pub struct Projectile {
|
|||
pub speed_rng: f32,
|
||||
pub lifetime: f32,
|
||||
pub lifetime_rng: f32,
|
||||
pub damage: f32,
|
||||
}
|
||||
|
||||
impl super::Build for Gun {
|
||||
|
@ -69,6 +71,7 @@ impl super::Build for Gun {
|
|||
speed_rng: gun.projectile.speed_rng,
|
||||
lifetime: gun.projectile.lifetime,
|
||||
lifetime_rng: gun.projectile.lifetime_rng,
|
||||
damage: gun.projectile.damage,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ pub(super) mod syntax {
|
|||
pub size: f32,
|
||||
pub engines: Vec<Engine>,
|
||||
pub guns: Vec<Gun>,
|
||||
pub hull: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -38,6 +39,7 @@ pub struct Ship {
|
|||
pub size: f32,
|
||||
pub engines: Vec<EnginePoint>,
|
||||
pub guns: Vec<GunPoint>,
|
||||
pub hull: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -67,6 +69,7 @@ impl super::Build for Ship {
|
|||
name: ship_name.to_owned(),
|
||||
sprite: ship.sprite.to_owned(),
|
||||
size,
|
||||
hull: ship.hull,
|
||||
engines: ship
|
||||
.engines
|
||||
.iter()
|
||||
|
|
176
src/game/game.rs
176
src/game/game.rs
|
@ -1,110 +1,47 @@
|
|||
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 std::time::Instant;
|
||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||
|
||||
use super::{camera::Camera, physics::Physics, ship, system::System, util, InputStatus};
|
||||
use crate::{
|
||||
consts,
|
||||
content::Content,
|
||||
render::{Sprite, SpriteTexture},
|
||||
use super::{
|
||||
camera::Camera,
|
||||
system::System,
|
||||
util,
|
||||
world::{ShipGun, ShipHandle, ShipOutfit, World},
|
||||
InputStatus,
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
use crate::{consts, content::Content, render::Sprite};
|
||||
|
||||
pub struct Game {
|
||||
pub input: InputStatus,
|
||||
pub last_update: Instant,
|
||||
pub player: ship::Ship,
|
||||
pub test: ship::Ship,
|
||||
pub player: ShipHandle,
|
||||
pub system: System,
|
||||
pub camera: Camera,
|
||||
paused: bool,
|
||||
pub time_scale: f32,
|
||||
|
||||
pub projectiles: HashMap<ColliderHandle, Projectile>,
|
||||
|
||||
collision_handler: ChannelEventCollector,
|
||||
physics: Physics,
|
||||
|
||||
collision_recv: Receiver<CollisionEvent>,
|
||||
world: World,
|
||||
}
|
||||
|
||||
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 mut world = World::new();
|
||||
|
||||
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 c = world.add_ship(
|
||||
&ct.ships[0],
|
||||
vec![
|
||||
ShipOutfit::Gun(ShipGun::new(ct.guns[0].clone(), 1)),
|
||||
ShipOutfit::Gun(ShipGun::new(ct.guns[0].clone(), 2)),
|
||||
ShipOutfit::Engine(ct.engines[0].clone()),
|
||||
],
|
||||
);
|
||||
|
||||
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();
|
||||
world.add_ship(&ct.ships[0], vec![]);
|
||||
|
||||
Game {
|
||||
last_update: Instant::now(),
|
||||
input: InputStatus::new(),
|
||||
projectiles: HashMap::new(),
|
||||
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,
|
||||
),
|
||||
player: c,
|
||||
|
||||
camera: Camera {
|
||||
pos: (0.0, 0.0).into(),
|
||||
zoom: 500.0,
|
||||
|
@ -113,11 +50,7 @@ impl Game {
|
|||
|
||||
paused: false,
|
||||
time_scale: 1.0,
|
||||
test: ship::Ship::new(&ct.ships[0], vec![], rh2),
|
||||
|
||||
physics: p,
|
||||
collision_handler: ChannelEventCollector::new(collision_send, contact_force_send),
|
||||
collision_recv,
|
||||
world,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,42 +78,11 @@ impl Game {
|
|||
pub fn update(&mut self) {
|
||||
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| {
|
||||
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.
|
||||
}
|
||||
}
|
||||
self.world.tick(t);
|
||||
|
||||
if self.input.v_scroll != 0.0 {
|
||||
self.camera.zoom =
|
||||
|
@ -189,10 +91,10 @@ impl Game {
|
|||
}
|
||||
|
||||
// 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);
|
||||
self.camera.pos = ship_pos;
|
||||
|
||||
self.last_update = Instant::now();
|
||||
}
|
||||
|
||||
|
@ -200,8 +102,7 @@ impl Game {
|
|||
let mut sprites: Vec<Sprite> = Vec::new();
|
||||
|
||||
sprites.append(&mut self.system.get_sprites());
|
||||
sprites.push(self.player.get_sprite(&self.physics.rigid_body_set));
|
||||
sprites.push(self.test.get_sprite(&self.physics.rigid_body_set));
|
||||
sprites.extend(self.world.get_ship_sprites());
|
||||
|
||||
// Make sure sprites are drawn in the correct order
|
||||
// (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));
|
||||
|
||||
// Don't waste time sorting these, they should always be on top.
|
||||
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: pos.x,
|
||||
y: pos.y,
|
||||
z: 1.0,
|
||||
},
|
||||
size: p.size,
|
||||
angle: ang,
|
||||
children: None,
|
||||
})
|
||||
}
|
||||
sprites.extend(self.world.get_projectile_sprites());
|
||||
|
||||
return sprites;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
mod camera;
|
||||
mod game;
|
||||
mod inputstatus;
|
||||
mod physics;
|
||||
mod ship;
|
||||
mod system;
|
||||
mod systemobject;
|
||||
mod util;
|
||||
mod world;
|
||||
|
||||
pub use game::Game;
|
||||
pub use inputstatus::InputStatus;
|
||||
|
|
|
@ -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);
|
|
@ -8,21 +8,21 @@ use rapier2d::{
|
|||
pipeline::{EventHandler, PhysicsPipeline},
|
||||
};
|
||||
|
||||
pub struct Physics {
|
||||
pub(super) struct PhysicsWrapper {
|
||||
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,
|
||||
pub ip: IntegrationParameters,
|
||||
pub pp: PhysicsPipeline,
|
||||
pub im: IslandManager,
|
||||
pub bp: BroadPhase,
|
||||
pub np: NarrowPhase,
|
||||
pub ij: ImpulseJointSet,
|
||||
pub mj: MultibodyJointSet,
|
||||
pub ccd: CCDSolver,
|
||||
}
|
||||
|
||||
impl Physics {
|
||||
impl PhysicsWrapper {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
rigid_body_set: RigidBodySet::new(),
|
||||
|
@ -33,8 +33,8 @@ impl Physics {
|
|||
im: IslandManager::new(),
|
||||
bp: BroadPhase::new(),
|
||||
np: NarrowPhase::new(),
|
||||
ijs: ImpulseJointSet::new(),
|
||||
mjs: MultibodyJointSet::new(),
|
||||
ij: ImpulseJointSet::new(),
|
||||
mj: MultibodyJointSet::new(),
|
||||
ccd: CCDSolver::new(),
|
||||
}
|
||||
}
|
||||
|
@ -49,8 +49,8 @@ impl Physics {
|
|||
&mut self.np,
|
||||
&mut self.rigid_body_set,
|
||||
&mut self.collider_set,
|
||||
&mut self.ijs,
|
||||
&mut self.mjs,
|
||||
&mut self.ij,
|
||||
&mut self.mj,
|
||||
&mut self.ccd,
|
||||
None,
|
||||
&(),
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ mod ship;
|
|||
pub use outfits::{ShipGun, ShipOutfit, ShipOutfits};
|
||||
pub use ship::Ship;
|
||||
|
||||
use super::{game::ProjectileBuilder, InputStatus};
|
||||
use super::{super::InputStatus, ProjectileBuilder};
|
||||
|
||||
pub struct ShipControls {
|
||||
pub left: bool,
|
|
@ -55,11 +55,11 @@ pub struct ShipOutfits {
|
|||
}
|
||||
|
||||
impl<'a> ShipOutfits {
|
||||
pub fn new(enginepoints: Vec<content::EnginePoint>, gunpoints: Vec<content::GunPoint>) -> Self {
|
||||
pub fn new(content: &content::Ship) -> Self {
|
||||
Self {
|
||||
outfits: Vec::new(),
|
||||
enginepoints,
|
||||
gunpoints,
|
||||
enginepoints: content.engines.clone(),
|
||||
gunpoints: content.guns.clone(),
|
||||
engine_flare_sprites: vec![],
|
||||
}
|
||||
}
|
|
@ -1,41 +1,44 @@
|
|||
use cgmath::{Deg, EuclideanSpace, Matrix2, Rad, Vector2};
|
||||
use nalgebra::vector;
|
||||
use rand::Rng;
|
||||
use rapier2d::dynamics::{RigidBodyBuilder, RigidBodyHandle, RigidBodySet};
|
||||
use rapier2d::dynamics::{RigidBody, RigidBodyBuilder};
|
||||
use rapier2d::geometry::ColliderBuilder;
|
||||
use rapier2d::pipeline::ActiveEvents;
|
||||
|
||||
use super::super::util;
|
||||
use super::ProjectileBuilder;
|
||||
use super::{outfits::ShipOutfits, InputStatus, ShipControls, ShipOutfit, ShipTickResult};
|
||||
use crate::game::game::ProjectileBuilder;
|
||||
use crate::game::{util, world::ShipHandle};
|
||||
use crate::{
|
||||
content,
|
||||
render::{Sprite, SpriteTexture},
|
||||
};
|
||||
|
||||
pub struct Ship {
|
||||
pub rigid_body: RigidBodyHandle,
|
||||
pub controls: ShipControls,
|
||||
pub physics_handle: ShipHandle,
|
||||
outfits: ShipOutfits,
|
||||
|
||||
sprite: SpriteTexture,
|
||||
size: f32,
|
||||
pub hull: f32,
|
||||
|
||||
// TODO: replace with AI enum
|
||||
pub controls: ShipControls,
|
||||
}
|
||||
|
||||
impl Ship {
|
||||
pub fn new(ct: &content::Ship, outfits: Vec<ShipOutfit>, rigid_body: RigidBodyHandle) -> Self {
|
||||
let mut o = ShipOutfits::new(ct.engines.clone(), ct.guns.clone());
|
||||
pub fn new(c: &content::Ship, outfits: Vec<ShipOutfit>, physics_handle: ShipHandle) -> Self {
|
||||
let mut o = ShipOutfits::new(c);
|
||||
for x in outfits.into_iter() {
|
||||
o.add(x)
|
||||
}
|
||||
|
||||
Ship {
|
||||
rigid_body,
|
||||
controls: ShipControls::new(),
|
||||
|
||||
physics_handle,
|
||||
outfits: o,
|
||||
sprite: SpriteTexture(ct.sprite.clone()),
|
||||
size: ct.size,
|
||||
controls: ShipControls::new(),
|
||||
sprite: SpriteTexture(c.sprite.clone()),
|
||||
size: c.size,
|
||||
hull: c.hull,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,8 +49,7 @@ impl Ship {
|
|||
self.controls.guns = input.key_guns;
|
||||
}
|
||||
|
||||
pub fn fire_guns(&mut self, rigid_body_set: &RigidBodySet) -> Vec<ProjectileBuilder> {
|
||||
let r = &rigid_body_set[self.rigid_body];
|
||||
pub fn fire_guns(&mut self, r: &RigidBody) -> Vec<ProjectileBuilder> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut out = Vec::new();
|
||||
|
||||
|
@ -93,13 +95,13 @@ impl Ship {
|
|||
),
|
||||
size: g.kind.projectile.size
|
||||
+ 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;
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, rigid_body_set: &mut RigidBodySet, t: f32) -> ShipTickResult {
|
||||
let r = &mut rigid_body_set[self.rigid_body];
|
||||
pub fn tick(&mut self, r: &mut RigidBody, t: f32) -> ShipTickResult {
|
||||
let ship_ang = util::rigidbody_angle(r);
|
||||
|
||||
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 {
|
||||
self.fire_guns(rigid_body_set)
|
||||
self.fire_guns(r)
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
@ -130,8 +132,7 @@ impl Ship {
|
|||
return ShipTickResult { projectiles: p };
|
||||
}
|
||||
|
||||
pub fn get_sprite(&self, rigid_body_set: &RigidBodySet) -> Sprite {
|
||||
let r = &rigid_body_set[self.rigid_body];
|
||||
pub fn get_sprite(&self, r: &RigidBody) -> Sprite {
|
||||
let ship_pos = util::rigidbody_position(r);
|
||||
let ship_ang = util::rigidbody_angle(r);
|
||||
|
|
@ -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]))
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ use winit::{
|
|||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// TODO: error if missing
|
||||
let content = content::Content::load_dir(consts::CONTENT_ROOT)?;
|
||||
let game = game::Game::new(content);
|
||||
|
||||
|
|
Loading…
Reference in New Issue