agentsclimarketplace

Bevy animation

Skill chrisgliddon/bevy-skills/skills/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

Install
npx -y skills add chrisgliddon/bevy-skills --skill bevy-animation

Assembled 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 AnimationGraph in 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 a Curve per frame via EasingCurve + EaseFunction or CubicSegment::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 Name requirement for AnimationTargetId::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

TopicReference
Building blend / additive graphs, AnimationMask, hot-swapreferences/animation-graph.md
Blender → glTF → Bevy import: Name, axis, multi-clip, unsupported extensionsreferences/gltf-import.md
Procedural animation in Update vs FixedUpdate, root-motion extractionreferences/procedural-animation.md
Idle / walk / run blending; AnimationTransitions::play + Bevy States integrationreferences/state-machines.md
#[derive(AnimationEvent)], add_event_to_target, On<E> observer wiringreferences/animation-events.md
AnimatableCurve, EasingCurve, EaseFunction, cubic-bezier, UnevenSampleAutoCurvereferences/curves-and-tweening.md
AnimationPlayer cost, skin buffers, graph complexity, runtime graph swapsreferences/performance.md
Principles — Slow In/Out, Timing, Anticipation → easing + durationreferences/principles-easing-and-timing.md
Principles — Squash & Stretch, Arc, Exaggeration → Transform keyframesreferences/principles-transform-keyframes.md
Principles — Staging, Follow Through, Secondary Action → graph layering + masksreferences/principles-graph-layering.md
Principles — Straight-Ahead vs Pose-to-Pose, Solid Drawing, Appeal → upstream authoringreferences/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_easing and clip duration + ActiveAnimation::set_speed.
  • Transform keyframes — Squash & Stretch, Arc, Exaggeration are AnimatableKeyframeCurve over Transform::scale / ::translation via animated_field!.
  • Graph layering — Staging, Follow Through & Overlapping Action, Secondary Action are AnimationGraph::add_additive_blend + AnimationMask group 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 use AnimationTargetId(Uuid) plus AnimatedBy(Entity); the glTF loader supplies both.
  • Regenerate persisted target IDs. Bevy 0.19 changed the AnimationTargetId calculation algorithm. Serialized target IDs from 0.18 must be recalculated from the hierarchy/name path.
  • gltf_animation is on by default. It's already in bevy = "0.19"'s default features — no opt-in needed unless you ran default-features = false.
  • AnimationTransitions::play_with_transition does NOT exist. The real and only method is play(&mut self, player, node, Duration) -> &mut ActiveAnimation. Chain .set_repeat(RepeatAnimation::Forever) / .set_speed(f32) on the returned value.
  • animated_field! and AnimatableCurve are not in the prelude. Import explicitly from bevy::animation::{animated_field, animation_curves::{AnimatableCurve, AnimatableKeyframeCurve}}.
  • On<AnimationEvent> is not an EntityEvent observer. On<E> derefs to &E (access event fields directly). The firing entity is at trigger.trigger().target (the AnimationEventTrigger::target field renamed from animation_player in 0.18). .target() is not available — that's for EntityEvents.
  • Name is required on bone entities for AnimationTargetId::from_name(&Name) to resolve. The glTF loader sets this; hand-built skeletons must too.
  • AnimationMask bit polarity. A bit set in a node's u64 mask means that node will not animate targets in that group. Register a bone into a group via graph.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 write Transform should run in Update or order explicitly relative to AnimationSystems in PostUpdate.
  • 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 — see references/gltf-import.md.

See also

  • bevy-cameras — camera animation (panning, dolly, focus follow) overlaps with this skill; use the same EaseFunction / EasingCurve toolkit.
  • bevy-core-concepts — Update vs FixedUpdate choice matters for procedural animation; see procedural-animation.md.
  • bevy-ecs-systems — observer wiring patterns (On<E>, add_observer) used for AnimationEvents.
  • bevy-pbr-materials — material-parameter animation via AnimatableCurve targeting StandardMaterial fields (emissive flicker, alpha fades).
  • bevy-custom-assets — AssetLoader patterns if you serialise AnimationGraph to RON (.animgraph.ron) and load it as an asset.
  • bevy-migration-0-18-to-0-19 — persisted target-ID and morph-target asset changes.

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.