Intro to Animation Controllers

guide

Animation controllers (AC) are state-machines that can be used in both the resource pack, and the behavior pack. In the resource pack, animation controllers (RPAC) are used to play animations, and in the behavior pack (BPAC), they are used to play commands, and command "animations".

What are State Machines?

State machines are a special kind of logic management, that relies on a series of states. Each state has two properties:

  • What to do in the current state
  • How to move to other states

State machines are used all over the place, especially in classical programming. They aren't only found in minecraft! You can learn more about state machines here.

A state machine can only be in one state at a time. When a state machine "runs", you can think of it as moving from state to state, executing the logic inside, and then following transitions to other states.

State Machine example

The reason that state-machines are useful, is they allow us to naturally break up our animations into a logical flow, where each state handles its own animations and its own logic.

For example, imagine you want to animate the spinning blade of a helicopter - but only when on the ground. You have two states:

  • ground state
  • flying state

We can annotate these states with the two pieces of information described above:

  • ground state:
    • play no animation
    • move to flying state if in the air
  • flying state:
    • play flying animation
    • move to ground state if on the ground

Here is the state-machine, visualized as a flow-chart:

In this flowchart, states are represented by rectangles, and arrows represent transitions from one state to another.

Flowcharts are a nice way to visualize multi-state finite-state-machines, is it allows you to follow the logical flow of the animation. Let us look at a more detailed example, which adds a third explode state:

As you can see, states can go to more than one state at once. States can also be dead-ends (since the helicopter is dead, and doesn't need further animation). The branching flow of animation-controllers is a large part of what makes them powerful.

What are Animation Controllers?

Animation Controllers are Minecraft state machines that allow us to play animations and run commands. Animation controllers always go under the animation_controllers folder, in either the RP, or the BP.

Attaching our controller to an entity

Animation controller are defined in their own files, and must be "attached" to entities before they can do anything. To attach an AC into your entity, you must do two things:

  • Define a short-name for the animation controller
  • Run the animation controller via scripts

Here is a sample description, which shows how the AC can first be defined in animations, and then played in scripts/animate.

RP/entity/helicopter.ce.json OR BP/entities/helicopter.se.jsonCopy
json
"description": {
	"identifier": "wiki:helicopter",
	"animations": {
		"blade_controller": "controller.animation.helicopter.blade"
	},
	"scripts": {
		"animate": [
			"blade_controller"
		]
	}
}
1
2
3
4
5
6
7
8
9
10
11

If you want to conditionally play an animation controller, you can supply an optional molang argument. If the argument evaluates to true, the controller will play:

RP/entity/helicopter.ce.json OR BP/entities/helicopter.se.jsonCopy
json
"scripts": {
	"animate": [
		{
			// Only play the blade_controller if the helicopter has a rider.
			"blade_controller": "q.has_rider"
		}
	]
}
1
2
3
4
5
6
7
8

RP Animation Controllers

RP animation controllers go in the RP, and can be attached to RP entities. They allow you to play bone-animations.

BP Animation Controllers

BP animation controllers go in the BP, and can be attached to BP entities. They allow you to play commands and send events to entities.

Animation Controller example

Lets look at a simple animation controller from our State Machine example above:

Simple Example

RP/animation_controllers/helicopter.ac.jsonCopy
json
{
	"format_version": "1.10.0",
	"animation_controllers": {
		"controller.animation.helicopter.blade": {
			"initial_state": "ground",
			"states": {
				"ground": {
					"transitions": [
						{
							"flying": "!q.is_on_ground"
						}
					]
				},
				"flying": {
					"animations": ["flying"],
					"transitions": [
						{
							"ground": "q.is_on_ground"
						}
					]
				}
			}
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

There is... a lot going on here. Lets break it down, step by step. As we do so, remember two things:

  • Animation controllers are a list of states
  • Each state contains two pieces of information: What to do in any given state, and How to transition to new states.

So this particular example contains two states:

  • ground
  • flying

You can note that "initial_state": "ground" means that our Animation Controller will begin in the ground state.

RP/animation_controllers/helicopter.ac.json#animation_controllers/controller.animation.helicopter.blade/statesCopy
json
"ground": {
    "transitions": [
        {
            "flying": "!q.is_on_ground"
        }
    ]
}
1
2
3
4
5
6
7

The ground state contains a list of transitions, which is how we get to other states. In this example, the default state is saying: Move to the flying state when q.is_on_ground is NOT true. In other words - start the flying animation when we fly into the air!

RP/animation_controllers/helicopter.ac.json#animation_controllers/controller.animation.helicopter.blade/statesCopy
json
"flying": {
    "animations": [
        "flying"
    ],
    "transitions": [
        {
            "ground": "q.is_on_ground"
        }
    ]
}
1
2
3
4
5
6
7
8
9
10

The flying state also contains a list of transitions. In this case it contains the opposite transition: Move to the ground state when q.is_on_ground is true. In other words - move back to the default state when we land on the ground!

Alongside the transition list, there is also a list of animations to play while inside of this state. In this case, playing the flying animation. This animation will need to be defined in the entity definition file for this entity.

Full Example

Here is the code for the second state machine from above, with three states this time. This example illustrates a few new concepts:

  • States with multiple transitions
  • States with no transitions
RP/animation_controllers/helicopter.ac.jsonCopy
json
{
	"format_version": "1.10.0",
	"animation_controllers": {
		"controller.animation.helicopter.blade": {
			"initial_state": "ground",
			"states": {
				"ground": {
					"transitions": [
						{
							"flying": "!q.is_on_ground"
						},
						{
							"explode": "!q.is_alive"
						}
					]
				},
				"flying": {
					"animations": ["flying"],
					"transitions": [
						{
							"ground": "q.is_on_ground"
						},
						{
							"explode": "!q.is_alive"
						}
					]
				},
				"explode": {
					"animations": ["explode"]
				}
			}
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

RP Animation Controllers

Resource Pack animation controllers can run things like sounds and particles too. Before calling sound or particle in ac, you need to define them in client entity file.

RP/entities/custom_tnt.json#minecraft:client_entity/descriptionCopy
json
"sound_effects": {
    "explosion": "wiki.custom_tnt.explosion" //where wiki.custom_tnt.explosion is a sound defined in sound_definitions just like animation sounds.
},
"particle_effects": {
    "fuse_lit": "wiki:tnt_fuse_lit_particle"
}
1
2
3
4
5
6

And only then you can call them in ac:

RP/animation_controllers/custom_tnt.animation_controllers.json#controller.animation.custom_tntCopy
json
"states":{
    "default":{
        "transitions":[
            {
                "explode_state":"q.mark_variant == 1"
            }
        ]
    },
    "explode_state":{
        "sound_effects":[
            {
                "effect":"explosion"
            }
        ],
		"particle_effects": [
			{
				"effect": "fuse_lit"
				// "locator": "<bone>" Locator can also go here too
			}
		],
        "transitions":[
            {
                "default":"q.mark_variant == 0"
            }
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

Warning! Not every particle works there. If you have problems, consider trying another particle. For example, use one from blaze ac.

BP Animation Controllers

Behavior Pack animation controllers use the same general format as RP Animation Controllers, except instead of triggering animations, they allow you to trigger commands. In general, they introduce two new fields:

  • on_entry: Commands to play when the state is entered
  • on_exit: Commands to play when the state is exited

Commands in this context mean three distinct things:

  • A slash command, such as /say Hello there!
  • An event trigger, on the entity, such as: @s wiki:transform_into_plane
  • An arbitrary molang expression, such as v.tickets += 1;

Here is an example BP animation controller, which exhibits some of this behavior:

BP/animation_controllers/helicopter.ac.jsonCopy
json
{
	"format_version": "1.10.0",
	"animation_controllers": {
		"controller.animation.helicopter.commands": {
			"initial_state": "ground",
			"states": {
				"ground": {
					"on_entry": ["/say I am now in the ground!"],
					"transitions": [
						{
							"flying": "!q.is_on_ground"
						}
					]
				},
				"flying": {
					"on_entry": ["/say I am now in the air!"],
					"transitions": [
						{
							"ground": "q.is_on_ground"
						}
					]
				}
			}
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Animation Controller Flow

Through the examples, hopefully you are starting to get some concept for how animation controller flow works. In this section, I will explain it more explicitly.

Loading

When an entity loads into the world, it will enter the default animation controller state, in each of its attached animation controllers. If no initial_state is defined, the state named default is used. If this is missing, the AC will generate a content log.

When running, the AC will do the following things each tick:

  1. Run any animations in the current state (will loop if set to loop, otherwise it will just play once). Run any commands in on_entry, the state was just entered.
  2. Check all transitions to see if there is any valid transition. Search from the top to the bottom of the list, and move to the first valid transition. If a transition is found, on_exit commands will be played.

Because of the way animation controllers are setup, it will only move from state to state at a MAXIMUM of once per tick.

Resetting

Animation Controllers "reset" when an entity reloads (player join/leave, chunk reload, etc). This means that it will "jump" back to the default state. You should always have logic in your default state that can handle restarting any critical animations.

Notes

You can create variables (and remap their values) in animation controllers too!

json
{
    "format_version": "1.17.30",
    "animation_controllers": {
        "controller.animation.sheep.move": {
            "states": {
                "default": {
                    "variables": {
                        "ground_speed_curve": {
                            "input": "q.ground_speed",
                            "remap_curve": {
                                "0.0": 0.2,
                                "1.0": 0.7
                            }
                        }
                    },
                    "animations": [
                        "wiggle_nose",
                        {
                            "walk": "v.ground_speed_curve"
                        }
                    ]
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Contributors

SirLichMedicalJewel105