Bevy animation
Use when wiring `AnimationPlayer` + `AnimationGraphHandle`, building an `AnimationGraph` from glTF clips, blending state-driven animations with `AnimationTransitions::play`, isolating body parts with `AnimationMask`, firing gameplay sync via `#[derive(AnimationEvent)]` + `On<E>` observers, tweening with `AnimatableCurve` / `EaseFunction` / `CubicSegment::new_bezier_easing`, or applying the 12 Basic Principles of Animation to 3D characters in Bevy 0.19.From its SKILL.md
npx -y skills add chrisgliddon/bevy-skills --skill bevy-animationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 10 stars10 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
9.8 KB, as published. Nobody here has run it
Bevy 0.19 — Animation (graphs, blending, events, 12 principles)
When to use this skill
- Loading a glTF clip and playing it on a character:
AnimationPlayer+AnimationGraphHandle. - Cross-fading between idle / walk / run via
AnimationTransitions::play(player, node, Duration). - Building an
AnimationGraphin code: blend nodes, additive nodes, weights,AnimationMask. - Triggering gameplay events from a clip timeline (footsteps, hitbox activation, VFX cues) with
#[derive(AnimationEvent)]+clip.add_event_to_target(...)+On<MyEvent>observers. - Procedural tweening without an
AnimationClip— sampling aCurveper frame viaEasingCurve+EaseFunctionorCubicSegment::new_bezier_easing. - Applying the 12 Basic Principles of Animation (Thomas & Johnston, 1981) to a Bevy character.
- Importing a Blender rig and finding bones aren't animated — usually the
Namerequirement forAnimationTargetId::from_name.
Canonical end-to-end pattern
Verified against bevy = "0.19" — cargo check clean in bevy-skills-tester/skill-snippets/examples/bevy_animation.rs.
use bevy::{
animation::{
animated_field,
animation_curves::{AnimatableCurve, AnimatableKeyframeCurve},
AnimationEvent, AnimationTargetId,
},
prelude::*,
};
use core::time::Duration;
#[derive(AnimationEvent, Clone)]
struct FootstepEvent { foot: u8 }
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut clips: ResMut<Assets<AnimationClip>>,
mut graphs: ResMut<Assets<AnimationGraph>>,
) {
// 1. Load a glTF clip
let walk: Handle<AnimationClip> =
asset_server.load("models/character.glb#Animation0");
// 2. Build a tiny procedural clip with a sample curve + an event
let bone = AnimationTargetId::from_name(&Name::new("Hips"));
let tween = AnimatableKeyframeCurve::new([
(0.0_f32, Vec3::ZERO),
(0.5, Vec3::new(0.0, 1.0, 0.0)),
(1.0, Vec3::ZERO),
]).expect("strictly-increasing times");
let curve = AnimatableCurve::new(animated_field!(Transform::translation), tween);
let mut proc = AnimationClip::default();
proc.add_curve_to_target(bone, curve);
proc.add_event(0.5, FootstepEvent { foot: 0 });
let proc = clips.add(proc);
// 3. Compose a graph: root → walk + additive(proc, mask=group 0 excluded)
const MASK_GROUP_0_BIT: u64 = 1 << 0;
let mut graph = AnimationGraph::new();
let root = graph.root;
let _walk_node = graph.add_clip(walk, 1.0, root);
let additive = graph.add_additive_blend(0.5, root);
let _proc_node = graph.add_clip_with_mask(proc, MASK_GROUP_0_BIT, 1.0, additive);
// 4. Spawn the player entity (bones come from the loaded glTF scene)
commands.spawn((
Name::new("AnimationRoot"),
AnimationPlayer::default(),
AnimationGraphHandle(graphs.add(graph)),
AnimationTransitions::new(),
));
}
fn start(mut q: Query<(&mut AnimationTransitions, &mut AnimationPlayer), Added<AnimationPlayer>>) {
use bevy::animation::{graph::AnimationNodeIndex, RepeatAnimation};
for (mut tx, mut player) in &mut q {
tx.play(&mut player, AnimationNodeIndex::new(1), Duration::from_millis(250))
.set_repeat(RepeatAnimation::Forever);
}
}
fn on_footstep(trigger: On<FootstepEvent>) {
let foot = trigger.foot; // On<E> derefs to &E
let _entity = trigger.trigger().target; // AnimationEventTrigger::target
let _ = foot;
}
fn main() {
App::new()
.add_plugins(DefaultPlugins) // gltf_animation is a default feature
.add_systems(Startup, setup)
.add_systems(Update, start)
.add_observer(on_footstep)
.run();
}
Topics
| Topic | Reference |
|---|---|
Building blend / additive graphs, AnimationMask, hot-swap | references/animation-graph.md |
Blender → glTF → Bevy import: Name, axis, multi-clip, unsupported extensions | references/gltf-import.md |
Procedural animation in Update vs FixedUpdate, root-motion extraction | references/procedural-animation.md |
Idle / walk / run blending; AnimationTransitions::play + Bevy States integration | references/state-machines.md |
#[derive(AnimationEvent)], add_event_to_target, On<E> observer wiring | references/animation-events.md |
AnimatableCurve, EasingCurve, EaseFunction, cubic-bezier, UnevenSampleAutoCurve | references/curves-and-tweening.md |
AnimationPlayer cost, skin buffers, graph complexity, runtime graph swaps | references/performance.md |
| Principles — Slow In/Out, Timing, Anticipation → easing + duration | references/principles-easing-and-timing.md |
Principles — Squash & Stretch, Arc, Exaggeration → Transform keyframes | references/principles-transform-keyframes.md |
| Principles — Staging, Follow Through, Secondary Action → graph layering + masks | references/principles-graph-layering.md |
| Principles — Straight-Ahead vs Pose-to-Pose, Solid Drawing, Appeal → upstream authoring | references/principles-authoring.md |
The 12 Basic Principles of Animation
The four principles-*.md references group Thomas & Johnston's twelve principles by which Bevy primitive they actually exercise. Quick map:
- Easing & Timing — Slow In/Out, Timing, Anticipation map to
EaseFunction/EasingCurve/CubicSegment::new_bezier_easingand clip duration +ActiveAnimation::set_speed. - Transform keyframes — Squash & Stretch, Arc, Exaggeration are
AnimatableKeyframeCurveoverTransform::scale/::translationviaanimated_field!. - Graph layering — Staging, Follow Through & Overlapping Action, Secondary Action are
AnimationGraph::add_additive_blend+AnimationMaskgroup isolation. - Authoring — Straight-Ahead vs Pose-to-Pose, Solid Drawing, Appeal live in your DCC tool (Blender, Maya). Bevy plays whatever you authored.
Gotchas
- Target components are split.
AnimationTarget { id, player }no longer exists. Bone entities useAnimationTargetId(Uuid)plusAnimatedBy(Entity); the glTF loader supplies both. - Regenerate persisted target IDs. Bevy 0.19 changed the
AnimationTargetIdcalculation algorithm. Serialized target IDs from 0.18 must be recalculated from the hierarchy/name path. gltf_animationis on by default. It's already inbevy = "0.19"'s default features — no opt-in needed unless you randefault-features = false.AnimationTransitions::play_with_transitiondoes NOT exist. The real and only method isplay(&mut self, player, node, Duration) -> &mut ActiveAnimation. Chain.set_repeat(RepeatAnimation::Forever)/.set_speed(f32)on the returned value.animated_field!andAnimatableCurveare not in the prelude. Import explicitly frombevy::animation::{animated_field, animation_curves::{AnimatableCurve, AnimatableKeyframeCurve}}.On<AnimationEvent>is not anEntityEventobserver.On<E>derefs to&E(access event fields directly). The firing entity is attrigger.trigger().target(theAnimationEventTrigger::targetfield renamed fromanimation_playerin 0.18)..target()is not available — that's forEntityEvents.Nameis required on bone entities forAnimationTargetId::from_name(&Name)to resolve. The glTF loader sets this; hand-built skeletons must too.AnimationMaskbit polarity. A bit set in a node'su64mask means that node will not animate targets in that group. Register a bone into a group viagraph.add_target_to_mask_group(target_id, group_u32). To restrict a node to a single group, set every bit except that group's.- Schedule. Bevy's animation systems run in
PostUpdate, chained.before(TransformSystems::Propagate). Procedural systems that also writeTransformshould run inUpdateor order explicitly relative toAnimationSystemsinPostUpdate. - Out of scope in core animation. No built-in IK, particle/FX systems are
separate, and the glTF loader does not implement
KHR_animation_pointer— seereferences/gltf-import.md.
See also
bevy-cameras— camera animation (panning, dolly, focus follow) overlaps with this skill; use the sameEaseFunction/EasingCurvetoolkit.bevy-core-concepts—UpdatevsFixedUpdatechoice matters for procedural animation; seeprocedural-animation.md.bevy-ecs-systems— observer wiring patterns (On<E>,add_observer) used forAnimationEvents.bevy-pbr-materials— material-parameter animation viaAnimatableCurvetargetingStandardMaterialfields (emissive flicker, alpha fades).bevy-custom-assets—AssetLoaderpatterns if you serialiseAnimationGraphto RON (.animgraph.ron) and load it as an asset.bevy-migration-0-18-to-0-19— persisted target-ID and morph-target asset changes.