use std::collections::HashMap;
use crate::{
entity::{EntityId, EntityManager},
math::{Position, Size, Color, Vec2},
EngineResult,
};
#[derive(Debug)]
pub struct AnimationManager {
animation_sets: HashMap<AnimationSetId, AnimationSet>,
entity_animations: HashMap<EntityId, EntityAnimations>,
tween_animations: HashMap<TweenId, TweenAnimation>,
global_animations: Vec<GlobalAnimation>,
next_set_id: AnimationSetId,
next_tween_id: TweenId,
global_time_scale: f32,
}
impl AnimationManager {
pub fn new() -> Self {
Self {
animation_sets: HashMap::new(),
entity_animations: HashMap::new(),
tween_animations: HashMap::new(),
global_animations: Vec::new(),
next_set_id: AnimationSetId(1),
next_tween_id: TweenId(1),
global_time_scale: 1.0,
}
}
pub fn create_animation_set(&mut self, name: String) -> AnimationSetId {
let id = self.next_set_id;
self.next_set_id.0 += 1;
let set = AnimationSet::new(id, name);
self.animation_sets.insert(id, set);
id
}
pub fn add_animation_to_set(&mut self, set_id: AnimationSetId, animation: Animation) -> Option<AnimationId> {
if let Some(set) = self.animation_sets.get_mut(&set_id) {
Some(set.add_animation(animation))
} else {
None
}
}
pub fn play_animation(&mut self, entity_id: EntityId, set_id: AnimationSetId, animation_id: AnimationId) {
let entity_anims = self.entity_animations.entry(entity_id).or_insert_with(|| EntityAnimations::new(entity_id));
entity_anims.play_animation(set_id, animation_id);
}
pub fn stop_animation(&mut self, entity_id: EntityId, set_id: AnimationSetId) {
if let Some(entity_anims) = self.entity_animations.get_mut(&entity_id) {
entity_anims.stop_animation(set_id);
}
}
pub fn create_tween<T: TweenableValue + 'static>(&mut self, target: T, duration: f32, easing: EasingFunction) -> TweenId {
let id = self.next_tween_id;
self.next_tween_id.0 += 1;
let tween = TweenAnimation::new(target, duration, easing);
self.tween_animations.insert(id, tween);
id
}
pub fn start_tween(&mut self, tween_id: TweenId) {
if let Some(tween) = self.tween_animations.get_mut(&tween_id) {
tween.start();
}
}
pub fn stop_tween(&mut self, tween_id: TweenId) {
if let Some(tween) = self.tween_animations.get_mut(&tween_id) {
tween.stop();
}
}
pub fn add_global_animation(&mut self, animation: GlobalAnimation) {
self.global_animations.push(animation);
}
pub fn set_time_scale(&mut self, scale: f32) {
self.global_time_scale = scale.max(0.0);
}
pub fn update(&mut self, entity_manager: &mut EntityManager, delta_time: f32) -> EngineResult<()> {
let scaled_delta = delta_time * self.global_time_scale;
let mut entities_to_remove = Vec::new();
let animation_sets = &self.animation_sets;
for (entity_id, entity_anims) in &mut self.entity_animations {
entity_anims.update(animation_sets, scaled_delta);
if entity_anims.is_empty() {
entities_to_remove.push(*entity_id);
}
}
for (entity_id, entity_anims) in &self.entity_animations {
self.apply_entity_animations(*entity_id, entity_anims, entity_manager);
}
for entity_id in entities_to_remove {
self.entity_animations.remove(&entity_id);
}
let mut tweens_to_remove = Vec::new();
for (tween_id, tween) in &mut self.tween_animations {
tween.update(scaled_delta);
if tween.is_complete() {
tweens_to_remove.push(*tween_id);
}
}
for tween_id in tweens_to_remove {
self.tween_animations.remove(&tween_id);
}
self.global_animations.retain_mut(|anim| {
anim.update(scaled_delta);
!anim.is_complete()
});
Ok(())
}
pub fn get_animation_set(&self, set_id: AnimationSetId) -> Option<&AnimationSet> {
self.animation_sets.get(&set_id)
}
pub fn has_active_animations(&self, entity_id: EntityId) -> bool {
self.entity_animations.get(&entity_id)
.map(|anims| !anims.is_empty())
.unwrap_or(false)
}
fn apply_entity_animations(&self, entity_id: EntityId, entity_anims: &EntityAnimations, entity_manager: &mut EntityManager) {
if let Some(current_frame) = entity_anims.get_current_sprite_frame() {
if let Some(sprite) = entity_manager.get_sprite(entity_id) {
let mut new_sprite = sprite.clone();
entity_manager.add_sprite(entity_id, new_sprite);
}
}
if let Some(transform_delta) = entity_anims.get_transform_delta() {
if let Some(transform) = entity_manager.get_transform_mut(entity_id) {
transform.position += transform_delta.position;
transform.rotation += transform_delta.rotation;
transform.scale += transform_delta.scale;
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AnimationSetId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AnimationId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TweenId(pub u32);
#[derive(Debug)]
pub struct AnimationSet {
pub id: AnimationSetId,
pub name: String,
animations: HashMap<AnimationId, Animation>,
next_anim_id: AnimationId,
}
impl AnimationSet {
pub fn new(id: AnimationSetId, name: String) -> Self {
Self {
id,
name,
animations: HashMap::new(),
next_anim_id: AnimationId(1),
}
}
pub fn add_animation(&mut self, animation: Animation) -> AnimationId {
let id = self.next_anim_id;
self.next_anim_id.0 += 1;
self.animations.insert(id, animation);
id
}
pub fn get_animation(&self, anim_id: AnimationId) -> Option<&Animation> {
self.animations.get(&anim_id)
}
pub fn remove_animation(&mut self, anim_id: AnimationId) -> Option<Animation> {
self.animations.remove(&anim_id)
}
pub fn get_all_animations(&self) -> impl Iterator<Item = (AnimationId, &Animation)> {
self.animations.iter().map(|(id, anim)| (*id, anim))
}
}
#[derive(Debug, Clone)]
pub struct Animation {
pub name: String,
pub animation_type: AnimationType,
pub duration: f32,
pub looping: bool,
pub play_mode: PlayMode,
pub metadata: AnimationMetadata,
}
impl Animation {
pub fn sprite_animation(name: String, frames: Vec<SpriteFrame>, fps: f32) -> Self {
let duration = frames.len() as f32 / fps;
Self {
name,
animation_type: AnimationType::Sprite { frames, fps },
duration,
looping: true,
play_mode: PlayMode::Forward,
metadata: AnimationMetadata::default(),
}
}
pub fn transform_animation(name: String, keyframes: Vec<TransformKeyframe>, duration: f32) -> Self {
Self {
name,
animation_type: AnimationType::Transform { keyframes },
duration,
looping: false,
play_mode: PlayMode::Forward,
metadata: AnimationMetadata::default(),
}
}
pub fn color_animation(name: String, keyframes: Vec<ColorKeyframe>, duration: f32) -> Self {
Self {
name,
animation_type: AnimationType::Color { keyframes },
duration,
looping: false,
play_mode: PlayMode::Forward,
metadata: AnimationMetadata::default(),
}
}
pub fn set_looping(&mut self, looping: bool) {
self.looping = looping;
}
pub fn set_play_mode(&mut self, play_mode: PlayMode) {
self.play_mode = play_mode;
}
}
#[derive(Debug, Clone)]
pub enum AnimationType {
Sprite {
frames: Vec<SpriteFrame>,
fps: f32,
},
Transform {
keyframes: Vec<TransformKeyframe>,
},
Color {
keyframes: Vec<ColorKeyframe>,
},
Custom {
data: Vec<u8>,
update_fn: fn(&[u8], f32) -> AnimationState,
},
}
#[derive(Debug, Clone)]
pub struct SpriteFrame {
pub texture_rect: Option<(u32, u32, u32, u32)>,
pub offset: Position,
pub duration: Option<f32>,
}
impl SpriteFrame {
pub fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
Self {
texture_rect: Some((x, y, width, height)),
offset: Position::new(0.0, 0.0),
duration: None,
}
}
pub fn with_offset(mut self, offset: Position) -> Self {
self.offset = offset;
self
}
pub fn with_duration(mut self, duration: f32) -> Self {
self.duration = Some(duration);
self
}
}
#[derive(Debug, Clone)]
pub struct TransformKeyframe {
pub time: f32,
pub position: Option<Position>,
pub rotation: Option<f32>,
pub scale: Option<Size>,
pub easing: EasingFunction,
}
impl TransformKeyframe {
pub fn new(time: f32) -> Self {
Self {
time,
position: None,
rotation: None,
scale: None,
easing: EasingFunction::Linear,
}
}
pub fn with_position(mut self, position: Position) -> Self {
self.position = Some(position);
self
}
pub fn with_rotation(mut self, rotation: f32) -> Self {
self.rotation = Some(rotation);
self
}
pub fn with_scale(mut self, scale: Size) -> Self {
self.scale = Some(scale);
self
}
pub fn with_easing(mut self, easing: EasingFunction) -> Self {
self.easing = easing;
self
}
}
#[derive(Debug, Clone)]
pub struct ColorKeyframe {
pub time: f32,
pub color: Color,
pub easing: EasingFunction,
}
impl ColorKeyframe {
pub fn new(time: f32, color: Color) -> Self {
Self {
time,
color,
easing: EasingFunction::Linear,
}
}
pub fn with_easing(mut self, easing: EasingFunction) -> Self {
self.easing = easing;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PlayMode {
Forward,
Reverse,
PingPong,
Random,
}
#[derive(Debug, Clone, Default)]
pub struct AnimationMetadata {
pub tags: Vec<String>,
pub priority: i32,
pub blend_weight: f32,
}
#[derive(Debug, Clone)]
pub struct AnimationState {
pub current_time: f32,
pub is_playing: bool,
pub is_paused: bool,
pub current_frame: usize,
pub direction: i32,
pub loop_count: u32,
}
impl AnimationState {
pub fn new() -> Self {
Self {
current_time: 0.0,
is_playing: false,
is_paused: false,
current_frame: 0,
direction: 1,
loop_count: 0,
}
}
pub fn play(&mut self) {
self.is_playing = true;
self.is_paused = false;
}
pub fn pause(&mut self) {
self.is_paused = true;
}
pub fn stop(&mut self) {
self.is_playing = false;
self.is_paused = false;
self.current_time = 0.0;
self.current_frame = 0;
self.loop_count = 0;
}
pub fn reset(&mut self) {
self.current_time = 0.0;
self.current_frame = 0;
self.direction = 1;
self.loop_count = 0;
}
}
#[derive(Debug)]
pub struct EntityAnimations {
pub entity_id: EntityId,
active_animations: HashMap<AnimationSetId, (AnimationId, AnimationState)>,
animation_layers: Vec<AnimationLayer>,
}
impl EntityAnimations {
pub fn new(entity_id: EntityId) -> Self {
Self {
entity_id,
active_animations: HashMap::new(),
animation_layers: Vec::new(),
}
}
pub fn play_animation(&mut self, set_id: AnimationSetId, animation_id: AnimationId) {
let mut state = AnimationState::new();
state.play();
self.active_animations.insert(set_id, (animation_id, state));
}
pub fn stop_animation(&mut self, set_id: AnimationSetId) {
self.active_animations.remove(&set_id);
}
pub fn pause_animation(&mut self, set_id: AnimationSetId) {
if let Some((_, state)) = self.active_animations.get_mut(&set_id) {
state.pause();
}
}
pub fn resume_animation(&mut self, set_id: AnimationSetId) {
if let Some((_, state)) = self.active_animations.get_mut(&set_id) {
state.is_paused = false;
state.is_playing = true;
}
}
pub fn update(&mut self, animation_sets: &HashMap<AnimationSetId, AnimationSet>, delta_time: f32) {
let mut animations_to_remove = Vec::new();
let mut updates = Vec::new();
for (set_id, (anim_id, state)) in &self.active_animations {
if !state.is_playing || state.is_paused {
continue;
}
if let Some(set) = animation_sets.get(set_id) {
if let Some(animation) = set.get_animation(*anim_id) {
updates.push((*set_id, *anim_id, animation.clone()));
if !animation.looping && state.current_time >= animation.duration {
animations_to_remove.push(*set_id);
}
}
}
}
for (set_id, _anim_id, animation) in updates {
if let Some((_anim_id, state)) = self.active_animations.get_mut(&set_id) {
state.current_time += delta_time * state.direction as f32;
match &animation.animation_type {
AnimationType::Sprite { frames, fps } => {
let frame_duration = 1.0 / fps;
let total_frames = frames.len();
if total_frames > 0 {
state.current_frame = ((state.current_time / frame_duration) as usize) % total_frames;
}
},
AnimationType::Transform { keyframes: _ } => {
},
AnimationType::Color { keyframes: _ } => {
},
AnimationType::Custom { data, update_fn } => {
*state = update_fn(data, state.current_time);
},
}
if animation.looping && state.current_time >= animation.duration {
match animation.play_mode {
PlayMode::Forward => {
state.current_time = 0.0;
state.loop_count += 1;
},
PlayMode::Reverse => {
state.current_time = animation.duration;
state.direction = -1;
state.loop_count += 1;
},
PlayMode::PingPong => {
state.direction *= -1;
if state.direction == 1 {
state.loop_count += 1;
}
},
PlayMode::Random => {
state.current_time = fastrand::f32() * animation.duration;
},
}
}
}
}
for set_id in animations_to_remove {
self.active_animations.remove(&set_id);
}
}
pub fn is_empty(&self) -> bool {
self.active_animations.is_empty()
}
pub fn get_current_sprite_frame(&self) -> Option<&SpriteFrame> {
for (set_id, (anim_id, state)) in &self.active_animations {
return None;
}
None
}
pub fn get_transform_delta(&self) -> Option<TransformDelta> {
let mut delta = TransformDelta::default();
let mut has_delta = false;
for (set_id, (anim_id, state)) in &self.active_animations {
}
if has_delta {
Some(delta)
} else {
None
}
}
fn update_animation_state(&self, animation: &Animation, state: &mut AnimationState, delta_time: f32) {
state.current_time += delta_time * state.direction as f32;
match &animation.animation_type {
AnimationType::Sprite { frames, fps } => {
let frame_duration = 1.0 / fps;
let total_frames = frames.len();
if total_frames > 0 {
state.current_frame = ((state.current_time / frame_duration) as usize) % total_frames;
}
},
AnimationType::Transform { keyframes } => {
self.update_transform_animation(keyframes, state);
},
AnimationType::Color { keyframes } => {
self.update_color_animation(keyframes, state);
},
AnimationType::Custom { data, update_fn } => {
*state = update_fn(data, state.current_time);
},
}
if animation.looping && state.current_time >= animation.duration {
match animation.play_mode {
PlayMode::Forward => {
state.current_time = 0.0;
state.loop_count += 1;
},
PlayMode::Reverse => {
state.current_time = animation.duration;
state.direction = -1;
state.loop_count += 1;
},
PlayMode::PingPong => {
state.direction *= -1;
if state.direction == 1 {
state.loop_count += 1;
}
},
PlayMode::Random => {
state.current_time = fastrand::f32() * animation.duration;
},
}
}
}
fn update_transform_animation(&self, keyframes: &[TransformKeyframe], state: &mut AnimationState) {
for i in 0..keyframes.len() - 1 {
let current_kf = &keyframes[i];
let next_kf = &keyframes[i + 1];
if state.current_time >= current_kf.time && state.current_time <= next_kf.time {
let t = (state.current_time - current_kf.time) / (next_kf.time - current_kf.time);
let eased_t = next_kf.easing.apply(t);
break;
}
}
}
fn update_color_animation(&self, keyframes: &[ColorKeyframe], state: &mut AnimationState) {
for i in 0..keyframes.len() - 1 {
let current_kf = &keyframes[i];
let next_kf = &keyframes[i + 1];
if state.current_time >= current_kf.time && state.current_time <= next_kf.time {
let t = (state.current_time - current_kf.time) / (next_kf.time - current_kf.time);
let eased_t = next_kf.easing.apply(t);
break;
}
}
}
}
#[derive(Debug)]
pub struct AnimationLayer {
pub name: String,
pub weight: f32,
pub blend_mode: BlendMode,
pub active_animation: Option<(AnimationSetId, AnimationId)>,
}
#[derive(Debug, Clone, Copy)]
pub enum BlendMode {
Override,
Additive,
Multiply,
Screen,
}
#[derive(Debug, Default)]
pub struct TransformDelta {
pub position: Position,
pub rotation: f32,
pub scale: Size,
}
pub struct TweenAnimation {
target_value: Box<dyn TweenableValue>,
duration: f32,
elapsed_time: f32,
easing: EasingFunction,
state: TweenState,
on_complete: Option<Box<dyn Fn() + Send>>,
}
impl std::fmt::Debug for TweenAnimation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TweenAnimation")
.field("target_value", &"<dyn TweenableValue>")
.field("duration", &self.duration)
.field("elapsed_time", &self.elapsed_time)
.field("easing", &self.easing)
.field("state", &self.state)
.field("on_complete", &self.on_complete.as_ref().map(|_| "<callback>"))
.finish()
}
}
impl TweenAnimation {
pub fn new<T: TweenableValue + 'static>(target: T, duration: f32, easing: EasingFunction) -> Self {
Self {
target_value: Box::new(target),
duration,
elapsed_time: 0.0,
easing,
state: TweenState::Stopped,
on_complete: None,
}
}
pub fn start(&mut self) {
self.state = TweenState::Playing;
self.elapsed_time = 0.0;
}
pub fn pause(&mut self) {
if self.state == TweenState::Playing {
self.state = TweenState::Paused;
}
}
pub fn resume(&mut self) {
if self.state == TweenState::Paused {
self.state = TweenState::Playing;
}
}
pub fn stop(&mut self) {
self.state = TweenState::Stopped;
self.elapsed_time = 0.0;
}
pub fn update(&mut self, delta_time: f32) {
if self.state != TweenState::Playing {
return;
}
self.elapsed_time += delta_time;
let progress = (self.elapsed_time / self.duration).clamp(0.0, 1.0);
let eased_progress = self.easing.apply(progress);
self.target_value.update(eased_progress);
if progress >= 1.0 {
self.state = TweenState::Complete;
if let Some(callback) = &self.on_complete {
callback();
}
}
}
pub fn is_complete(&self) -> bool {
self.state == TweenState::Complete
}
pub fn set_on_complete<F: Fn() + Send + 'static>(&mut self, callback: F) {
self.on_complete = Some(Box::new(callback));
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TweenState {
Stopped,
Playing,
Paused,
Complete,
}
pub trait TweenableValue: std::fmt::Debug + Send {
fn update(&mut self, progress: f32);
fn get_current_value(&self) -> Box<dyn std::any::Any>;
}
#[derive(Debug)]
pub struct PositionTween {
start_value: Position,
end_value: Position,
current_value: Position,
}
impl PositionTween {
pub fn new(start: Position, end: Position) -> Self {
Self {
start_value: start,
end_value: end,
current_value: start,
}
}
}
impl TweenableValue for PositionTween {
fn update(&mut self, progress: f32) {
self.current_value = self.start_value.lerp(self.end_value, progress);
}
fn get_current_value(&self) -> Box<dyn std::any::Any> {
Box::new(self.current_value)
}
}
#[derive(Debug)]
pub struct ColorTween {
start_value: Color,
end_value: Color,
current_value: Color,
}
impl ColorTween {
pub fn new(start: Color, end: Color) -> Self {
Self {
start_value: start,
end_value: end,
current_value: start,
}
}
}
impl TweenableValue for ColorTween {
fn update(&mut self, progress: f32) {
self.current_value = Color::new(
self.start_value.r + (self.end_value.r - self.start_value.r) * progress,
self.start_value.g + (self.end_value.g - self.start_value.g) * progress,
self.start_value.b + (self.end_value.b - self.start_value.b) * progress,
self.start_value.a + (self.end_value.a - self.start_value.a) * progress,
);
}
fn get_current_value(&self) -> Box<dyn std::any::Any> {
Box::new(self.current_value)
}
}
#[derive(Debug)]
pub struct FloatTween {
start_value: f32,
end_value: f32,
current_value: f32,
}
impl FloatTween {
pub fn new(start: f32, end: f32) -> Self {
Self {
start_value: start,
end_value: end,
current_value: start,
}
}
}
impl TweenableValue for FloatTween {
fn update(&mut self, progress: f32) {
self.current_value = self.start_value + (self.end_value - self.start_value) * progress;
}
fn get_current_value(&self) -> Box<dyn std::any::Any> {
Box::new(self.current_value)
}
}
#[derive(Debug, Clone, Copy)]
pub enum EasingFunction {
Linear,
EaseIn,
EaseOut,
EaseInOut,
EaseInSine,
EaseOutSine,
EaseInOutSine,
EaseInQuad,
EaseOutQuad,
EaseInOutQuad,
EaseInCubic,
EaseOutCubic,
EaseInOutCubic,
EaseInQuart,
EaseOutQuart,
EaseInOutQuart,
EaseInQuint,
EaseOutQuint,
EaseInOutQuint,
EaseInExpo,
EaseOutExpo,
EaseInOutExpo,
EaseInCirc,
EaseOutCirc,
EaseInOutCirc,
EaseInBack,
EaseOutBack,
EaseInOutBack,
EaseInElastic,
EaseOutElastic,
EaseInOutElastic,
EaseInBounce,
EaseOutBounce,
EaseInOutBounce,
}
impl EasingFunction {
pub fn apply(self, t: f32) -> f32 {
let t = t.clamp(0.0, 1.0);
match self {
Self::Linear => t,
Self::EaseIn => t * t,
Self::EaseOut => 1.0 - (1.0 - t) * (1.0 - t),
Self::EaseInOut => if t < 0.5 { 2.0 * t * t } else { 1.0 - (-2.0 * t + 2.0).powi(2) / 2.0 },
Self::EaseInSine => 1.0 - (t * std::f32::consts::PI / 2.0).cos(),
Self::EaseOutSine => (t * std::f32::consts::PI / 2.0).sin(),
Self::EaseInOutSine => -((std::f32::consts::PI * t).cos() - 1.0) / 2.0,
Self::EaseInQuad => t * t,
Self::EaseOutQuad => 1.0 - (1.0 - t) * (1.0 - t),
Self::EaseInOutQuad => if t < 0.5 { 2.0 * t * t } else { 1.0 - (-2.0 * t + 2.0).powi(2) / 2.0 },
Self::EaseInCubic => t * t * t,
Self::EaseOutCubic => 1.0 - (1.0 - t).powi(3),
Self::EaseInOutCubic => if t < 0.5 { 4.0 * t * t * t } else { 1.0 - (-2.0 * t + 2.0).powi(3) / 2.0 },
Self::EaseInQuart => t.powi(4),
Self::EaseOutQuart => 1.0 - (1.0 - t).powi(4),
Self::EaseInOutQuart => if t < 0.5 { 8.0 * t.powi(4) } else { 1.0 - (-2.0 * t + 2.0).powi(4) / 2.0 },
Self::EaseInQuint => t.powi(5),
Self::EaseOutQuint => 1.0 - (1.0 - t).powi(5),
Self::EaseInOutQuint => if t < 0.5 { 16.0 * t.powi(5) } else { 1.0 - (-2.0 * t + 2.0).powi(5) / 2.0 },
Self::EaseInExpo => if t == 0.0 { 0.0 } else { 2.0_f32.powf(10.0 * (t - 1.0)) },
Self::EaseOutExpo => if t == 1.0 { 1.0 } else { 1.0 - 2.0_f32.powf(-10.0 * t) },
Self::EaseInOutExpo => {
if t == 0.0 { 0.0 }
else if t == 1.0 { 1.0 }
else if t < 0.5 { 2.0_f32.powf(20.0 * t - 10.0) / 2.0 }
else { (2.0 - 2.0_f32.powf(-20.0 * t + 10.0)) / 2.0 }
},
Self::EaseInCirc => 1.0 - (1.0 - t * t).sqrt(),
Self::EaseOutCirc => (1.0 - (t - 1.0) * (t - 1.0)).sqrt(),
Self::EaseInOutCirc => {
if t < 0.5 { (1.0 - (1.0 - (2.0 * t).powi(2)).sqrt()) / 2.0 }
else { ((1.0 - (-2.0 * t + 2.0).powi(2)).sqrt() + 1.0) / 2.0 }
},
Self::EaseInBack => {
let c1 = 1.70158;
let c3 = c1 + 1.0;
c3 * t * t * t - c1 * t * t
},
Self::EaseOutBack => {
let c1 = 1.70158;
let c3 = c1 + 1.0;
1.0 + c3 * (t - 1.0).powi(3) + c1 * (t - 1.0).powi(2)
},
Self::EaseInOutBack => {
let c1 = 1.70158;
let c2 = c1 * 1.525;
if t < 0.5 {
((2.0 * t).powi(2) * ((c2 + 1.0) * 2.0 * t - c2)) / 2.0
} else {
((2.0 * t - 2.0).powi(2) * ((c2 + 1.0) * (t * 2.0 - 2.0) + c2) + 2.0) / 2.0
}
},
Self::EaseInElastic => {
let c4 = (2.0 * std::f32::consts::PI) / 3.0;
if t == 0.0 { 0.0 }
else if t == 1.0 { 1.0 }
else { -2.0_f32.powf(10.0 * t - 10.0) * ((t * 10.0 - 10.75) * c4).sin() }
},
Self::EaseOutElastic => {
let c4 = (2.0 * std::f32::consts::PI) / 3.0;
if t == 0.0 { 0.0 }
else if t == 1.0 { 1.0 }
else { 2.0_f32.powf(-10.0 * t) * ((t * 10.0 - 0.75) * c4).sin() + 1.0 }
},
Self::EaseInOutElastic => {
let c5 = (2.0 * std::f32::consts::PI) / 4.5;
if t == 0.0 { 0.0 }
else if t == 1.0 { 1.0 }
else if t < 0.5 { -(2.0_f32.powf(20.0 * t - 10.0) * ((20.0 * t - 11.125) * c5).sin()) / 2.0 }
else { (2.0_f32.powf(-20.0 * t + 10.0) * ((20.0 * t - 11.125) * c5).sin()) / 2.0 + 1.0 }
},
Self::EaseInBounce => 1.0 - Self::EaseOutBounce.apply(1.0 - t),
Self::EaseOutBounce => {
let n1 = 7.5625;
let d1 = 2.75;
if t < 1.0 / d1 {
n1 * t * t
} else if t < 2.0 / d1 {
n1 * (t - 1.5 / d1) * (t - 1.5 / d1) + 0.75
} else if t < 2.5 / d1 {
n1 * (t - 2.25 / d1) * (t - 2.25 / d1) + 0.9375
} else {
n1 * (t - 2.625 / d1) * (t - 2.625 / d1) + 0.984375
}
},
Self::EaseInOutBounce => {
if t < 0.5 { (1.0 - Self::EaseOutBounce.apply(1.0 - 2.0 * t)) / 2.0 }
else { (1.0 + Self::EaseOutBounce.apply(2.0 * t - 1.0)) / 2.0 }
},
}
}
}
pub struct GlobalAnimation {
pub name: String,
pub duration: f32,
pub elapsed_time: f32,
pub animation_fn: Box<dyn Fn(f32) + Send>,
pub looping: bool,
}
impl std::fmt::Debug for GlobalAnimation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GlobalAnimation")
.field("name", &self.name)
.field("duration", &self.duration)
.field("elapsed_time", &self.elapsed_time)
.field("animation_fn", &"<function>")
.field("looping", &self.looping)
.finish()
}
}
impl GlobalAnimation {
pub fn new<F: Fn(f32) + Send + 'static>(name: String, duration: f32, animation_fn: F) -> Self {
Self {
name,
duration,
elapsed_time: 0.0,
animation_fn: Box::new(animation_fn),
looping: false,
}
}
pub fn update(&mut self, delta_time: f32) {
self.elapsed_time += delta_time;
let progress = (self.elapsed_time / self.duration).clamp(0.0, 1.0);
(self.animation_fn)(progress);
if self.looping && progress >= 1.0 {
self.elapsed_time = 0.0;
}
}
pub fn is_complete(&self) -> bool {
!self.looping && self.elapsed_time >= self.duration
}
}
#[derive(Debug, Clone)]
pub enum AnimationEvent {
Started(EntityId, AnimationSetId, AnimationId),
Finished(EntityId, AnimationSetId, AnimationId),
LoopCompleted(EntityId, AnimationSetId, AnimationId, u32),
FrameChanged(EntityId, AnimationSetId, AnimationId, usize),
KeyframeReached(EntityId, AnimationSetId, AnimationId, String),
}
#[derive(Debug)]
pub struct AnimationBuilder {
name: String,
animation_type: Option<AnimationType>,
duration: f32,
looping: bool,
play_mode: PlayMode,
}
impl AnimationBuilder {
pub fn new(name: String) -> Self {
Self {
name,
animation_type: None,
duration: 1.0,
looping: false,
play_mode: PlayMode::Forward,
}
}
pub fn sprite_animation(mut self, frames: Vec<SpriteFrame>, fps: f32) -> Self {
self.duration = frames.len() as f32 / fps;
self.animation_type = Some(AnimationType::Sprite { frames, fps });
self
}
pub fn transform_animation(mut self, keyframes: Vec<TransformKeyframe>) -> Self {
if let Some(last_kf) = keyframes.last() {
self.duration = last_kf.time;
}
self.animation_type = Some(AnimationType::Transform { keyframes });
self
}
pub fn color_animation(mut self, keyframes: Vec<ColorKeyframe>) -> Self {
if let Some(last_kf) = keyframes.last() {
self.duration = last_kf.time;
}
self.animation_type = Some(AnimationType::Color { keyframes });
self
}
pub fn duration(mut self, duration: f32) -> Self {
self.duration = duration;
self
}
pub fn looping(mut self, looping: bool) -> Self {
self.looping = looping;
self
}
pub fn play_mode(mut self, play_mode: PlayMode) -> Self {
self.play_mode = play_mode;
self
}
pub fn build(self) -> Option<Animation> {
if let Some(animation_type) = self.animation_type {
Some(Animation {
name: self.name,
animation_type,
duration: self.duration,
looping: self.looping,
play_mode: self.play_mode,
metadata: AnimationMetadata::default(),
})
} else {
None
}
}
}
impl Default for AnimationManager {
fn default() -> Self {
Self::new()
}
}