diff --git a/Cargo.lock b/Cargo.lock index 36c3a4e..8822da9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -644,6 +644,7 @@ dependencies = [ "cgmath", "galactica-content", "galactica-galaxy", + "galactica-playeragent", "galactica-render", "galactica-systemsim", "galactica-util", @@ -687,6 +688,20 @@ dependencies = [ "walkdir", ] +[[package]] +name = "galactica-playeragent" +version = "0.0.0" +dependencies = [ + "anyhow", + "cgmath", + "galactica-content", + "galactica-galaxy", + "galactica-util", + "pollster", + "wgpu", + "winit", +] + [[package]] name = "galactica-render" version = "0.0.0" @@ -714,6 +729,7 @@ dependencies = [ "crossbeam", "galactica-content", "galactica-galaxy", + "galactica-playeragent", "galactica-util", "nalgebra", "rand", diff --git a/Cargo.toml b/Cargo.toml index 29de197..61c46dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ galactica-render = { path = "crates/render" } galactica-systemsim = { path = "crates/systemsim" } galactica-galaxy = { path = "crates/galaxy" } galactica-packer = { path = "crates/packer" } +galactica-playeragent = { path = "crates/playeragent" } galactica = { path = "crates/galactica" } image = { version = "0.24", features = ["png"] } diff --git a/crates/galactica/Cargo.toml b/crates/galactica/Cargo.toml index 27c65e7..0729a85 100644 --- a/crates/galactica/Cargo.toml +++ b/crates/galactica/Cargo.toml @@ -26,6 +26,7 @@ galactica-render = { workspace = true } galactica-util = { workspace = true } galactica-systemsim = { workspace = true } galactica-galaxy = { workspace = true } +galactica-playeragent = { workspace = true } winit = { workspace = true } wgpu = { workspace = true } diff --git a/crates/galactica/src/game.rs b/crates/galactica/src/game.rs index b914a01..9f6f154 100644 --- a/crates/galactica/src/game.rs +++ b/crates/galactica/src/game.rs @@ -1,119 +1,112 @@ use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle}; use galactica_galaxy::{ship::ShipPersonality, Galaxy, GxShipHandle}; +use galactica_playeragent::{PlayerAgent, PlayerStatus}; +use galactica_render::RenderInput; +use galactica_systemsim::{util, ParticleBuilder, StepResources, SystemSim, Wrapper}; use galactica_util::timing::Timing; use std::time::Instant; -use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode}; -use crate::camera::Camera; -use crate::inputstatus::InputStatus; -use galactica_render::RenderInput; -use galactica_systemsim::{objects::ShipControls, util, ParticleBuilder, StepResources, SystemSim}; +#[derive(Clone)] +pub struct GameState { + pub gx: Galaxy, + pub systemsim: SystemSim, +} pub struct Game { - input: InputStatus, - last_update: Instant, - player: GxShipHandle, - paused: bool, - time_scale: f32, - start_instant: Instant, - camera: Camera, - + // Core game data ct: Content, - gx: Galaxy, - - systemsim: SystemSim, + state_live: GameState, + state_static: GameState, + // Metadata + wrapper: Wrapper, // Physics computer + time_scale: f32, + last_update: Instant, + start_instant: Instant, new_particles: Vec, timing: Timing, } impl Game { - pub fn new(ct: Content) -> Self { - let mut galaxy = Galaxy::new(&ct); + pub fn new(ct: Content) -> (Self, GxShipHandle) { + let mut gx = Galaxy::new(&ct); - let player = galaxy.create_ship( + let player = gx.create_ship( &ct, ShipHandle { index: 0 }, FactionHandle { index: 0 }, ShipPersonality::Player, &SystemHandle { index: 0 }, ); - let s = galaxy.get_ship_mut(player).unwrap(); + let s = gx.get_ship_mut(player).unwrap(); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 })); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 1 })); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 })); - let a = galaxy.create_ship( + let a = gx.create_ship( &ct, ShipHandle { index: 0 }, FactionHandle { index: 1 }, ShipPersonality::Dummy, &SystemHandle { index: 0 }, ); - let s = galaxy.get_ship_mut(a).unwrap(); + let s = gx.get_ship_mut(a).unwrap(); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 })); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 1 })); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 })); - let a = galaxy.create_ship( + let a = gx.create_ship( &ct, ShipHandle { index: 0 }, FactionHandle { index: 0 }, ShipPersonality::Point, &SystemHandle { index: 0 }, ); - let s = galaxy.get_ship_mut(a).unwrap(); + let s = gx.get_ship_mut(a).unwrap(); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 })); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 1 })); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 })); - let physics = SystemSim::new(&ct, &galaxy, SystemHandle { index: 0 }); + let systemsim = SystemSim::new(&ct, &gx, SystemHandle { index: 0 }); - Game { - last_update: Instant::now(), - input: InputStatus::new(), - player, - start_instant: Instant::now(), + let state = GameState { gx, systemsim }; - camera: Camera { - pos: (0.0, 0.0).into(), - zoom: 500.0, - aspect: 1.0, + ( + Game { + ct, + state_live: state.clone(), + state_static: state, + + wrapper: Wrapper::new(), + + last_update: Instant::now(), + start_instant: Instant::now(), + time_scale: 1.0, + new_particles: Vec::new(), + timing: Timing::new(), }, - //system: object::System::new(&ct, SystemHandle { index: 0 }), - paused: false, - time_scale: 1.0, - systemsim: physics, - gx: galaxy, - ct, - new_particles: Vec::new(), - timing: Timing::new(), - } + player, + ) } - pub fn set_camera_aspect(&mut self, v: f32) { - self.camera.aspect = v + pub fn update_player_controls(&mut self, player: &PlayerAgent) { + self.state_live.systemsim.update_player_controls(player) } - pub fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) { - self.input.process_key(state, key) - } + pub fn get_player_status(&self, player: &PlayerAgent) -> PlayerStatus { + let o = self + .state_static + .systemsim + .get_ship(player.ship.unwrap()) + .unwrap(); + let r = self + .state_static + .systemsim + .get_rigid_body(o.rigid_body) + .unwrap(); + let pos = util::rigidbody_position(r); - pub fn process_click(&mut self, state: &ElementState, key: &MouseButton) { - self.input.process_click(state, key) - } - - pub fn process_scroll(&mut self, delta: &MouseScrollDelta, phase: &TouchPhase) { - self.input.process_scroll(delta, phase) - } - - pub fn set_paused(&mut self, pause: bool) { - if pause { - self.paused = true; - self.input.release_all() - } else { - self.paused = false; - } + PlayerStatus { pos } } pub fn update(&mut self) { @@ -121,50 +114,33 @@ impl Game { self.timing.start_frame(); self.timing.start_galaxy(); - self.gx.step(t); + self.state_live.gx.step(t); self.timing.mark_galaxy(); - self.systemsim.step(StepResources { - player: self.player, - player_controls: ShipControls { - left: self.input.key_left, - right: self.input.key_right, - thrust: self.input.key_thrust, - guns: self.input.key_guns, - }, + self.state_live.systemsim.step(StepResources { ct: &self.ct, - gx: &mut self.gx, + gx: &mut self.state_live.gx, particles: &mut self.new_particles, timing: &mut self.timing, + wrapper: &mut self.wrapper, t, }); - if self.input.v_scroll != 0.0 { - self.camera.zoom = (self.camera.zoom + self.input.v_scroll) - .clamp(self.ct.get_config().zoom_min, self.ct.get_config().zoom_max); - self.input.v_scroll = 0.0; - } - - self.camera.pos = { - let o = self.systemsim.get_ship(self.player).unwrap(); - let r = self.systemsim.get_rigid_body(o.rigid_body).unwrap(); - util::rigidbody_position(r) - }; - self.last_update = Instant::now(); + self.state_static = self.state_live.clone(); self.timing.mark_frame(); } - pub fn get_frame_state(&mut self) -> RenderInput { + pub fn get_frame_state(&mut self, player: &PlayerAgent) -> RenderInput { RenderInput { - camera_pos: self.camera.pos, - camera_zoom: self.camera.zoom, + camera_pos: player.camera.pos, + camera_zoom: player.camera.zoom, current_time: self.start_instant.elapsed().as_secs_f32(), ct: &self.ct, - systemsim: &self.systemsim, // TODO: maybe system should be stored here? + systemsim: &self.state_static.systemsim, // TODO: system should be stored here, not in game particles: &mut self.new_particles, - player_data: self.player, - gx: &self.gx, + player_data: player.ship.unwrap(), + gx: &self.state_static.gx, current_system: SystemHandle { index: 0 }, timing: &mut self.timing, } diff --git a/crates/galactica/src/main.rs b/crates/galactica/src/main.rs index 5010e2f..342f9c4 100644 --- a/crates/galactica/src/main.rs +++ b/crates/galactica/src/main.rs @@ -1,9 +1,8 @@ -mod camera; mod game; -mod inputstatus; use anyhow::{bail, Result}; use galactica_content::Content; +use galactica_playeragent::PlayerAgent; use galactica_util::constants::ASSET_CACHE; use std::{ fs, @@ -37,21 +36,26 @@ fn main() -> Result<()> { let mut gpu = pollster::block_on(galactica_render::GPUState::new(window, &content))?; gpu.init(&content); - let mut game = game::Game::new(content); - game.set_camera_aspect( + let (mut game, p) = game::Game::new(content); + + let mut player = PlayerAgent::new(p); + + player.set_camera_aspect( gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32, ); event_loop.run(move |event, _, control_flow| { match event { Event::RedrawRequested(window_id) if window_id == gpu.window().id() => { - match gpu.render(game.get_frame_state()) { + match gpu.render(game.get_frame_state(&player)) { Ok(_) => {} Err(wgpu::SurfaceError::Lost) => gpu.resize(game.get_content()), Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, // All other errors (Outdated, Timeout) should be resolved by the next frame Err(e) => eprintln!("{:?}", e), } + let status = game.get_player_status(&player); + player.step(game.get_content(), status); } Event::MainEventsCleared => { @@ -63,8 +67,8 @@ fn main() -> Result<()> { ref event, window_id, } if window_id == gpu.window().id() => match event { - WindowEvent::Focused(state) => { - game.set_paused(!state); + WindowEvent::Focused(_state) => { + //game.set_paused(!state); } WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::KeyboardInput { @@ -75,23 +79,28 @@ fn main() -> Result<()> { .. }, .. - } => game.process_key(state, key), + } => { + player.process_key(state, key); + game.update_player_controls(&player); + } WindowEvent::MouseInput { state, button, .. } => { - game.process_click(state, button); + player.process_click(state, button); + game.update_player_controls(&player); } WindowEvent::MouseWheel { delta, phase, .. } => { - game.process_scroll(delta, phase); + player.process_scroll(delta, phase); + game.update_player_controls(&player); } WindowEvent::Resized(_) => { gpu.resize(game.get_content()); - game.set_camera_aspect( + player.set_camera_aspect( gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32, ); } WindowEvent::ScaleFactorChanged { .. } => { gpu.resize(game.get_content()); - game.set_camera_aspect( + player.set_camera_aspect( gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32, ); diff --git a/crates/playeragent/Cargo.toml b/crates/playeragent/Cargo.toml new file mode 100644 index 0000000..ed75843 --- /dev/null +++ b/crates/playeragent/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "galactica-playeragent" +description = "TODO" +categories = { workspace = true } +keywords = { workspace = true } +version = { workspace = true } +rust-version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } +license = { workspace = true } +documentation = { workspace = true } +readme = { workspace = true } + +[lints] +workspace = true + +[dependencies] +galactica-content = { workspace = true } +galactica-util = { workspace = true } +galactica-galaxy = { workspace = true } + +winit = { workspace = true } +wgpu = { workspace = true } +pollster = { workspace = true } +anyhow = { workspace = true } +cgmath = { workspace = true } diff --git a/crates/galactica/src/camera.rs b/crates/playeragent/src/camera.rs similarity index 69% rename from crates/galactica/src/camera.rs rename to crates/playeragent/src/camera.rs index 42f6fdf..f1c2dd7 100644 --- a/crates/galactica/src/camera.rs +++ b/crates/playeragent/src/camera.rs @@ -12,3 +12,13 @@ pub struct Camera { /// Aspect ratio of viewport (width / height) pub aspect: f32, } + +impl Camera { + pub fn new() -> Self { + Self { + pos: (0.0, 0.0).into(), + zoom: 500.0, + aspect: 1.0, + } + } +} diff --git a/crates/galactica/src/inputstatus.rs b/crates/playeragent/src/inputstatus.rs similarity index 100% rename from crates/galactica/src/inputstatus.rs rename to crates/playeragent/src/inputstatus.rs diff --git a/crates/playeragent/src/lib.rs b/crates/playeragent/src/lib.rs new file mode 100644 index 0000000..c46d594 --- /dev/null +++ b/crates/playeragent/src/lib.rs @@ -0,0 +1,9 @@ +mod camera; +mod inputstatus; +mod playeragent; +mod playerstatus; + +pub use camera::Camera; +pub use inputstatus::InputStatus; +pub use playeragent::PlayerAgent; +pub use playerstatus::PlayerStatus; diff --git a/crates/playeragent/src/playeragent.rs b/crates/playeragent/src/playeragent.rs new file mode 100644 index 0000000..1510f5a --- /dev/null +++ b/crates/playeragent/src/playeragent.rs @@ -0,0 +1,56 @@ +use galactica_content::Content; +use galactica_galaxy::GxShipHandle; +use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode}; + +use crate::{camera::Camera, inputstatus::InputStatus, PlayerStatus}; + +pub struct PlayerAgent { + /// Which ship this player is controlling + pub ship: Option, + + /// This player's camera + pub camera: Camera, + + /// What buttons this player is pressing + pub input: InputStatus, + + /// What the player currently has selected + pub selection: Option<()>, +} + +impl PlayerAgent { + pub fn new(ship: GxShipHandle) -> Self { + Self { + input: InputStatus::new(), + camera: Camera::new(), + ship: Some(ship), + selection: None, + } + } + + pub fn set_camera_aspect(&mut self, v: f32) { + self.camera.aspect = v + } + + pub fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) { + self.input.process_key(state, key) + } + + pub fn process_click(&mut self, state: &ElementState, key: &MouseButton) { + self.input.process_click(state, key) + } + + pub fn process_scroll(&mut self, delta: &MouseScrollDelta, phase: &TouchPhase) { + self.input.process_scroll(delta, phase) + } + + pub fn step(&mut self, ct: &Content, status: PlayerStatus) { + if self.input.v_scroll != 0.0 { + self.camera.zoom = (self.camera.zoom + self.input.v_scroll) + .clamp(ct.get_config().zoom_min, ct.get_config().zoom_max); + self.input.v_scroll = 0.0; + } + + self.camera.pos = status.pos; + } +} diff --git a/crates/playeragent/src/playerstatus.rs b/crates/playeragent/src/playerstatus.rs new file mode 100644 index 0000000..1e9321f --- /dev/null +++ b/crates/playeragent/src/playerstatus.rs @@ -0,0 +1,5 @@ +use cgmath::Point2; + +pub struct PlayerStatus { + pub pos: Point2, +} diff --git a/crates/systemsim/Cargo.toml b/crates/systemsim/Cargo.toml index c706517..a54d3f3 100644 --- a/crates/systemsim/Cargo.toml +++ b/crates/systemsim/Cargo.toml @@ -20,6 +20,7 @@ workspace = true galactica-content = { workspace = true } galactica-galaxy = { workspace = true } galactica-util = { workspace = true } +galactica-playeragent = { workspace = true } rapier2d = { workspace = true } nalgebra = { workspace = true } diff --git a/crates/systemsim/src/lib.rs b/crates/systemsim/src/lib.rs index 0c54b26..e289510 100644 --- a/crates/systemsim/src/lib.rs +++ b/crates/systemsim/src/lib.rs @@ -13,3 +13,4 @@ mod wrapper; pub use particlebuilder::*; pub use stepresources::*; pub use systemsim::SystemSim; +pub use wrapper::Wrapper; diff --git a/crates/systemsim/src/stepresources.rs b/crates/systemsim/src/stepresources.rs index 93c41ba..fe5dc6a 100644 --- a/crates/systemsim/src/stepresources.rs +++ b/crates/systemsim/src/stepresources.rs @@ -1,10 +1,9 @@ -use crate::{objects::ShipControls, ParticleBuilder}; +use crate::{wrapper::Wrapper, ParticleBuilder}; use galactica_content::Content; -use galactica_galaxy::{Galaxy, GxShipHandle}; +use galactica_galaxy::Galaxy; use galactica_util::timing::Timing; /// External resources we need to compute time steps -#[derive(Debug)] pub struct StepResources<'a> { /// Game content pub ct: &'a Content, @@ -18,12 +17,9 @@ pub struct StepResources<'a> { /// Particles to create pub particles: &'a mut Vec, - /// Player inputs - pub player_controls: ShipControls, - - /// The ship that the player controls - pub player: GxShipHandle, - /// Record detailed frame timing breakdown pub timing: &'a mut Timing, + + /// Physics computer + pub wrapper: &'a mut Wrapper, } diff --git a/crates/systemsim/src/systemsim.rs b/crates/systemsim/src/systemsim.rs index 2c7102b..2e510f2 100644 --- a/crates/systemsim/src/systemsim.rs +++ b/crates/systemsim/src/systemsim.rs @@ -1,58 +1,63 @@ use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero}; -use crossbeam::channel::Receiver; use galactica_content::{ Content, GunPoint, OutfitHandle, ProjectileCollider, Relationship, SystemHandle, }; -use galactica_galaxy::{ship::GxShip, Galaxy, GxShipHandle}; +use galactica_galaxy::{ + ship::{GxShip, ShipPersonality}, + Galaxy, GxShipHandle, +}; +use galactica_playeragent::PlayerAgent; use nalgebra::{point, vector}; use rand::Rng; use rapier2d::{ - dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle}, - geometry::{ColliderBuilder, ColliderHandle, CollisionEvent}, - pipeline::{ActiveEvents, ChannelEventCollector}, + dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle, RigidBodySet}, + geometry::{ColliderBuilder, ColliderHandle, ColliderSet}, + pipeline::ActiveEvents, }; use std::{collections::HashMap, f32::consts::PI}; use crate::{ - controller::{self, ShipController}, - objects, + controller::ShipController, objects::{SySimProjectile, SySimShip}, - util, - wrapper::Wrapper, - ParticleBuilder, StepResources, + util, ParticleBuilder, StepResources, }; /// Manages the physics state of one system +#[derive(Clone)] pub struct SystemSim { /// The system this sim is attached to _system: SystemHandle, - wrapper: Wrapper, - projectiles: HashMap, - ships: HashMap, - ship_behaviors: HashMap>, - collider_ship_table: HashMap, + rigid_body_set: RigidBodySet, + collider_set: ColliderSet, - collision_handler: ChannelEventCollector, - collision_queue: Receiver, + projectiles: HashMap, + ships: HashMap, + + // TODO: these don't need to be cloned each frame + ship_behaviors: HashMap, + collider_ship_table: HashMap, } // Private methods impl<'a> SystemSim { - fn remove_projectile(&mut self, c: ColliderHandle) -> Option<(RigidBody, SySimProjectile)> { + fn remove_projectile( + &mut self, + res: &mut StepResources, + c: ColliderHandle, + ) -> Option<(RigidBody, SySimProjectile)> { let p = match self.projectiles.remove(&c) { Some(p) => p, None => return None, }; let r = self - .wrapper .rigid_body_set .remove( p.rigid_body, - &mut self.wrapper.im, - &mut self.wrapper.collider_set, - &mut self.wrapper.ij, - &mut self.wrapper.mj, + &mut res.wrapper.im, + &mut self.collider_set, + &mut res.wrapper.ij, + &mut res.wrapper.mj, true, ) .unwrap(); @@ -60,7 +65,7 @@ impl<'a> SystemSim { return Some((r, p)); } - fn remove_ship(&mut self, s: ColliderHandle) { + fn remove_ship(&mut self, res: &mut StepResources, s: ColliderHandle) { let s = match self.collider_ship_table.get(&s) { None => return, Some(s) => match self.ships.get(s) { @@ -69,12 +74,12 @@ impl<'a> SystemSim { }, }; - self.wrapper.rigid_body_set.remove( + self.rigid_body_set.remove( s.rigid_body, - &mut self.wrapper.im, - &mut self.wrapper.collider_set, - &mut self.wrapper.ij, - &mut self.wrapper.mj, + &mut res.wrapper.im, + &mut self.collider_set, + &mut res.wrapper.ij, + &mut res.wrapper.mj, true, ); let h = self.collider_ship_table.remove(&s.collider).unwrap(); @@ -112,28 +117,16 @@ impl<'a> SystemSim { }; if destory_projectile { - let pr = self - .wrapper - .rigid_body_set - .get(projectile.rigid_body) - .unwrap(); + let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap(); let v = util::rigidbody_velocity(pr).normalize() * projectile.content.force; let pos = util::rigidbody_position(pr); let _ = pr; - let r = self - .wrapper - .rigid_body_set - .get_mut(ship.rigid_body) - .unwrap(); + let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap(); r.apply_impulse_at_point(vector![v.x, v.y], point![pos.x, pos.y], true); // Borrow again, we can only have one at a time - let pr = self - .wrapper - .rigid_body_set - .get(projectile.rigid_body) - .unwrap(); + let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap(); let pos = util::rigidbody_position(pr); let angle = util::rigidbody_rotation(pr).angle(Vector2 { x: 1.0, y: 0.0 }); @@ -160,7 +153,7 @@ impl<'a> SystemSim { } }; - self.remove_projectile(projectile.collider); + self.remove_projectile(res, projectile.collider); } } } @@ -169,18 +162,14 @@ impl<'a> SystemSim { impl SystemSim { /// Create a new physics system pub fn new(ct: &Content, gx: &Galaxy, system: SystemHandle) -> Self { - let (collision_send, collision_queue) = crossbeam::channel::unbounded(); - let (contact_force_send, _) = crossbeam::channel::unbounded(); - let mut w = Self { _system: system, - wrapper: Wrapper::new(), + rigid_body_set: RigidBodySet::new(), + collider_set: ColliderSet::new(), projectiles: HashMap::new(), ships: HashMap::new(), ship_behaviors: HashMap::new(), collider_ship_table: HashMap::new(), - collision_handler: ChannelEventCollector::new(collision_send, contact_force_send), - collision_queue, }; // TODO: guarantee not touching @@ -220,22 +209,36 @@ impl SystemSim { .translation(vector![position.x, position.y]) .can_sleep(false); - let r = self.wrapper.rigid_body_set.insert(rb.build()); - let c = self.wrapper.collider_set.insert_with_parent( - cl.build(), - r, - &mut self.wrapper.rigid_body_set, - ); + let r = self.rigid_body_set.insert(rb.build()); + let c = self + .collider_set + .insert_with_parent(cl.build(), r, &mut self.rigid_body_set); self.collider_ship_table.insert(c, ship.get_handle()); - self.ship_behaviors - .insert(ship.get_handle(), Box::new(controller::Point::new())); + + self.ship_behaviors.insert( + ship.get_handle(), + match ship.get_personality() { + ShipPersonality::Dummy | ShipPersonality::Player => ShipController::new_null(), + ShipPersonality::Point => ShipController::new_point(), + }, + ); + self.ships.insert( ship.get_handle(), - objects::SySimShip::new(ct, ship.get_handle(), ship.get_faction(), r, c), + SySimShip::new(ct, ship.get_handle(), ship.get_faction(), r, c), ); } + /// Update a player ship's controls + pub fn update_player_controls(&mut self, player: &PlayerAgent) { + let ship_object = self.ships.get_mut(&player.ship.unwrap()).unwrap(); + ship_object.controls.guns = player.input.key_guns; + ship_object.controls.left = player.input.key_left; + ship_object.controls.right = player.input.key_right; + ship_object.controls.thrust = player.input.key_thrust; + } + /// Run ship updates: /// - updates ship controls (runs behaviors) /// - applies ship controls @@ -261,34 +264,32 @@ impl SystemSim { let ship_object = self.ships.get_mut(handle).unwrap(); ship_object.step( res, - &mut self.wrapper.rigid_body_set[ship_object.rigid_body], - &mut self.wrapper.collider_set[ship_object.collider], + &mut self.rigid_body_set[ship_object.rigid_body], + &mut self.collider_set[ship_object.collider], ); continue; } // Compute new controls let controls; - if ship_object.data_handle == res.player { - controls = res.player_controls.clone(); - } else { - let b = self.ship_behaviors.get_mut(handle).unwrap(); - controls = b.update_controls( - &res, - &self.wrapper.rigid_body_set, - &self.ships, - ship_object.rigid_body, - *handle, - ); - } + let b = self.ship_behaviors.get_mut(handle).unwrap(); + controls = b.update_controls( + &res, + &self.rigid_body_set, + &self.ships, + ship_object.rigid_body, + *handle, + ); // Now re-borrow mutably to apply changes let ship_object = self.ships.get_mut(handle).unwrap(); - ship_object.controls = controls; + if let Some(controls) = controls { + ship_object.controls = controls; + } ship_object.step( res, - &mut self.wrapper.rigid_body_set[ship_object.rigid_body], - &mut self.wrapper.collider_set[ship_object.collider], + &mut self.rigid_body_set[ship_object.rigid_body], + &mut self.collider_set[ship_object.collider], ); // If we're firing, try to fire each gun @@ -318,7 +319,7 @@ impl SystemSim { // Remove ships that don't exist for c in to_remove { - self.remove_ship(c); + self.remove_ship(res, c); } // Create projectiles @@ -364,11 +365,11 @@ impl SystemSim { .build(), }; - let rigid_body = self.wrapper.rigid_body_set.insert(rigid_body); - let collider = self.wrapper.collider_set.insert_with_parent( + let rigid_body = self.rigid_body_set.insert(rigid_body); + let collider = self.collider_set.insert_with_parent( collider, rigid_body, - &mut self.wrapper.rigid_body_set, + &mut self.rigid_body_set, ); self.projectiles.insert( @@ -392,10 +393,11 @@ impl SystemSim { res.timing.start_physics_sim(); // Update physics - self.wrapper.step(res.t, &self.collision_handler); + res.wrapper + .step(res.t, &mut self.rigid_body_set, &mut self.collider_set); // Handle collision events - while let Ok(event) = &self.collision_queue.try_recv() { + while let Ok(event) = &res.wrapper.collision_queue.try_recv() { if event.started() { let a = event.collider1(); let b = event.collider2(); @@ -432,7 +434,7 @@ impl SystemSim { let mut rng = rand::thread_rng(); for c in to_remove { - let (pr, p) = self.remove_projectile(c).unwrap(); + let (pr, p) = self.remove_projectile(&mut res, c).unwrap(); match &p.content.expire_effect { None => {} @@ -475,19 +477,19 @@ impl SystemSim { /// Get a rigid body from a handle pub fn get_rigid_body(&self, r: RigidBodyHandle) -> Option<&RigidBody> { - self.wrapper.rigid_body_set.get(r) + self.rigid_body_set.get(r) } /// Get a rigid body from a handle pub fn get_rigid_body_mut(&mut self, r: RigidBodyHandle) -> Option<&mut RigidBody> { - self.wrapper.rigid_body_set.get_mut(r) + self.rigid_body_set.get_mut(r) } /// Iterate over all ships in this physics system - pub fn iter_ship_body(&self) -> impl Iterator + '_ { + pub fn iter_ship_body(&self) -> impl Iterator + '_ { self.ships .values() - .map(|x| (x, self.wrapper.rigid_body_set.get(x.rigid_body).unwrap())) + .map(|x| (x, self.rigid_body_set.get(x.rigid_body).unwrap())) } /// Iterate over all ships in this physics system diff --git a/crates/systemsim/src/wrapper.rs b/crates/systemsim/src/wrapper.rs index 386a62e..063070c 100644 --- a/crates/systemsim/src/wrapper.rs +++ b/crates/systemsim/src/wrapper.rs @@ -1,33 +1,39 @@ +use crossbeam::channel::Receiver; use rapier2d::{ dynamics::{ CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, RigidBodySet, }, - geometry::{BroadPhase, ColliderSet, NarrowPhase}, + geometry::{BroadPhase, ColliderSet, CollisionEvent, NarrowPhase}, na::vector, - pipeline::{EventHandler, PhysicsPipeline}, + pipeline::{ChannelEventCollector, PhysicsPipeline}, }; -pub(crate) struct Wrapper { - pub rigid_body_set: RigidBodySet, - pub collider_set: ColliderSet, +/// Wraps Rapier2d physics parameters +pub struct Wrapper { + pub(crate) ip: IntegrationParameters, + pub(crate) pp: PhysicsPipeline, + pub(crate) im: IslandManager, + pub(crate) bp: BroadPhase, + pub(crate) np: NarrowPhase, + pub(crate) ij: ImpulseJointSet, + pub(crate) mj: MultibodyJointSet, + pub(crate) 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, + collision_handler: ChannelEventCollector, + + /// Collision event queue + /// this should be emptied after every frame. + pub collision_queue: Receiver, } impl Wrapper { + /// Make a new physics wrapper pub fn new() -> Self { - Self { - rigid_body_set: RigidBodySet::new(), - collider_set: ColliderSet::new(), + let (collision_send, collision_queue) = crossbeam::channel::unbounded(); + let (contact_force_send, _) = crossbeam::channel::unbounded(); + Self { ip: IntegrationParameters::default(), pp: PhysicsPipeline::new(), im: IslandManager::new(), @@ -36,10 +42,18 @@ impl Wrapper { ij: ImpulseJointSet::new(), mj: MultibodyJointSet::new(), ccd: CCDSolver::new(), + collision_queue, + collision_handler: ChannelEventCollector::new(collision_send, contact_force_send), } } - pub fn step(&mut self, t: f32, handler: &dyn EventHandler) { + /// Step physics sim by `t` seconds + pub fn step( + &mut self, + t: f32, + rigid_body_set: &mut RigidBodySet, + collider_set: &mut ColliderSet, + ) { self.ip.dt = t; self.pp.step( &vector![0.0, 0.0], @@ -47,14 +61,14 @@ impl Wrapper { &mut self.im, &mut self.bp, &mut self.np, - &mut self.rigid_body_set, - &mut self.collider_set, + rigid_body_set, + collider_set, &mut self.ij, &mut self.mj, &mut self.ccd, None, &(), - handler, + &mut self.collision_handler, ); } }