Compare commits

..

No commits in common. "173263f3343824330792589d5c8bbc7dd0a9e0d7" and "876a95e546dcf2ebd34a5b238f2d4701e378038a" have entirely different histories.

24 changed files with 246 additions and 413 deletions

16
Cargo.lock generated
View File

@ -644,7 +644,6 @@ dependencies = [
"cgmath", "cgmath",
"galactica-content", "galactica-content",
"galactica-galaxy", "galactica-galaxy",
"galactica-playeragent",
"galactica-render", "galactica-render",
"galactica-systemsim", "galactica-systemsim",
"galactica-util", "galactica-util",
@ -688,20 +687,6 @@ dependencies = [
"walkdir", "walkdir",
] ]
[[package]]
name = "galactica-playeragent"
version = "0.0.0"
dependencies = [
"anyhow",
"cgmath",
"galactica-content",
"galactica-galaxy",
"galactica-util",
"pollster",
"wgpu",
"winit",
]
[[package]] [[package]]
name = "galactica-render" name = "galactica-render"
version = "0.0.0" version = "0.0.0"
@ -729,7 +714,6 @@ dependencies = [
"crossbeam", "crossbeam",
"galactica-content", "galactica-content",
"galactica-galaxy", "galactica-galaxy",
"galactica-playeragent",
"galactica-util", "galactica-util",
"nalgebra", "nalgebra",
"rand", "rand",

View File

@ -48,7 +48,6 @@ galactica-render = { path = "crates/render" }
galactica-systemsim = { path = "crates/systemsim" } galactica-systemsim = { path = "crates/systemsim" }
galactica-galaxy = { path = "crates/galaxy" } galactica-galaxy = { path = "crates/galaxy" }
galactica-packer = { path = "crates/packer" } galactica-packer = { path = "crates/packer" }
galactica-playeragent = { path = "crates/playeragent" }
galactica = { path = "crates/galactica" } galactica = { path = "crates/galactica" }
image = { version = "0.24", features = ["png"] } image = { version = "0.24", features = ["png"] }

View File

@ -26,7 +26,6 @@ galactica-render = { workspace = true }
galactica-util = { workspace = true } galactica-util = { workspace = true }
galactica-systemsim = { workspace = true } galactica-systemsim = { workspace = true }
galactica-galaxy = { workspace = true } galactica-galaxy = { workspace = true }
galactica-playeragent = { workspace = true }
winit = { workspace = true } winit = { workspace = true }
wgpu = { workspace = true } wgpu = { workspace = true }

View File

@ -12,13 +12,3 @@ pub struct Camera {
/// Aspect ratio of viewport (width / height) /// Aspect ratio of viewport (width / height)
pub aspect: f32, pub aspect: f32,
} }
impl Camera {
pub fn new() -> Self {
Self {
pos: (0.0, 0.0).into(),
zoom: 500.0,
aspect: 1.0,
}
}
}

View File

@ -1,112 +1,119 @@
use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle}; use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle};
use galactica_galaxy::{ship::ShipPersonality, Galaxy, GxShipHandle}; 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 galactica_util::timing::Timing;
use std::time::Instant; use std::time::Instant;
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
#[derive(Clone)] use crate::camera::Camera;
pub struct GameState { use crate::inputstatus::InputStatus;
pub gx: Galaxy, use galactica_render::RenderInput;
pub systemsim: SystemSim, use galactica_systemsim::{objects::ShipControls, util, ParticleBuilder, StepResources, SystemSim};
}
pub struct Game { pub struct Game {
// Core game data input: InputStatus,
ct: Content,
state_live: GameState,
state_static: GameState,
// Metadata
wrapper: Wrapper, // Physics computer
time_scale: f32,
last_update: Instant, last_update: Instant,
player: GxShipHandle,
paused: bool,
time_scale: f32,
start_instant: Instant, start_instant: Instant,
camera: Camera,
ct: Content,
gx: Galaxy,
systemsim: SystemSim,
new_particles: Vec<ParticleBuilder>, new_particles: Vec<ParticleBuilder>,
timing: Timing, timing: Timing,
} }
impl Game { impl Game {
pub fn new(ct: Content) -> (Self, GxShipHandle) { pub fn new(ct: Content) -> Self {
let mut gx = Galaxy::new(&ct); let mut galaxy = Galaxy::new(&ct);
let player = gx.create_ship( let player = galaxy.create_ship(
&ct, &ct,
ShipHandle { index: 0 }, ShipHandle { index: 0 },
FactionHandle { index: 0 }, FactionHandle { index: 0 },
ShipPersonality::Player, ShipPersonality::Player,
&SystemHandle { index: 0 }, &SystemHandle { index: 0 },
); );
let s = gx.get_ship_mut(player).unwrap(); let s = galaxy.get_ship_mut(player).unwrap();
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 })); 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: 1 }));
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 })); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 }));
let a = gx.create_ship( let a = galaxy.create_ship(
&ct, &ct,
ShipHandle { index: 0 }, ShipHandle { index: 0 },
FactionHandle { index: 1 }, FactionHandle { index: 1 },
ShipPersonality::Dummy, ShipPersonality::Dummy,
&SystemHandle { index: 0 }, &SystemHandle { index: 0 },
); );
let s = gx.get_ship_mut(a).unwrap(); let s = galaxy.get_ship_mut(a).unwrap();
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 })); 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: 1 }));
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 })); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 }));
let a = gx.create_ship( let a = galaxy.create_ship(
&ct, &ct,
ShipHandle { index: 0 }, ShipHandle { index: 0 },
FactionHandle { index: 0 }, FactionHandle { index: 0 },
ShipPersonality::Point, ShipPersonality::Point,
&SystemHandle { index: 0 }, &SystemHandle { index: 0 },
); );
let s = gx.get_ship_mut(a).unwrap(); let s = galaxy.get_ship_mut(a).unwrap();
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 })); 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: 1 }));
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 })); s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 }));
let systemsim = SystemSim::new(&ct, &gx, SystemHandle { index: 0 }); let physics = SystemSim::new(&ct, &galaxy, SystemHandle { index: 0 });
let state = GameState { gx, systemsim }; Game {
last_update: Instant::now(),
( input: InputStatus::new(),
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(),
},
player, player,
) start_instant: Instant::now(),
camera: Camera {
pos: (0.0, 0.0).into(),
zoom: 500.0,
aspect: 1.0,
},
//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(),
}
} }
pub fn update_player_controls(&mut self, player: &PlayerAgent) { pub fn set_camera_aspect(&mut self, v: f32) {
self.state_live.systemsim.update_player_controls(player) self.camera.aspect = v
} }
pub fn get_player_status(&self, player: &PlayerAgent) -> PlayerStatus { pub fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) {
let o = self self.input.process_key(state, key)
.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);
PlayerStatus { pos } 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;
}
} }
pub fn update(&mut self) { pub fn update(&mut self) {
@ -114,33 +121,50 @@ impl Game {
self.timing.start_frame(); self.timing.start_frame();
self.timing.start_galaxy(); self.timing.start_galaxy();
self.state_live.gx.step(t); self.gx.step(t);
self.timing.mark_galaxy(); self.timing.mark_galaxy();
self.state_live.systemsim.step(StepResources { 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,
},
ct: &self.ct, ct: &self.ct,
gx: &mut self.state_live.gx, gx: &mut self.gx,
particles: &mut self.new_particles, particles: &mut self.new_particles,
timing: &mut self.timing, timing: &mut self.timing,
wrapper: &mut self.wrapper,
t, 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.last_update = Instant::now();
self.state_static = self.state_live.clone();
self.timing.mark_frame(); self.timing.mark_frame();
} }
pub fn get_frame_state(&mut self, player: &PlayerAgent) -> RenderInput { pub fn get_frame_state(&mut self) -> RenderInput {
RenderInput { RenderInput {
camera_pos: player.camera.pos, camera_pos: self.camera.pos,
camera_zoom: player.camera.zoom, camera_zoom: self.camera.zoom,
current_time: self.start_instant.elapsed().as_secs_f32(), current_time: self.start_instant.elapsed().as_secs_f32(),
ct: &self.ct, ct: &self.ct,
systemsim: &self.state_static.systemsim, // TODO: system should be stored here, not in game systemsim: &self.systemsim, // TODO: maybe system should be stored here?
particles: &mut self.new_particles, particles: &mut self.new_particles,
player_data: player.ship.unwrap(), player_data: self.player,
gx: &self.state_static.gx, gx: &self.gx,
current_system: SystemHandle { index: 0 }, current_system: SystemHandle { index: 0 },
timing: &mut self.timing, timing: &mut self.timing,
} }

View File

@ -1,8 +1,9 @@
mod camera;
mod game; mod game;
mod inputstatus;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use galactica_content::Content; use galactica_content::Content;
use galactica_playeragent::PlayerAgent;
use galactica_util::constants::ASSET_CACHE; use galactica_util::constants::ASSET_CACHE;
use std::{ use std::{
fs, fs,
@ -36,26 +37,21 @@ fn main() -> Result<()> {
let mut gpu = pollster::block_on(galactica_render::GPUState::new(window, &content))?; let mut gpu = pollster::block_on(galactica_render::GPUState::new(window, &content))?;
gpu.init(&content); gpu.init(&content);
let (mut game, p) = game::Game::new(content); let mut game = game::Game::new(content);
game.set_camera_aspect(
let mut player = PlayerAgent::new(p);
player.set_camera_aspect(
gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32, gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32,
); );
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
match event { match event {
Event::RedrawRequested(window_id) if window_id == gpu.window().id() => { Event::RedrawRequested(window_id) if window_id == gpu.window().id() => {
match gpu.render(game.get_frame_state(&player)) { match gpu.render(game.get_frame_state()) {
Ok(_) => {} Ok(_) => {}
Err(wgpu::SurfaceError::Lost) => gpu.resize(game.get_content()), Err(wgpu::SurfaceError::Lost) => gpu.resize(game.get_content()),
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// All other errors (Outdated, Timeout) should be resolved by the next frame // All other errors (Outdated, Timeout) should be resolved by the next frame
Err(e) => eprintln!("{:?}", e), Err(e) => eprintln!("{:?}", e),
} }
let status = game.get_player_status(&player);
player.step(game.get_content(), status);
} }
Event::MainEventsCleared => { Event::MainEventsCleared => {
@ -67,8 +63,8 @@ fn main() -> Result<()> {
ref event, ref event,
window_id, window_id,
} if window_id == gpu.window().id() => match event { } if window_id == gpu.window().id() => match event {
WindowEvent::Focused(_state) => { WindowEvent::Focused(state) => {
//game.set_paused(!state); game.set_paused(!state);
} }
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
@ -79,28 +75,23 @@ fn main() -> Result<()> {
.. ..
}, },
.. ..
} => { } => game.process_key(state, key),
player.process_key(state, key);
game.update_player_controls(&player);
}
WindowEvent::MouseInput { state, button, .. } => { WindowEvent::MouseInput { state, button, .. } => {
player.process_click(state, button); game.process_click(state, button);
game.update_player_controls(&player);
} }
WindowEvent::MouseWheel { delta, phase, .. } => { WindowEvent::MouseWheel { delta, phase, .. } => {
player.process_scroll(delta, phase); game.process_scroll(delta, phase);
game.update_player_controls(&player);
} }
WindowEvent::Resized(_) => { WindowEvent::Resized(_) => {
gpu.resize(game.get_content()); gpu.resize(game.get_content());
player.set_camera_aspect( game.set_camera_aspect(
gpu.window().inner_size().width as f32 gpu.window().inner_size().width as f32
/ gpu.window().inner_size().height as f32, / gpu.window().inner_size().height as f32,
); );
} }
WindowEvent::ScaleFactorChanged { .. } => { WindowEvent::ScaleFactorChanged { .. } => {
gpu.resize(game.get_content()); gpu.resize(game.get_content());
player.set_camera_aspect( game.set_camera_aspect(
gpu.window().inner_size().width as f32 gpu.window().inner_size().width as f32
/ gpu.window().inner_size().height as f32, / gpu.window().inner_size().height as f32,
); );

View File

@ -5,7 +5,7 @@ use galactica_content::{Content, FactionHandle, ShipHandle, SystemHandle};
/// Keeps track of all objects in the galaxy. /// Keeps track of all objects in the galaxy.
/// This struct does NO physics, it keeps track of data exclusively. /// This struct does NO physics, it keeps track of data exclusively.
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct Galaxy { pub struct Galaxy {
/// Universal counter. /// Universal counter.
/// Used to create unique handles for game objects. /// Used to create unique handles for game objects.

View File

@ -6,7 +6,7 @@ use super::{OutfitSet, ShipPersonality};
use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle}; use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle};
use rand::{rngs::ThreadRng, Rng}; use rand::{rngs::ThreadRng, Rng};
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct GxShip { pub struct GxShip {
// Metadata values // Metadata values
handle: GxShipHandle, handle: GxShipHandle,

View File

@ -1,28 +0,0 @@
[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 }

View File

@ -1,9 +0,0 @@
mod camera;
mod inputstatus;
mod playeragent;
mod playerstatus;
pub use camera::Camera;
pub use inputstatus::InputStatus;
pub use playeragent::PlayerAgent;
pub use playerstatus::PlayerStatus;

View File

@ -1,56 +0,0 @@
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<GxShipHandle>,
/// 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;
}
}

View File

@ -1,5 +0,0 @@
use cgmath::Point2;
pub struct PlayerStatus {
pub pos: Point2<f32>,
}

View File

@ -20,7 +20,6 @@ workspace = true
galactica-content = { workspace = true } galactica-content = { workspace = true }
galactica-galaxy = { workspace = true } galactica-galaxy = { workspace = true }
galactica-util = { workspace = true } galactica-util = { workspace = true }
galactica-playeragent = { workspace = true }
rapier2d = { workspace = true } rapier2d = { workspace = true }
nalgebra = { workspace = true } nalgebra = { workspace = true }

View File

@ -3,64 +3,24 @@
mod null; mod null;
mod point; mod point;
use null::*; pub use null::*;
use point::PointShipController; pub use point::Point;
use galactica_galaxy::GxShipHandle; use galactica_galaxy::GxShipHandle;
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet}; use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
use std::{collections::HashMap, fmt::Debug}; use std::collections::HashMap;
use crate::{ use crate::{
objects::{ShipControls, SySimShip}, objects::{ShipControls, SySimShip},
StepResources, StepResources,
}; };
/// Represents a ship controller
#[derive(Debug, Clone)]
pub enum ShipController {
/// Null controller
Null(NullShipController),
/// Point controller
Point(PointShipController),
}
impl ShipController {
/// Make a new null controller
pub fn new_null() -> Self {
Self::Null(NullShipController::new())
}
/// Make a new point controller
pub fn new_point() -> Self {
Self::Point(PointShipController::new())
}
/// Compute new ship controls from world state
pub fn update_controls(
&mut self,
res: &StepResources,
rigid_bodies: &RigidBodySet,
ships: &HashMap<GxShipHandle, SySimShip>,
this_ship: RigidBodyHandle,
this_data: GxShipHandle,
) -> Option<ShipControls> {
match self {
Self::Null(n) => n.update_controls(res, rigid_bodies, ships, this_ship, this_data),
Self::Point(p) => p.update_controls(res, rigid_bodies, ships, this_ship, this_data),
}
}
}
/// Ship controller trait. Any struct that implements this /// Ship controller trait. Any struct that implements this
/// may be used to control a ship. /// may be used to control a ship.
pub trait ShipControllerStruct pub trait ShipController {
where
Self: Debug + Clone,
{
/// Update a ship's controls based on system state. /// Update a ship's controls based on system state.
/// This method returns the ship's new control values, /// This method does not return anything, it modifies
/// or None if no change is to be made. /// the ship's controls in-place.
fn update_controls( fn update_controls(
&mut self, &mut self,
res: &StepResources, res: &StepResources,
@ -68,5 +28,5 @@ where
ships: &HashMap<GxShipHandle, SySimShip>, ships: &HashMap<GxShipHandle, SySimShip>,
this_ship: RigidBodyHandle, this_ship: RigidBodyHandle,
this_data: GxShipHandle, this_data: GxShipHandle,
) -> Option<ShipControls>; ) -> ShipControls;
} }

View File

@ -2,7 +2,7 @@ use galactica_galaxy::GxShipHandle;
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet}; use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
use std::collections::HashMap; use std::collections::HashMap;
use super::ShipControllerStruct; use super::ShipController;
use crate::{ use crate::{
objects::{ShipControls, SySimShip}, objects::{ShipControls, SySimShip},
StepResources, StepResources,
@ -10,17 +10,16 @@ use crate::{
/// The Null controller is assigned to objects that are static or not controlled by the computer. /// The Null controller is assigned to objects that are static or not controlled by the computer.
/// Most notably, the player's ship has a Null controller. /// Most notably, the player's ship has a Null controller.
#[derive(Debug, Clone)] pub struct Null {}
pub struct NullShipController {}
impl NullShipController { impl Null {
/// Create a new ship controller /// Create a new ship controller
pub fn new() -> Self { pub fn new() -> Self {
Self {} Self {}
} }
} }
impl ShipControllerStruct for NullShipController { impl ShipController for Null {
fn update_controls( fn update_controls(
&mut self, &mut self,
_res: &StepResources, _res: &StepResources,
@ -28,7 +27,7 @@ impl ShipControllerStruct for NullShipController {
_ships: &HashMap<GxShipHandle, SySimShip>, _ships: &HashMap<GxShipHandle, SySimShip>,
_this_ship: RigidBodyHandle, _this_ship: RigidBodyHandle,
_this_data: GxShipHandle, _this_data: GxShipHandle,
) -> Option<ShipControls> { ) -> ShipControls {
None ShipControls::new()
} }
} }

View File

@ -4,7 +4,7 @@ use galactica_galaxy::GxShipHandle;
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet}; use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
use std::collections::HashMap; use std::collections::HashMap;
use super::ShipControllerStruct; use super::ShipController;
use crate::{ use crate::{
objects::{ShipControls, SySimShip}, objects::{ShipControls, SySimShip},
util, StepResources, util, StepResources,
@ -12,17 +12,16 @@ use crate::{
/// "Point" ship controller. /// "Point" ship controller.
/// Point and shoot towards the nearest enemy. /// Point and shoot towards the nearest enemy.
#[derive(Debug, Clone)] pub struct Point {}
pub struct PointShipController {}
impl PointShipController { impl Point {
/// Create a new ship controller /// Create a new ship controller
pub fn new() -> Self { pub fn new() -> Self {
Self {} Self {}
} }
} }
impl ShipControllerStruct for PointShipController { impl ShipController for Point {
fn update_controls( fn update_controls(
&mut self, &mut self,
res: &StepResources, res: &StepResources,
@ -30,7 +29,7 @@ impl ShipControllerStruct for PointShipController {
ships: &HashMap<GxShipHandle, SySimShip>, ships: &HashMap<GxShipHandle, SySimShip>,
this_ship: RigidBodyHandle, this_ship: RigidBodyHandle,
this_data: GxShipHandle, this_data: GxShipHandle,
) -> Option<ShipControls> { ) -> ShipControls {
let mut controls = ShipControls::new(); let mut controls = ShipControls::new();
let this_rigidbody = rigid_bodies.get(this_ship).unwrap(); let this_rigidbody = rigid_bodies.get(this_ship).unwrap();
@ -61,7 +60,7 @@ impl ShipControllerStruct for PointShipController {
// Find the closest target // Find the closest target
let mut closest_enemy_position = match hostile_ships.next() { let mut closest_enemy_position = match hostile_ships.next() {
Some(c) => util::rigidbody_position(c), Some(c) => util::rigidbody_position(c),
None => return Some(controls), // Do nothing if no targets are available None => return controls, // Do nothing if no targets are available
}; };
let mut d = (my_position - closest_enemy_position).magnitude(); let mut d = (my_position - closest_enemy_position).magnitude();
@ -85,6 +84,6 @@ impl ShipControllerStruct for PointShipController {
} }
controls.guns = true; controls.guns = true;
return Some(controls); return controls;
} }
} }

View File

@ -13,4 +13,3 @@ mod wrapper;
pub use particlebuilder::*; pub use particlebuilder::*;
pub use stepresources::*; pub use stepresources::*;
pub use systemsim::SystemSim; pub use systemsim::SystemSim;
pub use wrapper::Wrapper;

View File

@ -6,7 +6,7 @@ use rapier2d::{dynamics::RigidBody, geometry::Collider};
use crate::{util, ParticleBuilder, StepResources}; use crate::{util, ParticleBuilder, StepResources};
#[derive(Debug, Clone)] #[derive(Debug)]
pub(super) struct ShipCollapseSequence { pub(super) struct ShipCollapseSequence {
total_length: f32, total_length: f32,
time_elapsed: f32, time_elapsed: f32,

View File

@ -3,7 +3,7 @@ use rand::Rng;
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle}; use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
/// A single projectile in this sim /// A single projectile in this sim
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct SySimProjectile { pub struct SySimProjectile {
/// This projectile's game data /// This projectile's game data
pub content: Projectile, pub content: Projectile,

View File

@ -41,7 +41,7 @@ impl ShipControls {
} }
/// A ship instance in the physics system /// A ship instance in the physics system
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct SySimShip { pub struct SySimShip {
/// This ship's physics handle /// This ship's physics handle
pub rigid_body: RigidBodyHandle, pub rigid_body: RigidBodyHandle,

View File

@ -1,9 +1,10 @@
use crate::{wrapper::Wrapper, ParticleBuilder}; use crate::{objects::ShipControls, ParticleBuilder};
use galactica_content::Content; use galactica_content::Content;
use galactica_galaxy::Galaxy; use galactica_galaxy::{Galaxy, GxShipHandle};
use galactica_util::timing::Timing; use galactica_util::timing::Timing;
/// External resources we need to compute time steps /// External resources we need to compute time steps
#[derive(Debug)]
pub struct StepResources<'a> { pub struct StepResources<'a> {
/// Game content /// Game content
pub ct: &'a Content, pub ct: &'a Content,
@ -17,9 +18,12 @@ pub struct StepResources<'a> {
/// Particles to create /// Particles to create
pub particles: &'a mut Vec<ParticleBuilder>, pub particles: &'a mut Vec<ParticleBuilder>,
/// Player inputs
pub player_controls: ShipControls,
/// The ship that the player controls
pub player: GxShipHandle,
/// Record detailed frame timing breakdown /// Record detailed frame timing breakdown
pub timing: &'a mut Timing, pub timing: &'a mut Timing,
/// Physics computer
pub wrapper: &'a mut Wrapper,
} }

View File

@ -1,63 +1,58 @@
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero}; use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero};
use crossbeam::channel::Receiver;
use galactica_content::{ use galactica_content::{
Content, GunPoint, OutfitHandle, ProjectileCollider, Relationship, SystemHandle, Content, GunPoint, OutfitHandle, ProjectileCollider, Relationship, SystemHandle,
}; };
use galactica_galaxy::{ use galactica_galaxy::{ship::GxShip, Galaxy, GxShipHandle};
ship::{GxShip, ShipPersonality},
Galaxy, GxShipHandle,
};
use galactica_playeragent::PlayerAgent;
use nalgebra::{point, vector}; use nalgebra::{point, vector};
use rand::Rng; use rand::Rng;
use rapier2d::{ use rapier2d::{
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle, RigidBodySet}, dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
geometry::{ColliderBuilder, ColliderHandle, ColliderSet}, geometry::{ColliderBuilder, ColliderHandle, CollisionEvent},
pipeline::ActiveEvents, pipeline::{ActiveEvents, ChannelEventCollector},
}; };
use std::{collections::HashMap, f32::consts::PI}; use std::{collections::HashMap, f32::consts::PI};
use crate::{ use crate::{
controller::ShipController, controller::{self, ShipController},
objects,
objects::{SySimProjectile, SySimShip}, objects::{SySimProjectile, SySimShip},
util, ParticleBuilder, StepResources, util,
wrapper::Wrapper,
ParticleBuilder, StepResources,
}; };
/// Manages the physics state of one system /// Manages the physics state of one system
#[derive(Clone)]
pub struct SystemSim { pub struct SystemSim {
/// The system this sim is attached to /// The system this sim is attached to
_system: SystemHandle, _system: SystemHandle,
rigid_body_set: RigidBodySet, wrapper: Wrapper,
collider_set: ColliderSet, projectiles: HashMap<ColliderHandle, objects::SySimProjectile>,
ships: HashMap<GxShipHandle, objects::SySimShip>,
projectiles: HashMap<ColliderHandle, SySimProjectile>, ship_behaviors: HashMap<GxShipHandle, Box<dyn ShipController>>,
ships: HashMap<GxShipHandle, SySimShip>,
// TODO: these don't need to be cloned each frame
ship_behaviors: HashMap<GxShipHandle, ShipController>,
collider_ship_table: HashMap<ColliderHandle, GxShipHandle>, collider_ship_table: HashMap<ColliderHandle, GxShipHandle>,
collision_handler: ChannelEventCollector,
collision_queue: Receiver<CollisionEvent>,
} }
// Private methods // Private methods
impl<'a> SystemSim { impl<'a> SystemSim {
fn remove_projectile( fn remove_projectile(&mut self, c: ColliderHandle) -> Option<(RigidBody, SySimProjectile)> {
&mut self,
res: &mut StepResources,
c: ColliderHandle,
) -> Option<(RigidBody, SySimProjectile)> {
let p = match self.projectiles.remove(&c) { let p = match self.projectiles.remove(&c) {
Some(p) => p, Some(p) => p,
None => return None, None => return None,
}; };
let r = self let r = self
.wrapper
.rigid_body_set .rigid_body_set
.remove( .remove(
p.rigid_body, p.rigid_body,
&mut res.wrapper.im, &mut self.wrapper.im,
&mut self.collider_set, &mut self.wrapper.collider_set,
&mut res.wrapper.ij, &mut self.wrapper.ij,
&mut res.wrapper.mj, &mut self.wrapper.mj,
true, true,
) )
.unwrap(); .unwrap();
@ -65,7 +60,7 @@ impl<'a> SystemSim {
return Some((r, p)); return Some((r, p));
} }
fn remove_ship(&mut self, res: &mut StepResources, s: ColliderHandle) { fn remove_ship(&mut self, s: ColliderHandle) {
let s = match self.collider_ship_table.get(&s) { let s = match self.collider_ship_table.get(&s) {
None => return, None => return,
Some(s) => match self.ships.get(s) { Some(s) => match self.ships.get(s) {
@ -74,12 +69,12 @@ impl<'a> SystemSim {
}, },
}; };
self.rigid_body_set.remove( self.wrapper.rigid_body_set.remove(
s.rigid_body, s.rigid_body,
&mut res.wrapper.im, &mut self.wrapper.im,
&mut self.collider_set, &mut self.wrapper.collider_set,
&mut res.wrapper.ij, &mut self.wrapper.ij,
&mut res.wrapper.mj, &mut self.wrapper.mj,
true, true,
); );
let h = self.collider_ship_table.remove(&s.collider).unwrap(); let h = self.collider_ship_table.remove(&s.collider).unwrap();
@ -117,16 +112,28 @@ impl<'a> SystemSim {
}; };
if destory_projectile { if destory_projectile {
let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap(); let pr = self
.wrapper
.rigid_body_set
.get(projectile.rigid_body)
.unwrap();
let v = util::rigidbody_velocity(pr).normalize() * projectile.content.force; let v = util::rigidbody_velocity(pr).normalize() * projectile.content.force;
let pos = util::rigidbody_position(pr); let pos = util::rigidbody_position(pr);
let _ = pr; let _ = pr;
let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap(); let r = self
.wrapper
.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); 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 // Borrow again, we can only have one at a time
let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap(); let pr = self
.wrapper
.rigid_body_set
.get(projectile.rigid_body)
.unwrap();
let pos = util::rigidbody_position(pr); let pos = util::rigidbody_position(pr);
let angle = util::rigidbody_rotation(pr).angle(Vector2 { x: 1.0, y: 0.0 }); let angle = util::rigidbody_rotation(pr).angle(Vector2 { x: 1.0, y: 0.0 });
@ -153,7 +160,7 @@ impl<'a> SystemSim {
} }
}; };
self.remove_projectile(res, projectile.collider); self.remove_projectile(projectile.collider);
} }
} }
} }
@ -162,14 +169,18 @@ impl<'a> SystemSim {
impl SystemSim { impl SystemSim {
/// Create a new physics system /// Create a new physics system
pub fn new(ct: &Content, gx: &Galaxy, system: SystemHandle) -> Self { 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 { let mut w = Self {
_system: system, _system: system,
rigid_body_set: RigidBodySet::new(), wrapper: Wrapper::new(),
collider_set: ColliderSet::new(),
projectiles: HashMap::new(), projectiles: HashMap::new(),
ships: HashMap::new(), ships: HashMap::new(),
ship_behaviors: HashMap::new(), ship_behaviors: HashMap::new(),
collider_ship_table: HashMap::new(), collider_ship_table: HashMap::new(),
collision_handler: ChannelEventCollector::new(collision_send, contact_force_send),
collision_queue,
}; };
// TODO: guarantee not touching // TODO: guarantee not touching
@ -209,36 +220,22 @@ impl SystemSim {
.translation(vector![position.x, position.y]) .translation(vector![position.x, position.y])
.can_sleep(false); .can_sleep(false);
let r = self.rigid_body_set.insert(rb.build()); let r = self.wrapper.rigid_body_set.insert(rb.build());
let c = self let c = self.wrapper.collider_set.insert_with_parent(
.collider_set cl.build(),
.insert_with_parent(cl.build(), r, &mut self.rigid_body_set); r,
&mut self.wrapper.rigid_body_set,
);
self.collider_ship_table.insert(c, ship.get_handle()); self.collider_ship_table.insert(c, ship.get_handle());
self.ship_behaviors
self.ship_behaviors.insert( .insert(ship.get_handle(), Box::new(controller::Point::new()));
ship.get_handle(),
match ship.get_personality() {
ShipPersonality::Dummy | ShipPersonality::Player => ShipController::new_null(),
ShipPersonality::Point => ShipController::new_point(),
},
);
self.ships.insert( self.ships.insert(
ship.get_handle(), ship.get_handle(),
SySimShip::new(ct, ship.get_handle(), ship.get_faction(), r, c), objects::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: /// Run ship updates:
/// - updates ship controls (runs behaviors) /// - updates ship controls (runs behaviors)
/// - applies ship controls /// - applies ship controls
@ -264,32 +261,34 @@ impl SystemSim {
let ship_object = self.ships.get_mut(handle).unwrap(); let ship_object = self.ships.get_mut(handle).unwrap();
ship_object.step( ship_object.step(
res, res,
&mut self.rigid_body_set[ship_object.rigid_body], &mut self.wrapper.rigid_body_set[ship_object.rigid_body],
&mut self.collider_set[ship_object.collider], &mut self.wrapper.collider_set[ship_object.collider],
); );
continue; continue;
} }
// Compute new controls // Compute new controls
let controls; let controls;
let b = self.ship_behaviors.get_mut(handle).unwrap(); if ship_object.data_handle == res.player {
controls = b.update_controls( controls = res.player_controls.clone();
&res, } else {
&self.rigid_body_set, let b = self.ship_behaviors.get_mut(handle).unwrap();
&self.ships, controls = b.update_controls(
ship_object.rigid_body, &res,
*handle, &self.wrapper.rigid_body_set,
); &self.ships,
ship_object.rigid_body,
*handle,
);
}
// Now re-borrow mutably to apply changes // Now re-borrow mutably to apply changes
let ship_object = self.ships.get_mut(handle).unwrap(); let ship_object = self.ships.get_mut(handle).unwrap();
if let Some(controls) = controls { ship_object.controls = controls;
ship_object.controls = controls;
}
ship_object.step( ship_object.step(
res, res,
&mut self.rigid_body_set[ship_object.rigid_body], &mut self.wrapper.rigid_body_set[ship_object.rigid_body],
&mut self.collider_set[ship_object.collider], &mut self.wrapper.collider_set[ship_object.collider],
); );
// If we're firing, try to fire each gun // If we're firing, try to fire each gun
@ -319,7 +318,7 @@ impl SystemSim {
// Remove ships that don't exist // Remove ships that don't exist
for c in to_remove { for c in to_remove {
self.remove_ship(res, c); self.remove_ship(c);
} }
// Create projectiles // Create projectiles
@ -365,11 +364,11 @@ impl SystemSim {
.build(), .build(),
}; };
let rigid_body = self.rigid_body_set.insert(rigid_body); let rigid_body = self.wrapper.rigid_body_set.insert(rigid_body);
let collider = self.collider_set.insert_with_parent( let collider = self.wrapper.collider_set.insert_with_parent(
collider, collider,
rigid_body, rigid_body,
&mut self.rigid_body_set, &mut self.wrapper.rigid_body_set,
); );
self.projectiles.insert( self.projectiles.insert(
@ -393,11 +392,10 @@ impl SystemSim {
res.timing.start_physics_sim(); res.timing.start_physics_sim();
// Update physics // Update physics
res.wrapper self.wrapper.step(res.t, &self.collision_handler);
.step(res.t, &mut self.rigid_body_set, &mut self.collider_set);
// Handle collision events // Handle collision events
while let Ok(event) = &res.wrapper.collision_queue.try_recv() { while let Ok(event) = &self.collision_queue.try_recv() {
if event.started() { if event.started() {
let a = event.collider1(); let a = event.collider1();
let b = event.collider2(); let b = event.collider2();
@ -434,7 +432,7 @@ impl SystemSim {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
for c in to_remove { for c in to_remove {
let (pr, p) = self.remove_projectile(&mut res, c).unwrap(); let (pr, p) = self.remove_projectile(c).unwrap();
match &p.content.expire_effect { match &p.content.expire_effect {
None => {} None => {}
@ -477,19 +475,19 @@ impl SystemSim {
/// Get a rigid body from a handle /// Get a rigid body from a handle
pub fn get_rigid_body(&self, r: RigidBodyHandle) -> Option<&RigidBody> { pub fn get_rigid_body(&self, r: RigidBodyHandle) -> Option<&RigidBody> {
self.rigid_body_set.get(r) self.wrapper.rigid_body_set.get(r)
} }
/// Get a rigid body from a handle /// Get a rigid body from a handle
pub fn get_rigid_body_mut(&mut self, r: RigidBodyHandle) -> Option<&mut RigidBody> { pub fn get_rigid_body_mut(&mut self, r: RigidBodyHandle) -> Option<&mut RigidBody> {
self.rigid_body_set.get_mut(r) self.wrapper.rigid_body_set.get_mut(r)
} }
/// Iterate over all ships in this physics system /// Iterate over all ships in this physics system
pub fn iter_ship_body(&self) -> impl Iterator<Item = (&SySimShip, &RigidBody)> + '_ { pub fn iter_ship_body(&self) -> impl Iterator<Item = (&objects::SySimShip, &RigidBody)> + '_ {
self.ships self.ships
.values() .values()
.map(|x| (x, self.rigid_body_set.get(x.rigid_body).unwrap())) .map(|x| (x, self.wrapper.rigid_body_set.get(x.rigid_body).unwrap()))
} }
/// Iterate over all ships in this physics system /// Iterate over all ships in this physics system

View File

@ -1,39 +1,33 @@
use crossbeam::channel::Receiver;
use rapier2d::{ use rapier2d::{
dynamics::{ dynamics::{
CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet,
RigidBodySet, RigidBodySet,
}, },
geometry::{BroadPhase, ColliderSet, CollisionEvent, NarrowPhase}, geometry::{BroadPhase, ColliderSet, NarrowPhase},
na::vector, na::vector,
pipeline::{ChannelEventCollector, PhysicsPipeline}, pipeline::{EventHandler, PhysicsPipeline},
}; };
/// Wraps Rapier2d physics parameters pub(crate) struct Wrapper {
pub struct Wrapper { pub rigid_body_set: RigidBodySet,
pub(crate) ip: IntegrationParameters, pub collider_set: ColliderSet,
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,
collision_handler: ChannelEventCollector, pub ip: IntegrationParameters,
pub pp: PhysicsPipeline,
/// Collision event queue pub im: IslandManager,
/// this should be emptied after every frame. pub bp: BroadPhase,
pub collision_queue: Receiver<CollisionEvent>, pub np: NarrowPhase,
pub ij: ImpulseJointSet,
pub mj: MultibodyJointSet,
pub ccd: CCDSolver,
} }
impl Wrapper { impl Wrapper {
/// Make a new physics wrapper
pub fn new() -> Self { pub fn new() -> Self {
let (collision_send, collision_queue) = crossbeam::channel::unbounded();
let (contact_force_send, _) = crossbeam::channel::unbounded();
Self { Self {
rigid_body_set: RigidBodySet::new(),
collider_set: ColliderSet::new(),
ip: IntegrationParameters::default(), ip: IntegrationParameters::default(),
pp: PhysicsPipeline::new(), pp: PhysicsPipeline::new(),
im: IslandManager::new(), im: IslandManager::new(),
@ -42,18 +36,10 @@ impl Wrapper {
ij: ImpulseJointSet::new(), ij: ImpulseJointSet::new(),
mj: MultibodyJointSet::new(), mj: MultibodyJointSet::new(),
ccd: CCDSolver::new(), ccd: CCDSolver::new(),
collision_queue,
collision_handler: ChannelEventCollector::new(collision_send, contact_force_send),
} }
} }
/// Step physics sim by `t` seconds pub fn step(&mut self, t: f32, handler: &dyn EventHandler) {
pub fn step(
&mut self,
t: f32,
rigid_body_set: &mut RigidBodySet,
collider_set: &mut ColliderSet,
) {
self.ip.dt = t; self.ip.dt = t;
self.pp.step( self.pp.step(
&vector![0.0, 0.0], &vector![0.0, 0.0],
@ -61,14 +47,14 @@ impl Wrapper {
&mut self.im, &mut self.im,
&mut self.bp, &mut self.bp,
&mut self.np, &mut self.np,
rigid_body_set, &mut self.rigid_body_set,
collider_set, &mut self.collider_set,
&mut self.ij, &mut self.ij,
&mut self.mj, &mut self.mj,
&mut self.ccd, &mut self.ccd,
None, None,
&(), &(),
&mut self.collision_handler, handler,
); );
} }
} }