Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/tools/scene_viewer/animation_plugin.rs
6598 views
1
//! Control animations of entities in the loaded scene.
2
use std::collections::HashMap;
3
4
use bevy::{animation::AnimationTarget, ecs::entity::EntityHashMap, gltf::Gltf, prelude::*};
5
6
use crate::scene_viewer_plugin::SceneHandle;
7
8
/// Controls animation clips for a unique entity.
9
#[derive(Component)]
10
struct Clips {
11
nodes: Vec<AnimationNodeIndex>,
12
current: usize,
13
}
14
15
impl Clips {
16
fn new(clips: Vec<AnimationNodeIndex>) -> Self {
17
Clips {
18
nodes: clips,
19
current: 0,
20
}
21
}
22
/// # Panics
23
///
24
/// When no clips are present.
25
fn current(&self) -> AnimationNodeIndex {
26
self.nodes[self.current]
27
}
28
fn advance_to_next(&mut self) {
29
self.current = (self.current + 1) % self.nodes.len();
30
}
31
}
32
33
/// Automatically assign [`AnimationClip`]s to [`AnimationPlayer`] and play
34
/// them, if the clips refer to descendants of the animation player (which is
35
/// the common case).
36
fn assign_clips(
37
mut players: Query<&mut AnimationPlayer>,
38
targets: Query<(Entity, &AnimationTarget)>,
39
children: Query<&ChildOf>,
40
scene_handle: Res<SceneHandle>,
41
clips: Res<Assets<AnimationClip>>,
42
gltf_assets: Res<Assets<Gltf>>,
43
assets: Res<AssetServer>,
44
mut graphs: ResMut<Assets<AnimationGraph>>,
45
mut commands: Commands,
46
mut setup: Local<bool>,
47
) {
48
if scene_handle.is_loaded && !*setup {
49
*setup = true;
50
} else {
51
return;
52
}
53
54
let gltf = gltf_assets.get(&scene_handle.gltf_handle).unwrap();
55
let animations = &gltf.animations;
56
if animations.is_empty() {
57
return;
58
}
59
60
let count = animations.len();
61
let plural = if count == 1 { "" } else { "s" };
62
info!("Found {} animation{plural}", animations.len());
63
let names: Vec<_> = gltf.named_animations.keys().collect();
64
info!("Animation names: {names:?}");
65
66
// Map animation target IDs to entities.
67
let animation_target_id_to_entity: HashMap<_, _> = targets
68
.iter()
69
.map(|(entity, target)| (target.id, entity))
70
.collect();
71
72
// Build up a list of all animation clips that belong to each player. A clip
73
// is considered to belong to an animation player if all targets of the clip
74
// refer to entities whose nearest ancestor player is that animation player.
75
76
let mut player_to_graph: EntityHashMap<(AnimationGraph, Vec<AnimationNodeIndex>)> =
77
EntityHashMap::default();
78
79
for (clip_id, clip) in clips.iter() {
80
let mut ancestor_player = None;
81
for target_id in clip.curves().keys() {
82
// If the animation clip refers to entities that aren't present in
83
// the scene, bail.
84
let Some(&target) = animation_target_id_to_entity.get(target_id) else {
85
continue;
86
};
87
88
// Find the nearest ancestor animation player.
89
let mut current = Some(target);
90
while let Some(entity) = current {
91
if players.contains(entity) {
92
match ancestor_player {
93
None => {
94
// If we haven't found a player yet, record the one
95
// we found.
96
ancestor_player = Some(entity);
97
}
98
Some(ancestor) => {
99
// If we have found a player, then make sure it's
100
// the same player we located before.
101
if ancestor != entity {
102
// It's a different player. Bail.
103
ancestor_player = None;
104
break;
105
}
106
}
107
}
108
}
109
110
// Go to the next parent.
111
current = children.get(entity).ok().map(ChildOf::parent);
112
}
113
}
114
115
let Some(ancestor_player) = ancestor_player else {
116
warn!(
117
"Unexpected animation hierarchy for animation clip {}; ignoring.",
118
clip_id
119
);
120
continue;
121
};
122
123
let Some(clip_handle) = assets.get_id_handle(clip_id) else {
124
warn!("Clip {} wasn't loaded.", clip_id);
125
continue;
126
};
127
128
let &mut (ref mut graph, ref mut clip_indices) =
129
player_to_graph.entry(ancestor_player).or_default();
130
let node_index = graph.add_clip(clip_handle, 1.0, graph.root);
131
clip_indices.push(node_index);
132
}
133
134
// Now that we've built up a list of all clips that belong to each player,
135
// package them up into a `Clips` component, play the first such animation,
136
// and add that component to the player.
137
for (player_entity, (graph, clips)) in player_to_graph {
138
let Ok(mut player) = players.get_mut(player_entity) else {
139
warn!("Animation targets referenced a nonexistent player. This shouldn't happen.");
140
continue;
141
};
142
let graph = graphs.add(graph);
143
let animations = Clips::new(clips);
144
player.play(animations.current()).repeat();
145
commands
146
.entity(player_entity)
147
.insert(animations)
148
.insert(AnimationGraphHandle(graph));
149
}
150
}
151
152
fn handle_inputs(
153
keyboard_input: Res<ButtonInput<KeyCode>>,
154
mut animation_player: Query<(&mut AnimationPlayer, &mut Clips, Entity, Option<&Name>)>,
155
) {
156
for (mut player, mut clips, entity, name) in &mut animation_player {
157
let display_entity_name = match name {
158
Some(name) => name.to_string(),
159
None => format!("entity {entity}"),
160
};
161
if keyboard_input.just_pressed(KeyCode::Space) {
162
if player.all_paused() {
163
info!("resuming animations for {display_entity_name}");
164
player.resume_all();
165
} else {
166
info!("pausing animation for {display_entity_name}");
167
player.pause_all();
168
}
169
}
170
if clips.nodes.len() <= 1 {
171
continue;
172
}
173
174
if keyboard_input.just_pressed(KeyCode::Enter) {
175
info!("switching to new animation for {display_entity_name}");
176
177
let resume = !player.all_paused();
178
// set the current animation to its start and pause it to reset to its starting state
179
player.rewind_all().pause_all();
180
181
clips.advance_to_next();
182
let current_clip = clips.current();
183
player.play(current_clip).repeat();
184
if resume {
185
player.resume_all();
186
}
187
}
188
}
189
}
190
191
pub struct AnimationManipulationPlugin;
192
impl Plugin for AnimationManipulationPlugin {
193
fn build(&self, app: &mut App) {
194
app.add_systems(Update, (handle_inputs, assign_clips));
195
}
196
}
197
198