Added basic ship behaviors

master
Mark 2023-12-29 18:34:30 -08:00
parent bffc7b9f23
commit 0184418394
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
6 changed files with 149 additions and 35 deletions

View File

@ -9,6 +9,7 @@ use crate::{
inputstatus::InputStatus,
physics::{util, Physics, ShipHandle},
render::Sprite,
shipbehavior::{self, ShipBehavior},
};
pub struct Game {
@ -20,14 +21,15 @@ pub struct Game {
paused: bool,
pub time_scale: f32,
world: Physics,
physics: Physics,
shipbehaviors: Vec<Box<dyn ShipBehavior>>,
}
impl Game {
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],
vec![
outfits::ShipOutfit::Gun(outfits::ShipGun::new(ct.guns[0].clone(), 1)),
@ -37,20 +39,28 @@ impl Game {
Point2 { x: 0.0, y: 0.0 },
);
world.add_ship(&ct.ships[0], vec![], Point2 { x: 300.0, y: 300.0 });
world.add_ship(
let h2 = physics.add_ship(&ct.ships[0], vec![], Point2 { x: 300.0, y: 300.0 });
let h3 = physics.add_ship(
&ct.ships[0],
vec![],
vec![outfits::ShipOutfit::Gun(outfits::ShipGun::new(
ct.guns[0].clone(),
0,
))],
Point2 {
x: -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 {
last_update: Instant::now(),
input: InputStatus::new(),
player: c,
player: h1,
camera: Camera {
pos: (0.0, 0.0).into(),
@ -60,7 +70,8 @@ impl Game {
paused: false,
time_scale: 1.0,
world,
physics,
shipbehaviors,
}
}
@ -88,11 +99,11 @@ impl Game {
pub fn update(&mut self) {
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
self.world
.get_ship_mut(&self.player)
.update_controls(&self.input);
for b in &mut self.shipbehaviors {
b.update_controls(&mut self.physics, &self.input, self.player);
}
self.world.tick(t);
self.physics.step(t);
if self.input.v_scroll != 0.0 {
self.camera.zoom =
@ -101,8 +112,8 @@ impl Game {
}
// TODO: Camera physics
let r = self.world.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_ship_mut(&self.player).physics_handle;
let r = self.physics.get_rigid_body(r.0); // TODO: r.0 shouldn't be public
let ship_pos = util::rigidbody_position(r);
self.camera.pos = ship_pos;
self.last_update = Instant::now();
@ -112,7 +123,7 @@ impl Game {
let mut sprites: Vec<Sprite> = Vec::new();
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
// (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));
// 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;
}

View File

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

View File

@ -11,11 +11,14 @@ use super::ProjectileBuilder;
use crate::{
content,
game::outfits,
inputstatus::InputStatus,
physics::{util, ShipHandle},
render::{Sprite, SpriteTexture},
};
pub struct ShipTickResult {
pub projectiles: Vec<ProjectileBuilder>,
}
pub struct ShipControls {
pub left: bool,
pub right: bool,
@ -34,10 +37,6 @@ impl ShipControls {
}
}
pub struct ShipTickResult {
pub projectiles: Vec<ProjectileBuilder>,
}
pub struct Ship {
pub physics_handle: ShipHandle,
outfits: outfits::ShipOutfits,
@ -45,8 +44,6 @@ pub struct Ship {
sprite: SpriteTexture,
size: f32,
pub hull: f32,
// TODO: replace with AI enum
pub controls: ShipControls,
}
@ -64,20 +61,13 @@ impl Ship {
Ship {
physics_handle,
outfits: o,
controls: ShipControls::new(),
sprite: SpriteTexture(c.sprite.clone()),
size: c.size,
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> {
let mut rng = rand::thread_rng();
let mut out = Vec::new();
@ -130,10 +120,11 @@ impl Ship {
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 engine_force = Matrix2::from_angle(ship_ang) * Vector2 { x: 0.0, y: 1.0 } * t;
if self.controls.thrust {
for e in self.outfits.iter_engines() {
r.apply_impulse(vector![engine_force.x, engine_force.y] * e.thrust, true);

View File

@ -101,7 +101,7 @@ impl Physics {
return h;
}
pub fn tick(&mut self, t: f32) {
pub fn step(&mut self, t: f32) {
// Run ship updates
let mut res = Vec::new();
let mut to_remove = Vec::new();
@ -111,7 +111,7 @@ impl Physics {
continue;
}
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 {
self.remove_ship(r);
@ -170,6 +170,14 @@ impl Physics {
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> + '_ {
self.ships
.values()

View File

@ -18,6 +18,13 @@ pub fn rigidbody_angle(r: &RigidBody) -> Deg<f32> {
.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> {
let v = r.velocity_at_point(&nalgebra::Point2::new(
r.translation()[0],

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;
}
}