From 7b1e14defd233ba72bb5ee697d4945ff5840e68a Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 11 Jan 2024 20:21:07 -0800 Subject: [PATCH] Minor reorganization --- crates/content/src/lib.rs | 2 +- crates/content/src/part/config.rs | 2 +- crates/content/src/part/system.rs | 4 +- crates/galactica/src/game.rs | 128 ++++++++++--------------- crates/galactica/src/main.rs | 62 +++++++++--- crates/playeragent/src/playeragent.rs | 4 +- crates/playeragent/src/playerstatus.rs | 2 +- crates/render/src/datastructs.rs | 2 +- crates/render/src/gpustate/render.rs | 3 - crates/render/src/ui/fpsindicator.rs | 12 +-- crates/render/src/ui/radar.rs | 30 +++--- crates/render/src/ui/status.rs | 24 +++-- crates/systemsim/src/systemsim.rs | 15 ++- crates/util/src/timing.rs | 47 +++------ 14 files changed, 177 insertions(+), 160 deletions(-) diff --git a/crates/content/src/lib.rs b/crates/content/src/lib.rs index 33a5fe8..a1b526e 100644 --- a/crates/content/src/lib.rs +++ b/crates/content/src/lib.rs @@ -134,7 +134,7 @@ impl ContentBuildContext { } /// Represents static game content -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Content { /// Sprites pub sprites: Vec, diff --git a/crates/content/src/part/config.rs b/crates/content/src/part/config.rs index dc035a8..06b4b9d 100644 --- a/crates/content/src/part/config.rs +++ b/crates/content/src/part/config.rs @@ -27,7 +27,7 @@ pub(crate) mod syntax { } let starfield_density = 0.01; - let starfield_size = self.starfield.min_dist * self.zoom_max; + let starfield_size = self.starfield.max_dist * self.zoom_max; let starfield_count = (starfield_size * starfield_density) as i32; // 12, because that should be enough to tile any screen. diff --git a/crates/content/src/part/system.rs b/crates/content/src/part/system.rs index 7cf3c86..d210be8 100644 --- a/crates/content/src/part/system.rs +++ b/crates/content/src/part/system.rs @@ -78,7 +78,7 @@ pub(crate) mod syntax { // These are exported. /// Represents a star system -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct System { /// This star system's name pub name: String, @@ -91,7 +91,7 @@ pub struct System { /// (A star, planet, moon, satellite, etc) /// These may be landable and may be decorative. /// System objects to not interact with the physics engine. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Object { /// This object's sprite pub sprite: SpriteHandle, diff --git a/crates/galactica/src/game.rs b/crates/galactica/src/game.rs index 9f6f154..5637ec0 100644 --- a/crates/galactica/src/game.rs +++ b/crates/galactica/src/game.rs @@ -1,8 +1,8 @@ +use cgmath::Point2; 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_playeragent::PlayerAgent; +use galactica_systemsim::{ParticleBuilder, StepResources, SystemSim, Wrapper}; use galactica_util::timing::Timing; use std::time::Instant; @@ -10,38 +10,50 @@ use std::time::Instant; pub struct GameState { pub gx: Galaxy, pub systemsim: SystemSim, + pub timing: Timing, + pub start_instant: Instant, } +unsafe impl Send for GameState {} +unsafe impl Sync for GameState {} + pub struct Game { // Core game data ct: Content, - state_live: GameState, - state_static: GameState, + state: 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, GxShipHandle) { - let mut gx = Galaxy::new(&ct); +unsafe impl<'a> Send for Game {} - let player = gx.create_ship( - &ct, +impl<'a> Game { + pub fn make_player(&mut self) -> GxShipHandle { + let player = self.state.gx.create_ship( + &self.ct, ShipHandle { index: 0 }, FactionHandle { index: 0 }, ShipPersonality::Player, &SystemHandle { index: 0 }, ); - 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 s = self.state.gx.get_ship_mut(player).unwrap(); + s.add_outfit(&self.ct.get_outfit(OutfitHandle { index: 0 })); + s.add_outfit(&self.ct.get_outfit(OutfitHandle { index: 1 })); + s.add_outfit(&self.ct.get_outfit(OutfitHandle { index: 2 })); + + self.state + .systemsim + .add_ship(&self.ct, &s, Point2 { x: 0.0, y: 0.0 }); + + return player; + } + + pub fn new(ct: Content) -> Self { + let mut gx = Galaxy::new(&ct); let a = gx.create_ship( &ct, @@ -69,84 +81,50 @@ impl Game { let systemsim = SystemSim::new(&ct, &gx, SystemHandle { index: 0 }); - let state = GameState { gx, systemsim }; + let state = GameState { + gx, + systemsim, + timing: Timing::new(), + start_instant: Instant::now(), + }; - ( - Game { - ct, - state_live: state.clone(), - state_static: state, + Game { + ct, + state, - wrapper: Wrapper::new(), - - last_update: Instant::now(), - start_instant: Instant::now(), - time_scale: 1.0, - new_particles: Vec::new(), - timing: Timing::new(), - }, - player, - ) + wrapper: Wrapper::new(), + last_update: Instant::now(), + time_scale: 1.0, + new_particles: Vec::new(), + } } pub fn update_player_controls(&mut self, player: &PlayerAgent) { - self.state_live.systemsim.update_player_controls(player) + self.state.systemsim.update_player_controls(player) } - 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); - - PlayerStatus { pos } + pub fn get_state(&self) -> &GameState { + &self.state } pub fn update(&mut self) { + self.state.timing.start_frame(); let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale; - self.timing.start_frame(); - self.timing.start_galaxy(); - self.state_live.gx.step(t); - self.timing.mark_galaxy(); + self.state.timing.start_galaxy(); + self.state.gx.step(t); + self.state.timing.mark_galaxy(); - self.state_live.systemsim.step(StepResources { + self.state.systemsim.step(StepResources { ct: &self.ct, - gx: &mut self.state_live.gx, + gx: &mut self.state.gx, particles: &mut self.new_particles, - timing: &mut self.timing, + timing: &mut self.state.timing, wrapper: &mut self.wrapper, t, }); self.last_update = Instant::now(); - self.state_static = self.state_live.clone(); - self.timing.mark_frame(); - } - - pub fn get_frame_state(&mut self, player: &PlayerAgent) -> RenderInput { - RenderInput { - camera_pos: player.camera.pos, - camera_zoom: player.camera.zoom, - current_time: self.start_instant.elapsed().as_secs_f32(), - ct: &self.ct, - systemsim: &self.state_static.systemsim, // TODO: system should be stored here, not in game - particles: &mut self.new_particles, - player_data: player.ship.unwrap(), - gx: &self.state_static.gx, - current_system: SystemHandle { index: 0 }, - timing: &mut self.timing, - } - } - - pub fn get_content(&self) -> &Content { - &self.ct + self.state.timing.mark_frame(); } } diff --git a/crates/galactica/src/main.rs b/crates/galactica/src/main.rs index 342f9c4..f0d974f 100644 --- a/crates/galactica/src/main.rs +++ b/crates/galactica/src/main.rs @@ -1,8 +1,10 @@ mod game; use anyhow::{bail, Result}; -use galactica_content::Content; -use galactica_playeragent::PlayerAgent; +use galactica_content::{Content, SystemHandle}; +use galactica_playeragent::{PlayerAgent, PlayerStatus}; +use galactica_render::RenderInput; +use galactica_systemsim::util; use galactica_util::constants::ASSET_CACHE; use std::{ fs, @@ -28,7 +30,7 @@ fn main() -> Result<()> { let content = Content::load_dir( PathBuf::from("./content"), PathBuf::from("./assets"), - atlas_index, + atlas_index.clone(), )?; let event_loop = EventLoop::new(); @@ -36,10 +38,10 @@ fn main() -> Result<()> { let mut gpu = pollster::block_on(galactica_render::GPUState::new(window, &content))?; gpu.init(&content); - let (mut game, p) = game::Game::new(content); + let mut game = game::Game::new(content.clone()); + let p = game.make_player(); let mut player = PlayerAgent::new(p); - player.set_camera_aspect( gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32, ); @@ -47,19 +49,54 @@ fn main() -> Result<()> { 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(&player)) { + let render_input = RenderInput { + camera_pos: player.camera.pos, + camera_zoom: player.camera.zoom, + current_time: game.get_state().start_instant.elapsed().as_secs_f32(), + ct: &content, + systemsim: &game.get_state().systemsim, + particles: &mut Vec::new(), + //particles: &mut self.new_particles, + player_data: player.ship.unwrap(), + gx: &game.get_state().gx, + current_system: SystemHandle { index: 0 }, + timing: game.get_state().timing.clone(), + }; + + match gpu.render(render_input) { Ok(_) => {} - Err(wgpu::SurfaceError::Lost) => gpu.resize(game.get_content()), + Err(wgpu::SurfaceError::Lost) => gpu.resize(&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 => { + game.update_player_controls(&player); game.update(); + + let player_status = { + let pos = { + let o = &game.get_state().systemsim.get_ship(player.ship.unwrap()); + if let Some(o) = o { + let r = &game.get_state().systemsim.get_rigid_body(o.rigid_body); + if let Some(r) = r { + Some(util::rigidbody_position(r)) + } else { + None + } + } else { + None + } + }; + + PlayerStatus { pos } + }; + + // This must be updated BEFORE rendering! + player.step(&content, player_status); + gpu.window().request_redraw(); } @@ -81,25 +118,22 @@ fn main() -> Result<()> { .. } => { player.process_key(state, key); - game.update_player_controls(&player); } WindowEvent::MouseInput { state, button, .. } => { player.process_click(state, button); - game.update_player_controls(&player); } WindowEvent::MouseWheel { delta, phase, .. } => { player.process_scroll(delta, phase); - game.update_player_controls(&player); } WindowEvent::Resized(_) => { - gpu.resize(game.get_content()); + gpu.resize(&content); 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()); + gpu.resize(&content); player.set_camera_aspect( gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32, diff --git a/crates/playeragent/src/playeragent.rs b/crates/playeragent/src/playeragent.rs index 1510f5a..2da8615 100644 --- a/crates/playeragent/src/playeragent.rs +++ b/crates/playeragent/src/playeragent.rs @@ -51,6 +51,8 @@ impl PlayerAgent { self.input.v_scroll = 0.0; } - self.camera.pos = status.pos; + if status.pos.is_some() { + self.camera.pos = status.pos.unwrap(); + } } } diff --git a/crates/playeragent/src/playerstatus.rs b/crates/playeragent/src/playerstatus.rs index 1e9321f..3389125 100644 --- a/crates/playeragent/src/playerstatus.rs +++ b/crates/playeragent/src/playerstatus.rs @@ -1,5 +1,5 @@ use cgmath::Point2; pub struct PlayerStatus { - pub pos: Point2, + pub pos: Option>, } diff --git a/crates/render/src/datastructs.rs b/crates/render/src/datastructs.rs index 7fd4cae..1df65dd 100644 --- a/crates/render/src/datastructs.rs +++ b/crates/render/src/datastructs.rs @@ -41,7 +41,7 @@ pub struct RenderInput<'a> { pub particles: &'a mut Vec, /// Time we spent in each part of the game loop - pub timing: &'a mut Timing, + pub timing: Timing, } /// Renderer state. A reference to this struct is often passed to helper functions. diff --git a/crates/render/src/gpustate/render.rs b/crates/render/src/gpustate/render.rs index 256a239..7f3ae6e 100644 --- a/crates/render/src/gpustate/render.rs +++ b/crates/render/src/gpustate/render.rs @@ -16,8 +16,6 @@ use crate::{ impl super::GPUState { /// Main render function. Draws sprites on a window. pub fn render(&mut self, input: RenderInput) -> Result<(), wgpu::SurfaceError> { - input.timing.start_render(); - let output = self.surface.get_current_texture()?; let view = output.texture.create_view(&Default::default()); @@ -217,7 +215,6 @@ impl super::GPUState { self.state.queue.submit(iter::once(encoder.finish())); output.present(); - input.timing.mark_render(); Ok(()) } } diff --git a/crates/render/src/ui/fpsindicator.rs b/crates/render/src/ui/fpsindicator.rs index 69f9f8a..2b78cb6 100644 --- a/crates/render/src/ui/fpsindicator.rs +++ b/crates/render/src/ui/fpsindicator.rs @@ -36,12 +36,12 @@ impl FpsIndicator { self.buffer.set_text( &mut state.text_font_system, &format!( - "Frame: {:05.02?}%\nGame: {:05.02?}%\nShips: {:05.02?}%\nPhys: {:05.02?}%\nRender: {:.02?}", - 100.0 * (input.timing.frame / input.timing.render), - 100.0 * (input.timing.galaxy / input.timing.frame), - 100.0 * (input.timing.physics_sim / input.timing.frame), - 100.0 * (input.timing.physics_ship / input.timing.frame), - 1.0 / input.timing.render + "Frame: {:#?} ({:05.00})\nGame: {:05.02}%\nShips: {:05.02}%\nPhys: {:05.02}%\n", + input.timing.frame, 1.0 /input.timing.frame.as_secs_f32(), + 100.0 * (input.timing.galaxy.as_secs_f32() / input.timing.frame.as_secs_f32()), + 100.0 * (input.timing.physics_sim.as_secs_f32() / input.timing.frame.as_secs_f32()), + 100.0 + * (input.timing.physics_ship.as_secs_f32() / input.timing.frame.as_secs_f32()), ), Attrs::new().family(Family::Monospace), Shaping::Basic, diff --git a/crates/render/src/ui/radar.rs b/crates/render/src/ui/radar.rs index 85e4a69..f9df156 100644 --- a/crates/render/src/ui/radar.rs +++ b/crates/render/src/ui/radar.rs @@ -8,11 +8,15 @@ use crate::{ PositionAnchor, RenderInput, }; -pub(super) struct Radar {} +pub(super) struct Radar { + last_player_position: Point2, +} impl Radar { pub fn new() -> Self { - Self {} + Self { + last_player_position: Point2 { x: 0.0, y: 0.0 }, + } } } @@ -25,13 +29,17 @@ impl Radar { let system_object_scale = 1.0 / 600.0; let ship_scale = 1.0 / 10.0; - let player_world_object = input.systemsim.get_ship(input.player_data).unwrap(); - let player_body = input - .systemsim - .get_rigid_body(player_world_object.rigid_body) - .unwrap(); + // TODO: maybe a cleaner solution for last posititon? + // This is necessary because the player may be dead or landed + if let Some(player_world_object) = input.systemsim.get_ship(input.player_data) { + let player_body = input + .systemsim + .get_rigid_body(player_world_object.rigid_body) + .unwrap(); + + self.last_player_position = util::rigidbody_position(player_body) + }; - let player_position = util::rigidbody_position(player_body); let planet_sprite = input.ct.get_sprite_handle("ui::planetblip"); let ship_sprite = input.ct.get_sprite_handle("ui::shipblip"); let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow"); @@ -65,7 +73,7 @@ impl Radar { x: o.pos.x, y: o.pos.y, }; - let d = (p - player_position) / radar_range; + let d = (p - self.last_player_position) / radar_range; // Add half the blip sprite's height to distance let m = d.magnitude() + (size / (2.0 * radar_size)); if m < hide_range { @@ -120,7 +128,7 @@ impl Radar { let ship = input.ct.get_ship(s.data_handle.content_handle()); let size = (ship.size * ship.sprite.aspect) * ship_scale; let p = util::rigidbody_position(r); - let d = (p - player_position) / radar_range; + let d = (p - self.last_player_position) / radar_range; let m = d.magnitude() + (size / (2.0 * radar_size)); if m < hide_range { let size = size.min((hide_range - m) * size * shrink_distance); @@ -250,7 +258,7 @@ impl Radar { } // Arrow to center of system - let q = Point2 { x: 0.0, y: 0.0 } - player_position; + let q = Point2 { x: 0.0, y: 0.0 } - self.last_player_position; let m = q.magnitude(); if m > 200.0 { let player_angle = q.angle(Vector2 { x: 0.0, y: 1.0 }); diff --git a/crates/render/src/ui/status.rs b/crates/render/src/ui/status.rs index 694404b..404d3e7 100644 --- a/crates/render/src/ui/status.rs +++ b/crates/render/src/ui/status.rs @@ -25,13 +25,23 @@ impl Status { panic!("UI limit exceeded!") } - let player_world_object = input.systemsim.get_ship(input.player_data).unwrap(); - - let data = input.gx.get_ship(player_world_object.data_handle).unwrap(); - let max_shields = data.get_outfits().get_shield_strength(); - let current_shields = data.get_shields(); - let current_hull = data.get_hull(); - let max_hull = input.ct.get_ship(data.get_content()).hull; + let max_shields; + let current_shields; + let current_hull; + let max_hull; + if let Some(player_world_object) = input.systemsim.get_ship(input.player_data) { + let data = input.gx.get_ship(player_world_object.data_handle).unwrap(); + max_shields = data.get_outfits().get_shield_strength(); + current_shields = data.get_shields(); + current_hull = data.get_hull(); + max_hull = input.ct.get_ship(data.get_content()).hull; + } else { + // Sensible defaults if ship doesn't exist (landed or dead) + max_shields = 1.0; + current_shields = 0.0; + current_hull = 0.0; + max_hull = 1.0; + }; state.queue.write_buffer( &state.vertex_buffers.ui.instances, diff --git a/crates/systemsim/src/systemsim.rs b/crates/systemsim/src/systemsim.rs index 2e510f2..2707409 100644 --- a/crates/systemsim/src/systemsim.rs +++ b/crates/systemsim/src/systemsim.rs @@ -232,11 +232,16 @@ impl SystemSim { /// 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; + if player.ship.is_none() { + return; + } + let ship_object = self.ships.get_mut(&player.ship.unwrap()); + if let Some(ship_object) = ship_object { + 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: diff --git a/crates/util/src/timing.rs b/crates/util/src/timing.rs index 20a43c9..75b4df2 100644 --- a/crates/util/src/timing.rs +++ b/crates/util/src/timing.rs @@ -1,47 +1,40 @@ //! Keep track of the time we spent in each part of the game loop. -use std::time::Instant; +use std::time::{Duration, Instant}; /// Utility struct. /// Keeps track of the time we spent in each part of the game loop. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Timing { - /// The time we spent processing the whole frame - pub frame: f32, + /// The time we spent on all frame computations + pub frame: Duration, frame_timer: Instant, /// The time we spent simulating game state - pub galaxy: f32, + pub galaxy: Duration, galaxy_timer: Instant, /// The time we spent simulating physics - pub physics_sim: f32, + pub physics_sim: Duration, physics_sim_timer: Instant, /// The time we spent updating physics ships - pub physics_ship: f32, + pub physics_ship: Duration, physics_ship_timer: Instant, - - /// The time we spent simulating physics - pub render: f32, - render_timer: Instant, } -// TODO: macros // TODO: document each duration impl Timing { /// Create a new timing struct pub fn new() -> Self { Self { - frame: f32::NAN, - galaxy: f32::NAN, - physics_sim: f32::NAN, - physics_ship: f32::NAN, - render: f32::NAN, + frame: Duration::ZERO, + galaxy: Duration::ZERO, + physics_sim: Duration::ZERO, + physics_ship: Duration::ZERO, galaxy_timer: Instant::now(), physics_sim_timer: Instant::now(), physics_ship_timer: Instant::now(), - render_timer: Instant::now(), frame_timer: Instant::now(), } } @@ -66,33 +59,23 @@ impl Timing { self.physics_ship_timer = Instant::now(); } - /// Start render timer - pub fn start_render(&mut self) { - self.render_timer = Instant::now(); - } - /// Record galaxy simulation time. pub fn mark_frame(&mut self) { - self.frame = self.frame_timer.elapsed().as_secs_f32(); + self.frame = self.frame_timer.elapsed(); } /// Record galaxy simulation time. pub fn mark_galaxy(&mut self) { - self.galaxy = self.galaxy_timer.elapsed().as_secs_f32(); + self.galaxy = self.galaxy_timer.elapsed(); } /// Record physics simulation time pub fn mark_physics_sim(&mut self) { - self.physics_sim = self.physics_sim_timer.elapsed().as_secs_f32(); + self.physics_sim = self.physics_sim_timer.elapsed(); } /// Record physics ship update time pub fn mark_physics_ships(&mut self) { - self.physics_ship = self.physics_ship_timer.elapsed().as_secs_f32(); - } - - /// Record physics simulation time - pub fn mark_render(&mut self) { - self.render = self.render_timer.elapsed().as_secs_f32(); + self.physics_ship = self.physics_ship_timer.elapsed(); } }