Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Animation Playback

Live Demo: Dance

Control animation playback through the AnimationPlayer component.

AnimationPlayer Component

#![allow(unused)]
fn main() {
pub struct AnimationPlayer {
    pub clips: Vec<AnimationClip>,
    pub current_clip: Option<usize>,
    pub time: f32,
    pub speed: f32,
    pub looping: bool,
    pub playing: bool,
    pub blend_from_clip: Option<usize>,
    pub blend_factor: f32,
}
}

Basic Playback

#![allow(unused)]
fn main() {
fn play_animation(world: &mut World, entity: Entity, clip_index: usize) {
    if let Some(player) = world.core.get_animation_player_mut(entity) {
        player.play(clip_index);
        player.looping = true;
    }
}
}

Controlling Speed

#![allow(unused)]
fn main() {
fn set_animation_speed(world: &mut World, entity: Entity, speed: f32) {
    if let Some(player) = world.core.get_animation_player_mut(entity) {
        player.speed = speed;  // 1.0 = normal, 0.5 = half speed, 2.0 = double
    }
}
}

Pausing and Resuming

#![allow(unused)]
fn main() {
fn pause_animation(world: &mut World, entity: Entity) {
    if let Some(player) = world.core.get_animation_player_mut(entity) {
        player.pause();
    }
}

fn resume_animation(world: &mut World, entity: Entity) {
    if let Some(player) = world.core.get_animation_player_mut(entity) {
        player.resume();
    }
}
}

Looping

#![allow(unused)]
fn main() {
fn set_looping(world: &mut World, entity: Entity, looping: bool) {
    if let Some(player) = world.core.get_animation_player_mut(entity) {
        player.looping = looping;
    }
}
}

Checking Animation State

#![allow(unused)]
fn main() {
fn is_animation_finished(world: &World, entity: Entity) -> bool {
    if let Some(player) = world.core.get_animation_player(entity) {
        if !player.looping {
            if let Some(index) = player.current_clip {
                let clip = &player.clips[index];
                return player.time >= clip.duration;
            }
        }
    }
    false
}

fn get_animation_progress(world: &World, entity: Entity) -> f32 {
    if let Some(player) = world.core.get_animation_player(entity) {
        if let Some(index) = player.current_clip {
            let clip = &player.clips[index];
            return player.time / clip.duration;
        }
    }
    0.0
}
}

Animation by Name

Find and play animations by name:

#![allow(unused)]
fn main() {
fn play_animation_by_name(world: &mut World, entity: Entity, name: &str) -> bool {
    if let Some(player) = world.core.get_animation_player_mut(entity) {
        for (index, clip) in player.clips.iter().enumerate() {
            if clip.name.to_lowercase().contains(&name.to_lowercase()) {
                player.play(index);
                return true;
            }
        }
    }
    false
}
}

State-Based Animation

Common pattern for character animation:

#![allow(unused)]
fn main() {
#[derive(Clone, Copy, PartialEq)]
enum MovementState {
    Idle,
    Walking,
    Running,
    Jumping,
}

fn update_character_animation(
    world: &mut World,
    entity: Entity,
    state: MovementState,
    indices: &AnimationIndices,
    current: &mut Option<usize>,
) {
    let target = match state {
        MovementState::Idle => indices.idle,
        MovementState::Walking => indices.walk,
        MovementState::Running => indices.run,
        MovementState::Jumping => indices.jump,
    };

    // Only change if different
    if target != *current {
        if let Some(index) = target {
            if let Some(player) = world.core.get_animation_player_mut(entity) {
                player.blend_to(index, 0.2);  // Smooth transition
                *current = Some(index);
            }
        }
    }
}
}

Speed Based on Movement

Match animation speed to movement speed:

#![allow(unused)]
fn main() {
fn sync_animation_to_movement(world: &mut World, entity: Entity, velocity: f32) {
    if let Some(player) = world.core.get_animation_player_mut(entity) {
        // Assuming walk animation matches 3 m/s
        let base_speed = 3.0;
        player.speed = (velocity / base_speed).clamp(0.5, 2.0);
    }
}
}

One-Shot Animations

Play an animation once without looping:

#![allow(unused)]
fn main() {
fn play_once(world: &mut World, entity: Entity, clip_index: usize) {
    if let Some(player) = world.core.get_animation_player_mut(entity) {
        player.play(clip_index);
        player.looping = false;
    }
}
}

Animation Events

Trigger events at specific times:

#![allow(unused)]
fn main() {
fn check_animation_events(world: &World, entity: Entity, event_time: f32) -> bool {
    if let Some(player) = world.core.get_animation_player(entity) {
        let prev_time = player.time - world.resources.window.timing.delta_time * player.speed;
        // Check if we crossed the event time this frame
        prev_time < event_time && player.time >= event_time
    } else {
        false
    }
}

fn footstep_system(world: &mut World, character: Entity, footstep_source: Entity) {
    if check_animation_events(world, character, 0.3) || check_animation_events(world, character, 0.8) {
        if let Some(audio) = world.core.get_audio_source_mut(footstep_source) {
            audio.playing = true;
        }
    }
}
}