Minor reorganization

master
Mark 2024-01-11 20:21:07 -08:00
parent 173263f334
commit 7b1e14defd
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
14 changed files with 177 additions and 160 deletions

View File

@ -134,7 +134,7 @@ impl ContentBuildContext {
}
/// Represents static game content
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Content {
/// Sprites
pub sprites: Vec<Sprite>,

View File

@ -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.

View File

@ -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,

View File

@ -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<ParticleBuilder>,
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();
}
}

View File

@ -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,

View File

@ -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();
}
}
}

View File

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

View File

@ -41,7 +41,7 @@ pub struct RenderInput<'a> {
pub particles: &'a mut Vec<ParticleBuilder>,
/// 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.

View File

@ -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(())
}
}

View File

@ -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,

View File

@ -8,11 +8,15 @@ use crate::{
PositionAnchor, RenderInput,
};
pub(super) struct Radar {}
pub(super) struct Radar {
last_player_position: Point2<f32>,
}
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 });

View File

@ -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,

View File

@ -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:

View File

@ -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();
}
}