---
title: Animating Bones In-Code
hide_meta: true
---

# How to modify bones in-code to do animations

<Callout variant="info">
    This page covers setting bones directly in `setCustomAnimations`. For most procedural motion, [pose modifiers](./procedural_animation#pose-modifiers) are easier: they can add to a bone every frame without drifting, and can be weighted, faded and limited to certain bones. See [Procedural Animation](./procedural_animation) for the full guide, and [Inverse Kinematics](./inverse_kinematics), [Secondary Motion](./secondary_motion) and [Blend Spaces](./blend_spaces) for the built-in systems.
</Callout>

- Open your AzAnimator class created for the targeted source you wish to do in-code animations for.

- Override the method `setCustomAnimations` and get your bones after the `super` call:

```java
    @Override
    public void setCustomAnimations(Creeper animatable, float partialTicks) {
        super.setCustomAnimations(animatable, partialTicks);
        AzBoneCache boneCache = this.context().boneCache();
        AzBone leftArm = boneCache.getBakedModel().getBoneOrNull("your_bones_name");
    }
```

- Check that the bone isn't `null` (the name must match Blockbench exactly), and you can start changing its rotation, position and scale in code. Rotations are in radians.

## Setting Values Without Drift

`setCustomAnimations` runs after all of your controllers have been applied and layered, so a value you set here replaces the animated value for that frame.

**To fully control a bone**, set it from the bone's default pose:

```java
bone.setRotX(bone.getInitialAzSnapshot().getRotX() + angle);
```

**To add movement on top of the animation**, only add to the bone's current value if an animation actually moved the bone this frame, and fall back to the default pose otherwise:

```java
float base = boneCache.wasRotationAnimatedThisFrame(bone)
    ? bone.getRotX()
    : bone.getInitialAzSnapshot().getRotX();

bone.setRotX(base + angle);
```

<Callout variant="warning">
    Don't write `bone.setRotX(bone.getRotX() + angle)` on its own. If no animation moved the bone this frame, it still holds the value your code set last frame, so the angle is added again every frame and the bone spins away. `wasPositionAnimatedThisFrame` and `wasScaleAnimatedThisFrame` do the same check for position and scale.
</Callout>

To mix whole animations together, layer controllers instead; see the Layering Controllers section of the [Animation Controller Builder](./animation_controller_builder) page.

## Feeding Values to Keyframes Instead

If you'd rather keep the motion in Blockbench and only supply a number from code (a charge level, an aim angle), set a Molang variable on the animatable and read it in the keyframe instead of moving bones yourself:

```java
@Override
protected void applyMolangQueries(Creeper animatable, double animTime, float partialTicks) {
    super.applyMolangQueries(animatable, animTime, partialTicks);
    this.context().molangScope().set("v.swell", animatable.getSwelling(partialTicks));
}
```

A keyframe can then use `v.swell * 30`. Each animatable keeps its own values. See [Adding Custom Molang Queries](../../molang/molang#driving-variable-from-code).

## Example

<Callout variant="info">
    This example swings the arms and legs like a player's while walking. It sets each limb from its default pose, so it works whether or not your animations also move the limbs.
</Callout>

```java
    @Override
    public void setCustomAnimations(Creeper animatable, float partialTicks) {
        super.setCustomAnimations(animatable, partialTicks);
        AzBakedModel model = this.context().boneCache().getBakedModel();

        float walkPosition = animatable.walkAnimation.position(partialTicks) * 0.6662F;
        float walkSpeed = animatable.walkAnimation.speed(partialTicks);

        float armSwing = Mth.cos(walkPosition) * walkSpeed;
        float legSwing = Mth.cos(walkPosition) * 1.4F * walkSpeed;

        // Opposite arms and legs swing together, like the player model.
        swing(model.getBoneOrNull("right_Arm"), -armSwing);
        swing(model.getBoneOrNull("left_Arm"), armSwing);
        swing(model.getBoneOrNull("right_Leg"), legSwing);
        swing(model.getBoneOrNull("left_Leg"), -legSwing);
    }

    private static void swing(AzBone bone, float angle) {
        if (bone != null) {
            bone.setRotX(bone.getInitialAzSnapshot().getRotX() + angle);
        }
    }
```