A mob rarely plays just one animation. Our Ruby golem should stand and sway, walk when it moves, and raise its fists when it spots an enemy. An animation controller decides which of those plays right now.
This guide builds on Entity animations. Read that first if you have not made an animation yet.
The files
RP/
animations/
ruby_golem.animation.json
animation_controllers/
ruby_golem.animation_controllers.json
entity/
ruby_golem.entity.jsonThink of it as a set of rooms
A controller is a set of states. Think of each state as a room:
- The mob is always in exactly one room.
- Each room has a list of animations that play while the mob is inside.
- Each room has doors (transitions). A door has a Molang check on it. When the check is true, the mob walks through to the other room.
Our golem has two rooms to start with: idle and walking.
Step 1: idle and walk
Controller files go in the animation_controllers folder of the resource pack:
{
"format_version": "1.10.0",
"animation_controllers": {
"controller.animation.ruby_golem.move": {
"initial_state": "idle",
"states": {
"idle": {
"animations": ["idle"],
"transitions": [
{ "walking": "query.modified_move_speed > 0.1" }
],
"blend_transition": 0.2
},
"walking": {
"animations": ["walk"],
"transitions": [
{ "idle": "query.modified_move_speed < 0.05" }
],
"blend_transition": 0.2
}
}
}
}
}Read it like this:
- The golem starts in
idleand plays theidleanimation. - As soon as its speed goes over 0.1, it moves to
walkingand playswalkinstead. - When it slows below 0.05, it goes back to
idle. - Each switch fades over 0.2 seconds, so the legs do not snap.
The names inside animations are the nicknames from the client entity, not the long animation. names. More on that in Step 2.
Two different numbers on purpose
The way in (> 0.1) and the way out (< 0.05) use different numbers. If they were the same, a golem moving at exactly that speed could flip between rooms every frame and look jittery.
Controller fields
| Field | What it does |
|---|---|
initial_state | The state the mob starts in. Leave it out and the game looks for a state called default. |
states | All the states, by name. |
Controller names start with controller.animation.. A good pattern is controller.animation.<mob>.<job>, like controller.animation.ruby_golem.move.
Inside a state
| Field | What it does |
|---|---|
animations | What plays in this state. Nicknames, or nicknames with a blend. |
transitions | The doors out of this state, checked in order. |
blend_transition | Seconds to fade from this state into the next one. |
blend_via_shortest_path | true makes the fade turn bones the short way round. |
on_entry | Things to run once when the mob enters the state. |
on_exit | Things to run once when the mob leaves the state. |
variables | Values worked out for this state, see below. |
particle_effects | Particles to show while in this state. |
sound_effects | Sounds to play when entering this state. |
Particles and sounds are explained in Animation events.
Transitions
transitions is a list. Each entry has one key (the state to go to) and one Molang check:
"transitions": [
{ "alert": "query.has_target" },
{ "swimming": "query.is_in_water" },
{ "idle": "query.modified_move_speed < 0.05" }
]- The checks run from top to bottom. The first one that is true wins.
- Only one transition happens per frame.
- If none are true, the mob stays where it is.
So put the most important door first. Here, spotting an enemy beats everything else.
Blending inside a state
A state can play several animations at once. Each one can have a blend value, just like in scripts.animate:
"walking": {
"animations": [
"idle",
{ "walk": "query.modified_move_speed" }
]
}A slow golem shows a little walk on top of its sway. A fast one shows the full walk.
Step 2: connect it to the mob
A controller is listed in the client entity's animations map, the same way as an animation. Then you play it from scripts.animate:
"animations": {
"idle": "animation.ruby_golem.idle",
"walk": "animation.ruby_golem.walk",
"fists_up": "animation.ruby_golem.fists_up",
"look_at_target": "animation.common.look_at_target",
"move_controller": "controller.animation.ruby_golem.move"
},
"scripts": {
"animate": [
"look_at_target",
"move_controller"
]
}Notice that idle and walk are not in animate any more. The controller plays them now. If you leave them in, they play twice.
You can run several controllers side by side. Each one keeps its own current state. A common setup is one controller for the legs and body, and another for the arms.
Step 3: an alert state
Let's make the golem raise its fists while it has a target. Add a third state and a door to it from both other states:
"states": {
"idle": {
"animations": ["idle"],
"transitions": [
{ "alert": "query.has_target" },
{ "walking": "query.modified_move_speed > 0.1" }
],
"blend_transition": 0.2
},
"walking": {
"animations": ["walk"],
"transitions": [
{ "alert": "query.has_target" },
{ "idle": "query.modified_move_speed < 0.05" }
],
"blend_transition": 0.2
},
"alert": {
"animations": [
"fists_up",
{ "walk": "query.modified_move_speed" }
],
"transitions": [
{ "idle": "!query.has_target" }
],
"blend_transition": 0.3
}
}In alert the golem keeps its fists up and still walks when it chases you.
Waiting for an animation to finish
Some animations should play once, all the way through, like a roar. Two queries help with that:
| Query | True when |
|---|---|
query.all_animations_finished | Every animation in the state has reached its end |
query.any_animation_finished | At least one of them has reached its end |
"roar": {
"animations": ["roar"],
"transitions": [
{ "alert": "query.all_animations_finished" }
]
}These only work for animations that do not loop. A looping animation never finishes.
on_entry and on_exit
on_entry runs once when the mob walks into a state, on_exit runs once when it leaves. In a resource pack they run Molang, which is handy for setting variables:
"alert": {
"on_entry": ["variable.times_alerted = (variable.times_alerted ?? 0) + 1;"],
"on_exit": ["variable.calm_since = query.life_time;"],
"animations": ["fists_up"],
"transitions": [{ "idle": "!query.has_target" }]
}In a behavior pack the same lists can also run commands and entity events. See Animation events.
State variables
A state can work out its own values with variables. The handy part is remap_curve: it turns one range of numbers into another.
"walking": {
"variables": {
"swing": {
"input": "query.modified_move_speed",
"remap_curve": {
"0.0": 0.3,
"1.0": 1.0
}
}
},
"animations": [{ "walk": "variable.swing" }]
}When the speed is 0, variable.swing is 0.3. When it is 1, it is 1.0. In between, the game draws a straight line. So even a slow golem shows a bit of walk.
When something goes wrong
| What you see | Most likely cause |
|---|---|
| Nothing plays | The controller is in animations but not in scripts.animate |
| An animation plays twice as strong | It is both in scripts.animate and in a controller state |
| The mob gets stuck in a state | No transition out of it is ever true. Check the Molang on its doors |
| A once-only animation never ends | It has "loop": true, so all_animations_finished is never true |
| The mob flickers between two states | The checks in and out overlap. Use two different numbers |
| Content Log says a state is missing | A transition points to a state name with a typo |
More help is in Troubleshooting.
What next
- Choose textures and hide model parts with Render controllers.
- Trigger sounds, particles and commands with Animation events.
- Learn more Molang tricks in the Molang guide.