Resource Pack vs. Behavior Pack Controllers
Bedrock splits animation controllers into two distinct environments: client-side (Resource Pack) and server-side (Behavior Pack). While both share the exact same JSON state machine syntax and format version, they have access to different capabilities and run independently of one another.
Resource Pack controllers live in the RP/animation_controllers/ directory. Their primary job is to play bone animations, trigger client-side particle effects, and play sounds that are defined in the client entity file. Because they run on the client, they only have access to visual and synced entity properties. They cannot change gameplay states, run slash commands, or trigger entity component groups.
Behavior Pack controllers live in the BP/animation_controllers/ directory. Despite the name, they have nothing to do with visual bone movement. Instead, developers use them as lightweight logic engines to run slash commands, trigger entity events, or play server-side timelines when specific Molang conditions are met. They have full access to server-side queries, making them ideal for detecting health changes, combat states, or equipment updates.
Core Anatomy of a Controller
An animation controller file starts with a format_version (always "1.10.0" for controllers) and the root object "animation_controllers". Inside this object, you can define one or more named controllers. It is best practice to prefix your controller identifier with "controller.animation." followed by your entity name and the controller's purpose.
Inside each controller, you define an "initial_state" and a "states" object. When the entity spawns or reloads, the controller immediately enters the state named in "initial_state". If you omit "initial_state", the game defaults to looking for a state literally named "default".
Each state is an object that dictates what happens while the entity is in that state, as well as the rules for leaving it. A controller can only be in one state at a time per instance, though an entity can run multiple separate controllers simultaneously (for example, one controller handling leg movement and another handling arm attacks).
| Property | Pack Type | What it does |
|---|---|---|
| initial_state | Both | Specifies the exact name of the state the controller starts in. Defaults to 'default' if omitted. |
| animations | Both | An array of animation short-names to play while this state is active. Supports conditional objects and blend weights. |
| transitions | Both | An array of objects mapping target state names to Molang expressions. Evaluated in order from top to bottom. |
| blend_transition | RP Only | A float value in seconds that smoothly cross-fades bone transforms when leaving this state for another. |
| particle_effects | RP Only | Triggers client-side particles defined in the client entity file when the state is entered. |
| sound_effects | RP Only | Plays sound short-names defined in the client entity file when the state is entered. |
| on_entry | BP Only | An array of slash commands, entity events (prefixed with @s), or Molang expressions executed once upon entering the state. |
| on_exit | BP Only | An array of slash commands, entity events, or Molang expressions executed once right before leaving the state. |
Client-Side Visual Controller Example
The most common use for a Resource Pack controller is switching between idle and moving animations. In the example below, the controller starts in the "idle" state and plays the "idle_pose" animation. Every frame, it checks the "transitions" array to see if "query.ground_speed" is greater than 0.1.
When the entity starts moving fast enough, the Molang condition evaluates to true (1.0), and the controller transitions to the "walk" state. Notice the "blend_transition" property set to 0.2. This tells the client to spend 0.2 seconds smoothly interpolating the bone rotations from the idle pose into the walking animation, preventing harsh visual snapping.
Once in the "walk" state, the "walk_cycle" animation loops continuously until the entity slows down. When "query.ground_speed" drops below or equal to 0.1, the transition back to "idle" fires.
{
"format_version": "1.10.0",
"animation_controllers": {
"controller.animation.custom_mob.move": {
"initial_state": "idle",
"states": {
"idle": {
"animations": [
"idle_pose"
],
"transitions": [
{
"walk": "query.ground_speed > 0.1"
}
],
"blend_transition": 0.2
},
"walk": {
"animations": [
"walk_cycle"
],
"transitions": [
{
"idle": "query.ground_speed <= 0.1"
}
],
"blend_transition": 0.2
}
}
}
}
}Server-Side Logic Controller Example
Behavior Pack controllers are indispensable for executing commands or triggering entity events based on live gameplay data. Because "on_entry" only fires once when the state changes, you often pair two states together to create a reset loop or a toggle.
In this server-side example, the controller monitors whether the entity is in water. It begins in the "dry" state. The moment "query.is_in_water" returns true, the controller shifts to the "wet" state and executes the "on_entry" array.
Inside "on_entry", strings starting with a forward slash are run as standard Minecraft commands by the entity. Strings starting with "@s " fire an entity event defined in the Behavior Pack entity file's "events" block. Any other string is evaluated as raw Molang, allowing you to set "variable." values on the fly.
{
"format_version": "1.10.0",
"animation_controllers": {
"controller.animation.custom_mob.water_check": {
"initial_state": "dry",
"states": {
"dry": {
"transitions": [
{
"wet": "query.is_in_water"
}
]
},
"wet": {
"on_entry": [
"/playsound random.fizz @a[r=10]",
"@s custom:become_waterlogged",
"variable.times_wet = (variable.times_wet ?? 0) + 1;"
],
"on_exit": [
"@s custom:dry_out"
],
"transitions": [
{
"dry": "!query.is_in_water"
}
]
}
}
}
}
}Linking Controllers to an Entity
Creating the controller file does nothing on its own; you must register and run it inside your entity definition. The registration syntax is identical for both the client entity (RP) and the server entity (BP) in modern format versions.
First, inside the "description" block under "animations", assign a short-name to your controller's full identifier. You mix both regular animations and animation controllers in this same "animations" object. Second, inside the "scripts" block under "animate", list the short-name of your controller so the game knows to actively evaluate it every tick.
Remember that any animation short-names referenced inside a controller (such as "idle_pose" and "walk_cycle" from the earlier example) must also be defined in that same entity file's "animations" block. The controller itself does not know the full "animation.custom_mob.idle" identifier; it only looks up the short-name on the entity running it.
{
"format_version": "1.10.0",
"minecraft:client_entity": {
"description": {
"identifier": "custom:mob",
"materials": {
"default": "entity_alphatest"
},
"textures": {
"default": "textures/entity/custom_mob"
},
"geometry": {
"default": "geometry.custom_mob"
},
"animations": {
"idle_pose": "animation.custom_mob.idle",
"walk_cycle": "animation.custom_mob.walk",
"move_controller": "controller.animation.custom_mob.move"
},
"scripts": {
"animate": [
"move_controller"
]
},
"render_controllers": [
"controller.render.default"
]
}
}
}Waiting for Animations to Finish
A very common pattern in animation controllers is playing a one-shot action—like an attack swing, a jump, or a roar—and returning to the idle state only after the animation finishes playing. Bedrock provides two dedicated Molang queries for this exact scenario: "query.any_animation_finished" and "query.all_animations_finished".
When placed in a state's "transitions" array, "query.all_animations_finished" evaluates to true only when every non-looping animation currently running in that specific state has reached the end of its timeline. If an animation is set to "loop": true in its animation file, it will never finish, and "query.all_animations_finished" will never return true.
This technique works in Behavior Pack controllers as well. If you link a server-side timeline animation to a BP controller state, you can use "query.all_animations_finished" as a reliable server-side timer before transitioning to the next logic state.
Which versions this applies to
Applies to Minecraft Bedrock 1.10.0 and all newer versions. Animation controllers use format_version 1.10.0 in both packs and do not require any experimental toggles.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Content Log Error: [Animation][error]-controller.animation.custom_mob.move | state 'idle' animation 'idle_pose' not found | The controller is trying to play an animation short-name ('idle_pose') that has not been registered in the entity's 'description.animations' object, or there is a typo between the two files. | Open the entity JSON file and ensure the exact short-name used in the controller's 'animations' array is mapped to a valid animation identifier under 'description.animations'. |
| The entity freezes or gets stuck in an attack/action state forever and never transitions back to idle. | The transition back to idle uses 'query.all_animations_finished' or 'query.any_animation_finished', but the animation playing in that state has '"loop": true' set in its animation file. | Open the animation file in RP/animations/ and remove '"loop": true' (or set it to '"hold_on_last_frame"') so the animation actually reaches an end state and triggers the query. |
| Slash commands and '@s' events inside 'on_entry' never execute, with no errors in the Content Log. | 'on_entry' and 'on_exit' are server-side features, meaning they only work in Behavior Pack animation controllers. Placing them in a Resource Pack controller causes the game to silently ignore them. | Move the controller file to BP/animation_controllers/ and register it inside the Behavior Pack entity file (BP/entities/) under 'description.animations' and 'description.scripts.animate'. |
| Game crashes or throws a schema error when loading the world after editing a Behavior Pack controller. | In a Behavior Pack entity file using format_version 1.12.0 or lower, 'scripts.animate' was not supported in the same way, or the controller file itself was given an invalid format_version like 1.20.0. | Keep the animation controller's 'format_version' strictly at '1.10.0', and ensure your Behavior Pack entity file uses a modern 'format_version' (such as '1.16.0' or higher) to support 'description.scripts.animate'. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
