-
-
Notifications
You must be signed in to change notification settings - Fork 162
The Animation Controller (Geckolib4)
The AnimationController is the class that handles animations for GeckoLib animatables.
Its purpose is to give users an entrypoint to managing animations for an animatable instance, primarily doing so through the use of its AnimationStateHandler.
Each AnimationController handles ONE animation at a time, and each animatable may have one or more controllers, allowing for multiple simultaneous animations to be played concurrently.
- How it works
- Multiple Controllers
- Controller Settings
- Triggered Animations
- The AnimationStateHandler
- Examples
- DefaultAnimations
When you create an Animatable, you will be prompted to add registerControllers to your class. This is your space to add as many controllers as you need to add for your animating purposes.
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
// Register your controllers here
}Note
If your animatable does not have any animations, you do not need to register any controllers.
You can then register a controller to the registrar, setting it up however you want to.
E.G.
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "Walking", 0, state -> {
return state.isMoving() ? state.setAndContinue(DefaultAnimations.WALK) : PlayState.STOP;
}));
}When registering multiple controllers to a single animatable, the order of the registration can be important. GeckoLib runs all controllers in the order they are registered in, which means that animations that are handled by later-registered controllers override earlier-registered ones.
For this reason, it is recommended to register 'broad' animations first (such as walking, idling, etc), then registering more specific animations later.
As an example, if we register a walk/idle controller first, then an attack controller second, then the attack controller will be able to swing the entity's arm while it is still walking, blending the two animations together.
You may notice when constructing your AnimationController, there are a few parameters to pass in. There are also some additional settings you may set on your controller(s) at any time.
These are the parameters the various constructors of AnimationController takes when creating one.
-
animatable- This is the animatable instance this controller is for (E.G. Your entity) -
name- This is the name of theAnimationController. Each controller in an animatable should have a unique name, ideally related to what the controller's animations are (E.G. "Walking" for a walking-animation controller) -
transitionTickTime- This is the number of game ticks that the controller should use to interpolate between two animations (or when starting an animation). This can act to smooth out movement, but may also cause your animation's timing to look off. It's recommended to leave this as default, but feel free to experiment. -
animationHandler- This is the predicate that you as a user create to determine what animation(s) the controller should be playing, and when. See the AnimationStateHandler section for more information.
These are additional methods you can add to your controller when registering it to apply various properties or settings.
-
setSoundKeyframeHandler- Register a sound keyframe handler for this controller. See the relevant wiki page for more info -
setParticleKeyframeHandler- Register a particle keyframe handler for this controller. See the relevant wiki page for more info -
setCustomInstructionKeyframeHandler- Register a custom instruction keyframe handler for this controller. See the relevant wiki page for more info -
setAnimationSpeed- Override how fast this controller's animations run. A value of2runs animations twice as fast, etc. -
setAnimationSpeedHandler- A more customisable version ofsetAnimationSpeed -
setOverrideEasingType- Force an override ofEasingTypefor all animations on this controller. -
setOverrideEasingTypeFunction- A more customisable version ofsetOverrideEasingType. -
receiveTriggeredAnimations- Add a triggered animation to this controller. See the Triggered Animations section for more info.
AnimationControllers can have zero or more "triggered" animations registered to it. For more information on how to implement and use triggered animations, see the relevant wiki page
When a triggered animation is triggered on a controller, the controller will skip the AnimationState check entirely while the triggered animation plays. Once the triggered animation finishes, it will clear itself and return to checking the AnimationState as normal.
If for some reason you want to manage the AnimationState even when a triggered animation is playing, you may do so by marking your controller with .receiveTriggeredAnimations().
Note
Enabling receiveTriggeredAnimations will cause your controller to be able to interrupt triggered animations, and so your predicate must account for the controller potentially playing a triggered animation.
AnimationController#isPlayingTriggeredAnimation can be used to check this easily.
The AnimationStateHandler is the user's access point into how a controller handles its animations. This is what actually determines what animation a controller is playing, and when.
An AnimationStateHandler is a predicate that takes in an instance of an AnimationState, and returns a PlayState to tell the controller what to do.
Note
The AnimationStateHandler is called every single render frame, allowing you to handle animations in real-time, as fast as physically possible. You should consider it the current 'state' of your controller, and not a place to tell animations to start/stop.
Example 1 - Basic walking
Let's look at this example:
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "Walking", state -> {
return state.isMoving() ? state.setAndContinue(DefaultAnimations.WALK) : PlayState.STOP;
}));
}The above example shows an AnimationController with an AnimationStateHandler that checks if the state of the animatable is that it is currently moving, and if so, tells the controller to ensure its current animation is DefaultAnimations.WALK (a walking animation), or else that the controller should STOP if it is not moving.
This is a basic walking animation controller, defining that a walking animation should be played when the animatable is moving, or that no animation should be played if it is not.
Example 2 - Walking + Running + Idling
Let's look at this example:
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "Walk/Run/Idle", state -> {
if (state.isMoving())
return state.setAndContinue(MyEntity.this.isSprinting() ? DefaultAnimations.RUN : DefaultAnimations.WALK);
return state.setAndContinue(DefaultAnimations.IDLE);
}));
}This example shows a more advanced walking controller. In this one, we first check if the animatable is moving. If so, we then check if our entity is sprinting (which uses the vanilla sprinting flag, but other conditions can be used). If it is sprinting, we tell it to ensure it's animating with DefaultAnimations.RUN (a running animation), or otherwise just WALK.
If it is not moving at all, we tell the controller to ensure it is just doing an idle animation (DefaultAnimations.IDLE).
Notice that we do not return STOP anywhere in this controller. This is because we always want this controller to be playing an animation. We just use the predicate to determine what animation it should be.
Example 3 - Spawning
Let's look at this example:
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "Spawning", state -> {
if (MyEntity.this.tickCount < 100)
return state.setAndContinue(MY_SPAWN_ANIMATION);
return PlayState.STOP;
}));
}This example shows a less common animation situation. In this example, we see that this entity has a spawning animation, that plays for about 100 ticks (5 seconds) when first joining the world.
To achieve this, we tell the controller that if the entity has only existed for less than 100 ticks, that it should play our custom MY_SPAWN_ANIMATION, or otherwise it should just STOP and play nothing.
Example 4 - Attack animation
Let's look at this example:
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "Attack", state -> {
if (MyEntity.this.swinging)
return state.setAndContinue(DefaultAnimations.ATTACK_SWING);
state.resetCurrentAnimation();
return PlayState.STOP;
}));
}This example makes use of the vanilla entity swinging boolean that AI goals typically use to determine when an entity should be swinging its arm. You can use whatever condition you want to use however in order to achieve this check.
In this example, we check if the entity is currently considered to be swinging. If so, the controller should ensure it is playing the DefaultAnimations.ATTACK_SWING (an attack animation) animation.
If it is not currently swinging, we tell the controller to reset the current animation, so that in the event that the entity stopped swinging before the animation finished, the controller knows to restart again from the start, rather than continuing from where it stopped. Then, we tell it to STOP.
GeckoLib includes a class called DefaultAnimations with it that contains a suite of built-in basic controllers and generic RawAnimations.
It is strongly recommended that you utilise this class as much as practical in order to keep your animation handling consistent and clean.
Using the animations or controllers in DefaultAnimations assumes that you are using the expected standard for animation names in your animation json.
For example, using DefaultAnimations#genericWalkIdleController assumes that you have a walk animation in your animation json called "move.walk", and an idle animation called "misc.idle".
Geckolib 3
Geckolib 4
- Installation
- Getting Started
- Upgrading from GeckoLib 3.1.x to 4.0
- Updating to GeckoLib 4.5
- Basic
- Advanced
- Miscellaneous
Geckolib 5
See the New Wiki!
Package repository hosting is graciously provided by Cloudsmith.
Cloudsmith is the only fully hosted, cloud-native, universal package management solution that enables your organization to create, store and share packages in any format, to any place, with total confidence.