
# 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(),
|
|
));
|
|
}
|