-
Open your AzAnimator class created for the targeted source you wish to do in-code animations for.
-
Override the method
setCustomAnimationsand get your bones after thesupercall:
@Overridepublic 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:
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:
float base = boneCache.wasRotationAnimatedThisFrame(bone)? bone.getRotX(): bone.getInitialAzSnapshot().getRotX();bone.setRotX(base + angle);
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.
To mix whole animations together, layer controllers instead; see the Layering Controllers section of the 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:
@Overrideprotected 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.
Example
@Overridepublic 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);}}