 c96949dabe
			
		
	
	
		c96949dabe
		
			
		
	
	
	
	
		
			
			# Objective Building upon https://github.com/bevyengine/bevy/pull/17191, improve the `animated_mesh` example by removing code, adding comments, and making the example more c&p'able. ## Solution - Split the setup function in two to clarify what the example is demonstrating. - `setup_mesh_and_animation` is the demonstration. - `setup_camera_and_environment` just sets up the example app. - Changed the animation playing to use `AnimationPlayer` directly instead of creating `AnimationTransitions`. - This appears sufficient when only playing a single animation. - Added a comment pointing users to an example of multiple animations. - Changed the animation to be the run cycle. - I think it got accidentally changed to the idle in [#17191](https://github.com/bevyengine/bevy/pull/17191), so this is reverting back to the original. - Note that we can improve it to select the animation by name if [#16529](https://github.com/bevyengine/bevy/pull/16529) lands. - Renamed `FOX_PATH` to a more neutral `GLTF_PATH`. - Updated the example descriptions to mention the fox. - This adds a little character and hints that the example involves character animation. - Removed a seemingly redundant `AnimationGraphHandle` component. - Removed an unnecessary `clone()`. - Added various comments. ## Notes - A draft of this PR was discussed on Discord: https://discord.com/channels/691052431525675048/1326910663972618302/1326920498663133348 - There was discord discussion on whether a component is "inserted onto", "inserted into" or "added to" an entity. - "Added to" is most common in code and docs, and seems best to me. But it awkwardly differs from the name of `EntityCommands::insert`. - This PR prefers "added to". - I plan to follow up this PR with similar changes to the `animated_mesh_control` and `animated_mesh_events` examples. - But I could roll them into this PR if requested. ## Testing `cargo run --example animated_mesh` --------- Co-authored-by: François Mockers <mockersf@gmail.com>
		
			
				
	
	
		
			112 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Plays an animation on a skinned glTF model of a fox.
 | |
| 
 | |
| use std::f32::consts::PI;
 | |
| 
 | |
| use bevy::{pbr::CascadeShadowConfigBuilder, prelude::*};
 | |
| 
 | |
| // An example asset that contains a mesh and animation.
 | |
| const GLTF_PATH: &str = "models/animated/Fox.glb";
 | |
| 
 | |
| fn main() {
 | |
|     App::new()
 | |
|         .insert_resource(AmbientLight {
 | |
|             color: Color::WHITE,
 | |
|             brightness: 2000.,
 | |
|             ..default()
 | |
|         })
 | |
|         .add_plugins(DefaultPlugins)
 | |
|         .add_systems(Startup, setup_mesh_and_animation)
 | |
|         .add_systems(Startup, setup_camera_and_environment)
 | |
|         .add_systems(Update, play_animation_once_loaded)
 | |
|         .run();
 | |
| }
 | |
| 
 | |
| #[derive(Resource)]
 | |
| struct Animations {
 | |
|     graph_handle: Handle<AnimationGraph>,
 | |
|     index: AnimationNodeIndex,
 | |
| }
 | |
| 
 | |
| // Create an animation graph and start loading the mesh and animation.
 | |
| fn setup_mesh_and_animation(
 | |
|     mut commands: Commands,
 | |
|     asset_server: Res<AssetServer>,
 | |
|     mut graphs: ResMut<Assets<AnimationGraph>>,
 | |
| ) {
 | |
|     // Build an animation graph containing a single animation.
 | |
|     let (graph, index) = AnimationGraph::from_clip(
 | |
|         // 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 added to the
 | |
|     // correct entity once the scene loads.
 | |
|     let graph_handle = graphs.add(graph);
 | |
|     commands.insert_resource(Animations {
 | |
|         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<Animations>,
 | |
|     mut players: Query<(Entity, &mut AnimationPlayer), Added<AnimationPlayer>>,
 | |
| ) {
 | |
|     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<Assets<Mesh>>,
 | |
|     mut materials: ResMut<Assets<StandardMaterial>>,
 | |
| ) {
 | |
|     // Camera
 | |
|     commands.spawn((
 | |
|         Camera3d::default(),
 | |
|         Transform::from_xyz(100.0, 100.0, 150.0).looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),
 | |
|     ));
 | |
| 
 | |
|     // Plane
 | |
|     commands.spawn((
 | |
|         Mesh3d(meshes.add(Plane3d::default().mesh().size(500000.0, 500000.0))),
 | |
|         MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
 | |
|     ));
 | |
| 
 | |
|     // 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(),
 | |
|     ));
 | |
| }
 |