Added animation automata to ships and projectiles

master
Mark 2024-01-20 09:59:21 -08:00
parent f4c0e91851
commit 132148fee3
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
3 changed files with 104 additions and 9 deletions

View File

@ -2,7 +2,7 @@ use crate::{AnimSectionHandle, Content, SectionEdge, SpriteHandle};
/// A single frame's state /// A single frame's state
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SpriteAnimationFrame { pub struct AnimationState {
/// The index of the texture we're fading from /// The index of the texture we're fading from
pub texture_a: u32, pub texture_a: u32,
@ -16,7 +16,7 @@ pub struct SpriteAnimationFrame {
pub fade: f32, pub fade: f32,
} }
impl SpriteAnimationFrame { impl AnimationState {
/// Convenience method. /// Convenience method.
/// Get texture index as an array /// Get texture index as an array
pub fn texture_index(&self) -> [u32; 2] { pub fn texture_index(&self) -> [u32; 2] {
@ -224,8 +224,8 @@ impl AnimAutomaton {
} }
/// Get the current frame of this animation /// Get the current frame of this animation
pub fn get_texture_idx(&self) -> SpriteAnimationFrame { pub fn get_texture_idx(&self) -> AnimationState {
return SpriteAnimationFrame { return AnimationState {
texture_a: self.last_texture, texture_a: self.last_texture,
texture_b: self.next_texture, texture_b: self.next_texture,
fade: self.current_fade, fade: self.current_fade,

View File

@ -1,4 +1,4 @@
use galactica_content::{FactionHandle, Projectile}; use galactica_content::{AnimAutomaton, AnimationState, Content, FactionHandle, Projectile};
use rand::Rng; use rand::Rng;
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle}; use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
@ -8,6 +8,9 @@ pub struct PhysProjectile {
/// This projectile's game data /// This projectile's game data
pub content: Projectile, pub content: Projectile,
/// This projectile's sprite animation state
anim: AnimAutomaton,
/// The remaining lifetime of this projectile, in seconds /// The remaining lifetime of this projectile, in seconds
pub lifetime: f32, pub lifetime: f32,
@ -27,7 +30,8 @@ pub struct PhysProjectile {
impl PhysProjectile { impl PhysProjectile {
/// Create a new projectile /// Create a new projectile
pub fn new( pub fn new(
content: Projectile, // TODO: use a handle ct: &Content,
content: Projectile, // TODO: use a handle?
rigid_body: RigidBodyHandle, rigid_body: RigidBodyHandle,
faction: FactionHandle, faction: FactionHandle,
collider: ColliderHandle, collider: ColliderHandle,
@ -36,6 +40,7 @@ impl PhysProjectile {
let size_rng = content.size_rng; let size_rng = content.size_rng;
let lifetime = content.lifetime; let lifetime = content.lifetime;
PhysProjectile { PhysProjectile {
anim: AnimAutomaton::new(ct, content.sprite),
rigid_body, rigid_body,
collider, collider,
content, content,
@ -46,8 +51,9 @@ impl PhysProjectile {
} }
/// Process this projectile's state after `t` seconds /// Process this projectile's state after `t` seconds
pub fn tick(&mut self, t: f32) { pub fn tick(&mut self, ct: &Content, t: f32) {
self.lifetime -= t; self.lifetime -= t;
self.anim.step(ct, t)
} }
/// Has this projectile expired? /// Has this projectile expired?
@ -55,3 +61,10 @@ impl PhysProjectile {
return self.lifetime < 0.0; return self.lifetime < 0.0;
} }
} }
impl PhysProjectile {
/// Get this projectile's animation state
pub fn get_anim_state(&self) -> AnimationState {
self.anim.get_texture_idx()
}
}

View File

@ -1,4 +1,6 @@
use galactica_content::{Content, FactionHandle, ShipHandle}; use galactica_content::{
AnimAutomaton, AnimationState, Content, EnginePoint, FactionHandle, OutfitHandle, ShipHandle,
};
use nalgebra::{point, vector, Rotation2, Vector2}; use nalgebra::{point, vector, Rotation2, Vector2};
use rand::Rng; use rand::Rng;
use rapier2d::{ use rapier2d::{
@ -51,7 +53,13 @@ pub struct PhysSimShip {
pub collider: ColliderHandle, pub collider: ColliderHandle,
/// This ship's game data /// This ship's game data
pub data: ShipData, pub(crate) data: ShipData,
/// This ship's sprite animation state
anim: AnimAutomaton,
/// Animation state for each of this ship's engines
engine_anim: Vec<(EnginePoint, AnimAutomaton)>,
/// This ship's controls /// This ship's controls
pub(crate) controls: ShipControls, pub(crate) controls: ShipControls,
@ -72,9 +80,11 @@ impl PhysSimShip {
) -> Self { ) -> Self {
let ship_ct = ct.get_ship(handle); let ship_ct = ct.get_ship(handle);
PhysSimShip { PhysSimShip {
anim: AnimAutomaton::new(ct, ship_ct.sprite),
rigid_body, rigid_body,
collider, collider,
data: ShipData::new(ct, handle, faction, personality), data: ShipData::new(ct, handle, faction, personality),
engine_anim: Vec::new(),
controls: ShipControls::new(), controls: ShipControls::new(),
collapse_sequence: Some(ShipCollapseSequence::new(ship_ct.collapse.length)), collapse_sequence: Some(ShipCollapseSequence::new(ship_ct.collapse.length)),
} }
@ -88,6 +98,18 @@ impl PhysSimShip {
collider: &mut Collider, collider: &mut Collider,
) { ) {
self.data.step(res.t); self.data.step(res.t);
self.anim.step(res.ct, res.t);
if self.controls.thrust {
for (_, e) in &mut self.engine_anim {
e.step(res.ct, res.t);
}
} else {
for (_, e) in &mut self.engine_anim {
e.reset(res.ct);
}
}
match self.data.get_state() { match self.data.get_state() {
ShipState::Collapsing { .. } => { ShipState::Collapsing { .. } => {
// Borrow checker hack, so we may pass self.data // Borrow checker hack, so we may pass self.data
@ -197,9 +219,69 @@ impl PhysSimShip {
} }
} }
/// Public mutable
impl PhysSimShip {
/// Re-create this ship's engine flare animations
/// Should be called whenever we change outfits
fn update_flares(&mut self, ct: &Content) {
// TODO: better way to pick flare sprite
let mut flare = None;
for (h, _) in self.data.get_outfits().iter_outfits() {
let c = ct.get_outfit(*h);
if c.engine_flare_sprite.is_some() {
flare = c.engine_flare_sprite;
break;
}
}
if flare.is_none() {
self.engine_anim = Vec::new();
return;
}
let flare = flare.unwrap();
self.engine_anim = ct
.get_ship(self.data.get_content())
.engines
.iter()
.map(|e| (e.clone(), AnimAutomaton::new(ct, flare)))
.collect();
}
/// Add one outfit to this ship
pub fn add_outfit(&mut self, ct: &Content, o: OutfitHandle) {
self.data.add_outfit(ct.get_outfit(o));
self.update_flares(ct);
}
/// Add many outfits to this ship
pub fn add_outfits(&mut self, ct: &Content, outfits: impl IntoIterator<Item = OutfitHandle>) {
for o in outfits {
self.data.add_outfit(ct.get_outfit(o));
}
self.update_flares(ct);
}
}
/// Public immutable
impl PhysSimShip { impl PhysSimShip {
/// Get this ship's control state /// Get this ship's control state
pub fn get_controls(&self) -> &ShipControls { pub fn get_controls(&self) -> &ShipControls {
&self.controls &self.controls
} }
/// Get this ship's engine animations
pub fn iter_engine_anim(&self) -> impl Iterator<Item = &(EnginePoint, AnimAutomaton)> {
self.engine_anim.iter()
}
/// Get this ship's animation state
pub fn get_anim_state(&self) -> AnimationState {
self.anim.get_texture_idx()
}
/// Get this ship's game data struct
pub fn get_data(&self) -> &ShipData {
&self.data
}
} }