AzCommand is a highly flexible class for controlling animations in AzureLib by dispatching a sequence of animation-related actions. It simplifies animation management by allowing you to manipulate the animation system at three hierarchical levels: root, controller, and animation. This guide will walk you through the basics of using AzCommand and how to replace the traditional AnimationController approach with it.
The Three Configuration Layers:
- Root: Manages all controllers globally.
- Controller: Configures specific animation controllers.
- Animation: Configures per-animation properties, automatically reverting to the previous state after the animation finishes.
Getting Started with AzCommand
1. Creating an AzCommand
Creating an AzCommand involves specifying the controller name, the animation name, and optionally the playback behavior (e.g., loop, play once). Here's how:
Example:
AzCommand walkCommand = AzCommand.create("Walk", "WALK_ANIMATION");
In this example:
"Walk"is the controller name."WALK_ANIMATION"is the animation to play.- By default, the play behavior is set to
PLAY_ONCE. You can customize it like this:
Example:
AzCommand idleCommand = AzCommand.create("Idle", "IDLE_ANIMATION", AzPlayBehaviors.LOOP);
Advanced Command Creation
1. Using AzCommand.create with Custom Tick Offset
The create command now supports specifying a custom starting tick offset and playback behavior.
Example:
AzCommand spinCommand = AzCommand.create("SpinController", "SPIN_ANIMATION", AzPlayBehaviors.LOOP, 10F);
In this example:
"SpinController": Name of the animation controller."SPIN_ANIMATION": Name of the animation.AzPlayBehaviors.LOOP: Sets the animation playback behavior to loop endlessly.10F: Starts the animation 10 ticks into its animation.
2. Using AzCommand.createSpeed to Set Animation Speed
The createSpeed command extends the functionality of create by allowing you to control the animation playback speed. This is useful for dynamically accelerating or decelerating animations based on specific game scenarios.
Example:
AzCommand runQuicklyCommand = AzCommand.createSpeed("RunController", "RUN_ANIMATION", 1.5F);
In this example:
"RunController": Name of the animation controller."RUN_ANIMATION": Name of the animation to play.1.5F: Playback speed, which increases the animation's speed by 50%.
Advanced Speed and Playback Customization
You can further specify both the playback behavior and animation speed in combination:
Example:
AzCommand slowCrawlCommand = AzCommand.createSpeed("CrawlController", "CRAWL_ANIMATION", AzPlayBehaviors.PLAY_ONCE, 0.5F);
In this example:
AzPlayBehaviors.PLAY_ONCE: The animation plays once without looping.0.5F: Slows the animation to 50% of its normal playback speed.
Combining Tick Offset and Animation Speed
The create family of methods provides full customization by allowing both the starting tick offset and playback speed to be defined simultaneously:
Example:
AzCommand complexCommand = AzCommand.createSpeed("ComplexController", "COMPLEX_ANIMATION", AzPlayBehaviors.PLAY_ONCE, 5F, 2F);
- This command starts the animation at tick 5 and plays it at double speed.
2. Composing Multiple Commands
If you need to merge several commands into one, you can use the compose method. This is helpful for combining animations across multiple controllers:
AzCommand combinedCommand = AzCommand.compose(walkCommand, idleCommand);
All actions from the two commands are merged into a single unified command.
Sending AzCommands
Once you've created an AzCommand, you can dispatch it to an entity, block entity, or item stack using the provided methods.
Send to an Entity
To trigger an animation on an entity, use the sendForEntity method:
walkCommand.sendForEntity(myEntity);
- The command will determine the proper dispatch based on the client or server side.
Send to a Block Entity
To send a command to a BlockEntity:
idleCommand.sendForBlockEntity(myBlockEntity);
This sends the command to all clients tracking the relevant chunk.
Send to an Item/Armor
To animate an item, use the sendForItem method:
combinedCommand.sendForItem(myEntity, myItemStack);
This requires the item stack to have a registered UUID, which is covered in the Item and Armor guides.
Best Practices
- Reuse Commands:
- Define reusable commands for common animations to simplify your rendering logic.
Converting from AnimationController to AzCommand
If you previously managed animations using AnimationController, you can streamline and simplify your code using AzCommand. This approach eliminates the need for directly managing AnimationController states by leveraging AzCommand to dispatch animations dynamically.
Old Approach (Using AnimationController)
@Overridepublic void registerControllers(AnimatableManager.ControllerRegistrar controllers) {controllers.add(new AnimationController<>(this, "Walk", 5, state -> {if (state.isMoving()) {return state.setAndContinue(WALK_ANIMATION);}return state.setAndContinue(IDLE_ANIMATION);}));}
In this example:
- The
AnimationControllerdynamically switches between a walking animation and an idle animation based on whether the entity is moving.
New Approach (Using AzCommand and MoveAnalysis)
With AzCommand, you can dynamically dispatch animation commands based on the entity's state. Instead of continuously managing AnimationController state updates, you simply send the appropriate AzCommand when needed.
To enhance the new AzCommand approach and replace the isMoving logic from the old AnimationController example, you can use the MoveAnalysis utility class. This class provides advanced functionalities, such as detecting horizontal movement and checking whether the entity is on the ground. Here's how you can implement this:
public class ExampleEntity extends Monster {private final AzCommand idleCommand = AzCommand.create("Idle", "IDLE_ANIMATION", AzPlayBehaviors.LOOP);private final AzCommand walkCommand = AzCommand.create("Walk", "WALK_ANIMATION");private final MoveAnalysis moveAnalysis;public ExampleEntity(EntityType<? extends Monster> entityType, Level level) {super(entityType, level);this.moveAnalysis = new MoveAnalysis(this);}@Overridepublic void tick() {super.tick(); // Update base entity behaviormoveAnalysis.update(); // Analyze the entity's movement stateif (this.level().isClientSide) { // Only execute animation logic on the clientboolean isMovingOnGround = moveAnalysis.isMovingHorizontally() && onGround();if (isMovingOnGround) {walkCommand.sendForEntity(this); // Send the walk animation if moving} else {idleCommand.sendForEntity(this); // Otherwise, send the idle animation}}}}
MoveAnalysis: Provides a reliable method to check if the entity is moving horizontally.- Benefits:
- Improves code readability and reusability by moving movement logic into
MoveAnalysis. - Cleanly separates movement detection from animation dispatch.
- Improves code readability and reusability by moving movement logic into