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
|
# 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
|
||||||
|
|
|
@ -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 }]
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
176
src/game/game.rs
176
src/game/game.rs
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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},
|
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,
|
||||||
&(),
|
&(),
|
|
@ -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 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,
|
|
@ -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![],
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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<()> {
|
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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue