Compare commits

..

2 Commits

Author SHA1 Message Date
Mark 0184418394
Added basic ship behaviors 2023-12-29 18:34:30 -08:00
Mark bffc7b9f23
Fixed hiding rotated sprites 2023-12-29 17:54:51 -08:00
7 changed files with 157 additions and 40 deletions

View File

@ -9,6 +9,7 @@ use crate::{
inputstatus::InputStatus, inputstatus::InputStatus,
physics::{util, Physics, ShipHandle}, physics::{util, Physics, ShipHandle},
render::Sprite, render::Sprite,
shipbehavior::{self, ShipBehavior},
}; };
pub struct Game { pub struct Game {
@ -20,14 +21,15 @@ pub struct Game {
paused: bool, paused: bool,
pub time_scale: f32, pub time_scale: f32,
world: Physics, physics: Physics,
shipbehaviors: Vec<Box<dyn ShipBehavior>>,
} }
impl Game { impl Game {
pub fn new(ct: Content) -> Self { pub fn new(ct: Content) -> Self {
let mut world = Physics::new(); let mut physics = Physics::new();
let c = world.add_ship( let h1 = physics.add_ship(
&ct.ships[0], &ct.ships[0],
vec![ vec![
outfits::ShipOutfit::Gun(outfits::ShipGun::new(ct.guns[0].clone(), 1)), outfits::ShipOutfit::Gun(outfits::ShipGun::new(ct.guns[0].clone(), 1)),
@ -37,20 +39,28 @@ impl Game {
Point2 { x: 0.0, y: 0.0 }, Point2 { x: 0.0, y: 0.0 },
); );
world.add_ship(&ct.ships[0], vec![], Point2 { x: 300.0, y: 300.0 }); let h2 = physics.add_ship(&ct.ships[0], vec![], Point2 { x: 300.0, y: 300.0 });
world.add_ship( let h3 = physics.add_ship(
&ct.ships[0], &ct.ships[0],
vec![], vec![outfits::ShipOutfit::Gun(outfits::ShipGun::new(
ct.guns[0].clone(),
0,
))],
Point2 { Point2 {
x: -300.0, x: -300.0,
y: 300.0, y: 300.0,
}, },
); );
let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new();
shipbehaviors.push(shipbehavior::Player::new(h1));
shipbehaviors.push(shipbehavior::Point::new(h3));
shipbehaviors.push(shipbehavior::Dummy::new(h2));
Game { Game {
last_update: Instant::now(), last_update: Instant::now(),
input: InputStatus::new(), input: InputStatus::new(),
player: c, player: h1,
camera: Camera { camera: Camera {
pos: (0.0, 0.0).into(), pos: (0.0, 0.0).into(),
@ -60,7 +70,8 @@ impl Game {
paused: false, paused: false,
time_scale: 1.0, time_scale: 1.0,
world, physics,
shipbehaviors,
} }
} }
@ -88,11 +99,11 @@ impl Game {
pub fn update(&mut self) { pub fn update(&mut self) {
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale; let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
self.world for b in &mut self.shipbehaviors {
.get_ship_mut(&self.player) b.update_controls(&mut self.physics, &self.input, self.player);
.update_controls(&self.input); }
self.world.tick(t); self.physics.step(t);
if self.input.v_scroll != 0.0 { if self.input.v_scroll != 0.0 {
self.camera.zoom = self.camera.zoom =
@ -101,8 +112,8 @@ impl Game {
} }
// TODO: Camera physics // TODO: Camera physics
let r = self.world.get_ship_mut(&self.player).physics_handle; let r = self.physics.get_ship_mut(&self.player).physics_handle;
let r = self.world.get_rigid_body(r.0); // TODO: r.0 shouldn't be public let r = self.physics.get_rigid_body(r.0); // TODO: r.0 shouldn't be public
let ship_pos = util::rigidbody_position(r); let ship_pos = util::rigidbody_position(r);
self.camera.pos = ship_pos; self.camera.pos = ship_pos;
self.last_update = Instant::now(); self.last_update = Instant::now();
@ -112,7 +123,7 @@ impl Game {
let mut sprites: Vec<Sprite> = Vec::new(); let mut sprites: Vec<Sprite> = Vec::new();
sprites.append(&mut self.system.get_sprites()); sprites.append(&mut self.system.get_sprites());
sprites.extend(self.world.get_ship_sprites()); sprites.extend(self.physics.get_ship_sprites());
// Make sure sprites are drawn in the correct order // Make sure sprites are drawn in the correct order
// (note the reversed a, b in the comparator) // (note the reversed a, b in the comparator)
@ -122,7 +133,7 @@ impl Game {
sprites.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z)); sprites.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z));
// Don't waste time sorting these, they should always be on top. // Don't waste time sorting these, they should always be on top.
sprites.extend(self.world.get_projectile_sprites()); sprites.extend(self.physics.get_projectile_sprites());
return sprites; return sprites;
} }

View File

@ -4,6 +4,7 @@ mod inputstatus;
mod objects; mod objects;
mod physics; mod physics;
mod render; mod render;
mod shipbehavior;
use anyhow::Result; use anyhow::Result;
use galactica_content as content; use galactica_content as content;

View File

@ -11,11 +11,14 @@ use super::ProjectileBuilder;
use crate::{ use crate::{
content, content,
game::outfits, game::outfits,
inputstatus::InputStatus,
physics::{util, ShipHandle}, physics::{util, ShipHandle},
render::{Sprite, SpriteTexture}, render::{Sprite, SpriteTexture},
}; };
pub struct ShipTickResult {
pub projectiles: Vec<ProjectileBuilder>,
}
pub struct ShipControls { pub struct ShipControls {
pub left: bool, pub left: bool,
pub right: bool, pub right: bool,
@ -34,10 +37,6 @@ impl ShipControls {
} }
} }
pub struct ShipTickResult {
pub projectiles: Vec<ProjectileBuilder>,
}
pub struct Ship { pub struct Ship {
pub physics_handle: ShipHandle, pub physics_handle: ShipHandle,
outfits: outfits::ShipOutfits, outfits: outfits::ShipOutfits,
@ -45,8 +44,6 @@ pub struct Ship {
sprite: SpriteTexture, sprite: SpriteTexture,
size: f32, size: f32,
pub hull: f32, pub hull: f32,
// TODO: replace with AI enum
pub controls: ShipControls, pub controls: ShipControls,
} }
@ -64,20 +61,13 @@ impl Ship {
Ship { Ship {
physics_handle, physics_handle,
outfits: o, outfits: o,
controls: ShipControls::new(),
sprite: SpriteTexture(c.sprite.clone()), sprite: SpriteTexture(c.sprite.clone()),
size: c.size, size: c.size,
hull: c.hull, hull: c.hull,
controls: ShipControls::new(),
} }
} }
pub fn update_controls(&mut self, input: &InputStatus) {
self.controls.thrust = input.key_thrust;
self.controls.right = input.key_right;
self.controls.left = input.key_left;
self.controls.guns = input.key_guns;
}
pub fn fire_guns(&mut self, r: &RigidBody) -> Vec<ProjectileBuilder> { pub fn fire_guns(&mut self, r: &RigidBody) -> Vec<ProjectileBuilder> {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let mut out = Vec::new(); let mut out = Vec::new();
@ -130,10 +120,11 @@ impl Ship {
return out; return out;
} }
pub fn tick(&mut self, r: &mut RigidBody, t: f32) -> ShipTickResult { /// Apply the effects of all active controls
pub fn apply_controls(&mut self, r: &mut RigidBody, t: f32) -> ShipTickResult {
let ship_ang = util::rigidbody_angle(r); let ship_ang = util::rigidbody_angle(r);
let engine_force = Matrix2::from_angle(ship_ang) * Vector2 { x: 0.0, y: 1.0 } * t; let engine_force = Matrix2::from_angle(ship_ang) * Vector2 { x: 0.0, y: 1.0 } * t;
if self.controls.thrust { if self.controls.thrust {
for e in self.outfits.iter_engines() { for e in self.outfits.iter_engines() {
r.apply_impulse(vector![engine_force.x, engine_force.y] * e.thrust, true); r.apply_impulse(vector![engine_force.x, engine_force.y] * e.thrust, true);

View File

@ -101,7 +101,7 @@ impl Physics {
return h; return h;
} }
pub fn tick(&mut self, t: f32) { pub fn step(&mut self, t: f32) {
// Run ship updates // Run ship updates
let mut res = Vec::new(); let mut res = Vec::new();
let mut to_remove = Vec::new(); let mut to_remove = Vec::new();
@ -111,7 +111,7 @@ impl Physics {
continue; continue;
} }
let r = &mut self.wrapper.rigid_body_set[s.physics_handle.0]; let r = &mut self.wrapper.rigid_body_set[s.physics_handle.0];
res.push(s.tick(r, t)); res.push(s.apply_controls(r, t));
} }
for r in to_remove { for r in to_remove {
self.remove_ship(r); self.remove_ship(r);
@ -170,6 +170,14 @@ impl Physics {
self.ships.get_mut(&s.1).unwrap() self.ships.get_mut(&s.1).unwrap()
} }
pub fn get_ship_body(&self, s: &ShipHandle) -> (&objects::Ship, &RigidBody) {
// TODO: handle dead handles
(
self.ships.get(&s.1).unwrap(),
self.wrapper.rigid_body_set.get(s.0).unwrap(),
)
}
pub fn get_ship_sprites(&self) -> impl Iterator<Item = Sprite> + '_ { pub fn get_ship_sprites(&self) -> impl Iterator<Item = Sprite> + '_ {
self.ships self.ships
.values() .values()

View File

@ -18,6 +18,13 @@ pub fn rigidbody_angle(r: &RigidBody) -> Deg<f32> {
.into() .into()
} }
pub fn rigidbody_rotation(r: &RigidBody) -> Vector2<f32> {
Vector2 {
x: r.rotation().im,
y: r.rotation().re,
}
}
pub fn rigidbody_velocity(r: &RigidBody) -> cgmath::Vector2<f32> { pub fn rigidbody_velocity(r: &RigidBody) -> cgmath::Vector2<f32> {
let v = r.velocity_at_point(&nalgebra::Point2::new( let v = r.velocity_at_point(&nalgebra::Point2::new(
r.translation()[0], r.translation()[0],

View File

@ -220,14 +220,17 @@ impl GPUState {
// Game dimensions of this sprite post-scale. // Game dimensions of this sprite post-scale.
// Don't divide by 2, we use this later. // Don't divide by 2, we use this later.
let height = s.size / s.pos.z; let height = s.size / s.pos.z;
let width = height * texture.aspect;
// Width or height, whichever is larger
// Accounts for sprite rotation.
let m = height * texture.aspect.max(1.0);
// Don't draw (or compute matrices for) // Don't draw (or compute matrices for)
// sprites that are off the screen // sprites that are off the screen
if pos.x < clip_ne.x - width if pos.x < clip_ne.x - m
|| pos.y > clip_ne.y + height || pos.y > clip_ne.y + m
|| pos.x > clip_sw.x + width || pos.x > clip_sw.x + m
|| pos.y < clip_sw.y - height || pos.y < clip_sw.y - m
{ {
return; return;
} }

96
src/shipbehavior/mod.rs Normal file
View File

@ -0,0 +1,96 @@
use cgmath::{Deg, InnerSpace};
use crate::{
inputstatus::InputStatus,
physics::{util, Physics, ShipHandle},
};
pub trait ShipBehavior
where
Self: Send,
{
fn update_controls(&mut self, physics: &mut Physics, input: &InputStatus, player: ShipHandle);
}
pub struct Dummy {
_handle: ShipHandle,
}
impl Dummy {
pub fn new(handle: ShipHandle) -> Box<Self> {
Box::new(Self { _handle: handle })
}
}
impl ShipBehavior for Dummy {
fn update_controls(
&mut self,
_physics: &mut Physics,
_input: &InputStatus,
_player: ShipHandle,
) {
}
}
pub struct Player {
handle: ShipHandle,
}
impl Player {
pub fn new(handle: ShipHandle) -> Box<Self> {
Box::new(Self { handle })
}
}
impl ShipBehavior for Player {
fn update_controls(&mut self, physics: &mut Physics, input: &InputStatus, _player: ShipHandle) {
let s = physics.get_ship_mut(&self.handle);
s.controls.left = input.key_left;
s.controls.right = input.key_right;
s.controls.guns = input.key_guns;
s.controls.thrust = input.key_thrust;
}
}
pub struct Point {
handle: ShipHandle,
}
impl Point {
pub fn new(handle: ShipHandle) -> Box<Self> {
Box::new(Self { handle })
}
}
impl ShipBehavior for Point {
fn update_controls(&mut self, physics: &mut Physics, _input: &InputStatus, player: ShipHandle) {
let (_, r) = physics.get_ship_body(&player);
let p = util::rigidbody_position(r);
let (_, r) = physics.get_ship_body(&self.handle);
let t = util::rigidbody_position(r);
let pa = util::rigidbody_rotation(r);
let v = r.angvel();
let d: Deg<f32> = (t - p).angle(pa).into();
println!("{:?}", d);
let s = physics.get_ship_mut(&self.handle);
s.controls.left = false;
s.controls.right = false;
if d < Deg(0.0) && v < 0.1 {
s.controls.left = false;
s.controls.right = true;
println!("r")
} else if d > Deg(0.0) && v > -0.1 {
println!("l");
s.controls.left = true;
s.controls.right = false;
}
s.controls.guns = true;
s.controls.thrust = false;
}
}