diff --git a/Cargo.toml b/Cargo.toml index d649d14573787..729279dc9ebb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1289,7 +1289,7 @@ doc-scrape-examples = true [package.metadata.example.animated_mesh] name = "Animated Mesh" -description = "Plays an animation from a skinned glTF" +description = "Plays an animation on a skinned glTF model of a fox" category = "Animation" wasm = true diff --git a/examples/README.md b/examples/README.md index b1a97eef77fa5..a39043e7c7833 100644 --- a/examples/README.md +++ b/examples/README.md @@ -194,7 +194,7 @@ Example | Description Example | Description --- | --- -[Animated Mesh](../examples/animation/animated_mesh.rs) | Plays an animation from a skinned glTF +[Animated Mesh](../examples/animation/animated_mesh.rs) | Plays an animation on a skinned glTF model of a fox [Animated Mesh Control](../examples/animation/animated_mesh_control.rs) | Plays an animation from a skinned glTF with keyboard controls [Animated Mesh Events](../examples/animation/animated_mesh_events.rs) | Plays an animation from a skinned glTF with events [Animated Transform](../examples/animation/animated_transform.rs) | Create and play an animation defined by code that operates on the `Transform` component diff --git a/examples/animation/animated_mesh.rs b/examples/animation/animated_mesh.rs index 12e8ecc75c263..bd5e4b3f3e721 100644 --- a/examples/animation/animated_mesh.rs +++ b/examples/animation/animated_mesh.rs @@ -1,10 +1,11 @@ -//! Plays animations from a skinned glTF. +//! Plays an animation on a skinned glTF model of a fox. -use std::{f32::consts::PI, time::Duration}; +use std::f32::consts::PI; use bevy::{pbr::CascadeShadowConfigBuilder, prelude::*}; -const FOX_PATH: &str = "models/animated/Fox.glb"; +// An example asset that contains a mesh and animation. +const GLTF_PATH: &str = "models/animated/Fox.glb"; fn main() { App::new() @@ -14,8 +15,9 @@ fn main() { ..default() }) .add_plugins(DefaultPlugins) - .add_systems(Startup, setup) - .add_systems(Update, setup_scene_once_loaded) + .add_systems(Startup, setup_mesh_and_animation) + .add_systems(Startup, setup_camera_and_environment) + .add_systems(Update, play_animation_once_loaded) .run(); } @@ -25,27 +27,61 @@ struct Animations { index: AnimationNodeIndex, } -fn setup( +// Create an animation graph and start loading the mesh and animation. +fn setup_mesh_and_animation( mut commands: Commands, asset_server: Res, - mut meshes: ResMut>, - mut materials: ResMut>, mut graphs: ResMut>, ) { - // Build the animation graph + // Build an animation graph containing a single animation. let (graph, index) = AnimationGraph::from_clip( - // We specifically want the "walk" animation, which is the first one. - asset_server.load(GltfAssetLabel::Animation(0).from_asset(FOX_PATH)), + // We want the "run" animation from our example asset, which has an + // index of two. + asset_server.load(GltfAssetLabel::Animation(2).from_asset(GLTF_PATH)), ); - // Keep our animation graph in a Resource so that it can be inserted onto - // the correct entity once the scene actually loads. + // Keep our animation graph in a Resource so that it can be added to the + // correct entity once the scene loads. let graph_handle = graphs.add(graph); commands.insert_resource(Animations { - graph_handle: graph_handle.clone(), + graph_handle, index, }); + // Tell the engine to start loading our mesh and animation, and then spawn + // them as a scene when ready. + commands.spawn(SceneRoot( + asset_server.load(GltfAssetLabel::Scene(0).from_asset(GLTF_PATH)), + )); +} + +// Detect that the scene is loaded and spawned, then play the animation. +fn play_animation_once_loaded( + mut commands: Commands, + animations: Res, + mut players: Query<(Entity, &mut AnimationPlayer), Added>, +) { + for (entity, mut player) in &mut players { + // Start the animation player and tell it to repeat forever. + // + // If you want to try stopping and switching animations, see the + // `animated_mesh_control.rs` example. + player.play(animations.index).repeat(); + + // Insert the animation graph with our selected animation. This + // connects the animation player to the mesh. + commands + .entity(entity) + .insert(AnimationGraphHandle(animations.graph_handle.clone())); + } +} + +// Spawn a camera and a simple environment with a ground plane and light. +fn setup_camera_and_environment( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { // Camera commands.spawn(( Camera3d::default(), @@ -72,34 +108,4 @@ fn setup( } .build(), )); - - // Fox - commands.spawn(( - SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))), - AnimationGraphHandle(graph_handle), - )); -} - -// Once the scene is loaded, 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.index, Duration::ZERO) - .repeat(); - - commands - .entity(entity) - .insert(transitions) - .insert(AnimationGraphHandle(animations.graph_handle.clone())); - } }