//! Plays animations from a skinned glTF. use std::{f32::consts::PI, time::Duration}; use bevy::{ animation::{animate_targets, RepeatAnimation}, pbr::CascadeShadowConfigBuilder, prelude::*, }; const FOX_PATH: &str = "models/animated/Fox.glb"; fn main() { App::new() .insert_resource(AmbientLight { color: Color::WHITE, brightness: 2000., }) .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .add_systems(Update, setup_scene_once_loaded.before(animate_targets)) .add_systems(Update, keyboard_animation_control) .run(); } #[derive(Resource)] struct Animations { animations: Vec, #[allow(dead_code)] graph: Handle, } fn setup( mut commands: Commands, asset_server: Res, mut meshes: ResMut>, mut materials: ResMut>, mut graphs: ResMut>, ) { // Build the animation graph let (graph, node_indices) = AnimationGraph::from_clips([ asset_server.load(GltfAssetLabel::Animation(2).from_asset(FOX_PATH)), asset_server.load(GltfAssetLabel::Animation(1).from_asset(FOX_PATH)), asset_server.load(GltfAssetLabel::Animation(0).from_asset(FOX_PATH)), ]); // Insert a resource with the current scene information let graph_handle = graphs.add(graph); commands.insert_resource(Animations { animations: node_indices, graph: graph_handle, }); // Camera commands.spawn(Camera3dBundle { transform: Transform::from_xyz(100.0, 100.0, 150.0) .looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y), ..default() }); // Plane commands.spawn(PbrBundle { mesh: meshes.add(Plane3d::default().mesh().size(500000.0, 500000.0)), material: materials.add(Color::srgb(0.3, 0.5, 0.3)), ..default() }); // Light commands.spawn(( Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), DirectionalLight { shadows_enabled: true, ..default() }, CascadeShadowConfigBuilder { first_cascade_far_bound: 200.0, maximum_distance: 400.0, ..default() } .build(), )); // Fox commands.spawn(SceneBundle { scene: asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH)), ..default() }); println!("Animation controls:"); println!(" - spacebar: play / pause"); println!(" - arrow up / down: speed up / slow down animation playback"); println!(" - arrow left / right: seek backward / forward"); println!(" - digit 1 / 3 / 5: play the animation times"); println!(" - L: loop the animation forever"); println!(" - return: change animation"); } // An `AnimationPlayer` is automatically added to the scene when it's ready. // When the player is added, start the animation. fn setup_scene_once_loaded( mut commands: Commands, animations: Res, mut players: Query<(Entity, &mut AnimationPlayer), Added>, ) { for (entity, mut player) in &mut players { let mut transitions = AnimationTransitions::new(); // Make sure to start the animation via the `AnimationTransitions` // component. The `AnimationTransitions` component wants to manage all // the animations and will get confused if the animations are started // directly via the `AnimationPlayer`. transitions .play(&mut player, animations.animations[0], Duration::ZERO) .repeat(); commands .entity(entity) .insert(animations.graph.clone()) .insert(transitions); } } fn keyboard_animation_control( keyboard_input: Res>, mut animation_players: Query<(&mut AnimationPlayer, &mut AnimationTransitions)>, animations: Res, mut current_animation: Local, ) { for (mut player, mut transitions) in &mut animation_players { let Some((&playing_animation_index, _)) = player.playing_animations().next() else { continue; }; if keyboard_input.just_pressed(KeyCode::Space) { let playing_animation = player.animation_mut(playing_animation_index).unwrap(); if playing_animation.is_paused() { playing_animation.resume(); } else { playing_animation.pause(); } } if keyboard_input.just_pressed(KeyCode::ArrowUp) { let playing_animation = player.animation_mut(playing_animation_index).unwrap(); let speed = playing_animation.speed(); playing_animation.set_speed(speed * 1.2); } if keyboard_input.just_pressed(KeyCode::ArrowDown) { let playing_animation = player.animation_mut(playing_animation_index).unwrap(); let speed = playing_animation.speed(); playing_animation.set_speed(speed * 0.8); } if keyboard_input.just_pressed(KeyCode::ArrowLeft) { let playing_animation = player.animation_mut(playing_animation_index).unwrap(); let elapsed = playing_animation.seek_time(); playing_animation.seek_to(elapsed - 0.1); } if keyboard_input.just_pressed(KeyCode::ArrowRight) { let playing_animation = player.animation_mut(playing_animation_index).unwrap(); let elapsed = playing_animation.seek_time(); playing_animation.seek_to(elapsed + 0.1); } if keyboard_input.just_pressed(KeyCode::Enter) { *current_animation = (*current_animation + 1) % animations.animations.len(); transitions .play( &mut player, animations.animations[*current_animation], Duration::from_millis(250), ) .repeat(); } if keyboard_input.just_pressed(KeyCode::Digit1) { let playing_animation = player.animation_mut(playing_animation_index).unwrap(); playing_animation .set_repeat(RepeatAnimation::Count(1)) .replay(); } if keyboard_input.just_pressed(KeyCode::Digit3) { let playing_animation = player.animation_mut(playing_animation_index).unwrap(); playing_animation .set_repeat(RepeatAnimation::Count(3)) .replay(); } if keyboard_input.just_pressed(KeyCode::Digit5) { let playing_animation = player.animation_mut(playing_animation_index).unwrap(); playing_animation .set_repeat(RepeatAnimation::Count(5)) .replay(); } if keyboard_input.just_pressed(KeyCode::KeyL) { let playing_animation = player.animation_mut(playing_animation_index).unwrap(); playing_animation.set_repeat(RepeatAnimation::Forever); } } }