Path: blob/main/examples/animation/animated_mesh_control.rs
6592 views
//! Plays animations from a skinned glTF.12use std::{f32::consts::PI, time::Duration};34use bevy::{animation::RepeatAnimation, light::CascadeShadowConfigBuilder, prelude::*};56const FOX_PATH: &str = "models/animated/Fox.glb";78fn main() {9App::new()10.insert_resource(AmbientLight {11color: Color::WHITE,12brightness: 2000.,13..default()14})15.add_plugins(DefaultPlugins)16.add_systems(Startup, setup)17.add_systems(Update, setup_scene_once_loaded)18.add_systems(Update, keyboard_control)19.run();20}2122#[derive(Resource)]23struct Animations {24animations: Vec<AnimationNodeIndex>,25graph_handle: Handle<AnimationGraph>,26}2728fn setup(29mut commands: Commands,30asset_server: Res<AssetServer>,31mut meshes: ResMut<Assets<Mesh>>,32mut materials: ResMut<Assets<StandardMaterial>>,33mut graphs: ResMut<Assets<AnimationGraph>>,34) {35// Build the animation graph36let (graph, node_indices) = AnimationGraph::from_clips([37asset_server.load(GltfAssetLabel::Animation(2).from_asset(FOX_PATH)),38asset_server.load(GltfAssetLabel::Animation(1).from_asset(FOX_PATH)),39asset_server.load(GltfAssetLabel::Animation(0).from_asset(FOX_PATH)),40]);4142// Keep our animation graph in a Resource so that it can be inserted onto43// the correct entity once the scene actually loads.44let graph_handle = graphs.add(graph);45commands.insert_resource(Animations {46animations: node_indices,47graph_handle,48});4950// Camera51commands.spawn((52Camera3d::default(),53Transform::from_xyz(100.0, 100.0, 150.0).looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),54));5556// Plane57commands.spawn((58Mesh3d(meshes.add(Plane3d::default().mesh().size(500000.0, 500000.0))),59MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),60));6162// Light63commands.spawn((64Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),65DirectionalLight {66shadows_enabled: true,67..default()68},69CascadeShadowConfigBuilder {70first_cascade_far_bound: 200.0,71maximum_distance: 400.0,72..default()73}74.build(),75));7677// Fox78commands.spawn(SceneRoot(79asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH)),80));8182// Instructions8384commands.spawn((85Text::new(concat!(86"space: play / pause\n",87"up / down: playback speed\n",88"left / right: seek\n",89"1-3: play N times\n",90"L: loop forever\n",91"return: change animation\n",92)),93Node {94position_type: PositionType::Absolute,95top: px(12),96left: px(12),97..default()98},99));100}101102// An `AnimationPlayer` is automatically added to the scene when it's ready.103// When the player is added, start the animation.104fn setup_scene_once_loaded(105mut commands: Commands,106animations: Res<Animations>,107mut players: Query<(Entity, &mut AnimationPlayer), Added<AnimationPlayer>>,108) {109for (entity, mut player) in &mut players {110let mut transitions = AnimationTransitions::new();111112// Make sure to start the animation via the `AnimationTransitions`113// component. The `AnimationTransitions` component wants to manage all114// the animations and will get confused if the animations are started115// directly via the `AnimationPlayer`.116transitions117.play(&mut player, animations.animations[0], Duration::ZERO)118.repeat();119120commands121.entity(entity)122.insert(AnimationGraphHandle(animations.graph_handle.clone()))123.insert(transitions);124}125}126127fn keyboard_control(128keyboard_input: Res<ButtonInput<KeyCode>>,129mut animation_players: Query<(&mut AnimationPlayer, &mut AnimationTransitions)>,130animations: Res<Animations>,131mut current_animation: Local<usize>,132) {133for (mut player, mut transitions) in &mut animation_players {134let Some((&playing_animation_index, _)) = player.playing_animations().next() else {135continue;136};137138if keyboard_input.just_pressed(KeyCode::Space) {139let playing_animation = player.animation_mut(playing_animation_index).unwrap();140if playing_animation.is_paused() {141playing_animation.resume();142} else {143playing_animation.pause();144}145}146147if keyboard_input.just_pressed(KeyCode::ArrowUp) {148let playing_animation = player.animation_mut(playing_animation_index).unwrap();149let speed = playing_animation.speed();150playing_animation.set_speed(speed * 1.2);151}152153if keyboard_input.just_pressed(KeyCode::ArrowDown) {154let playing_animation = player.animation_mut(playing_animation_index).unwrap();155let speed = playing_animation.speed();156playing_animation.set_speed(speed * 0.8);157}158159if keyboard_input.just_pressed(KeyCode::ArrowLeft) {160let playing_animation = player.animation_mut(playing_animation_index).unwrap();161let elapsed = playing_animation.seek_time();162playing_animation.seek_to(elapsed - 0.1);163}164165if keyboard_input.just_pressed(KeyCode::ArrowRight) {166let playing_animation = player.animation_mut(playing_animation_index).unwrap();167let elapsed = playing_animation.seek_time();168playing_animation.seek_to(elapsed + 0.1);169}170171if keyboard_input.just_pressed(KeyCode::Enter) {172*current_animation = (*current_animation + 1) % animations.animations.len();173174transitions175.play(176&mut player,177animations.animations[*current_animation],178Duration::from_millis(250),179)180.repeat();181}182183if keyboard_input.just_pressed(KeyCode::Digit1) {184let playing_animation = player.animation_mut(playing_animation_index).unwrap();185playing_animation186.set_repeat(RepeatAnimation::Count(1))187.replay();188}189190if keyboard_input.just_pressed(KeyCode::Digit2) {191let playing_animation = player.animation_mut(playing_animation_index).unwrap();192playing_animation193.set_repeat(RepeatAnimation::Count(2))194.replay();195}196197if keyboard_input.just_pressed(KeyCode::Digit3) {198let playing_animation = player.animation_mut(playing_animation_index).unwrap();199playing_animation200.set_repeat(RepeatAnimation::Count(3))201.replay();202}203204if keyboard_input.just_pressed(KeyCode::KeyL) {205let playing_animation = player.animation_mut(playing_animation_index).unwrap();206playing_animation.set_repeat(RepeatAnimation::Forever);207}208}209}210211212