Animations
Palladium uses PlayerAnimationLibrary under the hood to animate the player model. Animations are defined in the GeckoLib animation format, which can be authored in Blockbench using the GeckoLib plugin and its GeckoLib Animated Model model type.
Rather than playing a single static animation, most powers need to react to what the player is doing: play an idle pose while standing still, switch to a running cycle when sprinting, or trigger an attack animation when an ability fires. That is what animation controllers are for.
An animation controller is a state machine defined in a JSON file. You declare a set of named states, each playing one animation. Then you define transitions between them: conditions that are checked every tick, causing the controller to switch to a different state when they become true. At any given moment the controller is in exactly one state, playing its assigned animation.
For example, a simple movement controller might have three states: idle, running, and jumping. From idle it transitions to running when the player starts sprinting, and to jumping when they leave the ground. Each of those states can then transition back to idle once the condition no longer holds.
Animation controllers are assigned to a power per layer (movement, idle, or special). Each layer runs independently, so a power can have a movement controller and a special controller active at the same time without them interfering.
Animations
Animation files are placed at:
assets/<namespace>/player_animations/<name>.animation.json
The resource ID of an individual animation is determined by the animation's name inside the file itself, for example an animation named hero_idle in the mypack namespace is referenced as mypack:hero_idle.
Within an animation controller you reference an animation by that ID:
"animation": "mypack:hero_idle"
You can also define named shortcuts in the animations map (see Animation References below) and reference them with a # prefix:
"animation": "#idle"
Animation Controllers
Animation controller files are client-side content placed at:
assets/<namespace>/palladium/animation_controllers/<id>.json
For example, assets/mypack/palladium/animation_controllers/hero_movement.json is referenced as mypack:hero_movement.
Fields
| Field | Type | Default | Description |
|---|---|---|---|
parent | Identifier | Inherit states and animations from another controller. See Inheritance. | |
animations | Map<String, Identifier> | {} | Named animation shortcuts. Keys are used as #name references inside states. |
initial_state | String | The state the controller starts in. | |
blend_transition | Integer (ticks) | 5 | How many ticks to blend between animations when switching states. |
states | Map<String, State> | {} | The states of this controller. Keys are state names. |
States
Each entry in states defines one step in the state machine.
| Field | Type | Default | Description |
|---|---|---|---|
animation | String | palladium:empty | The animation to play while in this state. Accepts a full ID or a #key reference. |
transitions | Map<String, Condition> | {} | Conditions that move the controller to another state. Keys are the target state names. |
Transitions are evaluated in definition order every tick. The first condition that passes triggers an immediate state change.
Minimal example
{
"initial_state": "idle",
"blend_transition": 10,
"states": {
"idle": {
"animation": "mypack:hero_idle",
"transitions": {
"running": "query.is_sprinting() && query.is_on_ground()"
}
},
"running": {
"animation": "mypack:hero_run",
"transitions": {
"idle": "query.is_sprinting() == false || query.is_on_ground() == false"
}
}
}
}
Here is what this controller does, step by step:
initial_state: "idle": when the controller first becomes active, it starts in theidlestate.blend_transition: 10: when switching between states, the two animations are blended together over 10 ticks (half a second) so the transition looks smooth instead of snapping.idlestate: playsmypack:hero_idle. Every tick it checks whether the player is sprinting and on the ground. If both are true, the controller moves to therunningstate.runningstate: playsmypack:hero_run. Every tick it checks whether the player has stopped sprinting or has left the ground. If either is true, the controller moves back toidle.
The result is a controller that smoothly switches between an idle and a running animation based on what the player is doing.
Animation References
The top-level animations map lets you give short names to animation IDs so you can reuse or override them (especially useful with inheritance):
{
"animations": {
"idle": "mypack:hero_idle",
"run": "mypack:hero_run"
},
"initial_state": "idle",
"states": {
"idle": {
"animation": "#idle",
"transitions": { "running": "query.is_sprinting()" }
},
"running": {
"animation": "#run",
"transitions": { "idle": "query.is_sprinting() == false" }
}
}
}
Inheritance
Use parent to inherit all states and animations from another controller. The child's own values override anything from the parent, which is useful for creating variants without duplicating the full state machine.
{
"parent": "mypack:hero_movement",
"animations": {
"run": "mypack:hero_elite_run"
}
}
Here everything is inherited from mypack:hero_movement, but the #run animation reference is swapped out for the elite variant.
Transitions & Conditions
Transitions are a map where each key is a target state name and the value is a Condition. Two formats are supported:
MoLang (string)
A plain string is treated as a MoLang expression. The expression must evaluate to a truthy number (!= 0):
"transitions": {
"running": "query.is_sprinting() && query.is_on_ground()",
"jumping": "query.is_on_ground() == false"
}
Useful MoLang queries for animation controllers:
| Query | Description |
|---|---|
query.is_on_ground() | 1.0 when the player is standing on the ground. |
query.is_sprinting() | 1.0 when the player is sprinting. |
query.is_moving() | 1.0 when the player has non-zero horizontal velocity. |
palladium.any_animation_finished() | 1.0 on the tick the current animation finishes. Use to chain one-shot animations. |
palladium.flight_speed_forward() | Forward flight speed (useful in flight controllers). |
palladium.flight_speed_sideways() | Lateral flight speed. |
palladium.flight_propulsion_scale() | Propulsion scale of the current flight type (e.g. boost). |
math.abs(x) | Absolute value, handy for checking sideways speed. |
Palladium Condition
Any Palladium condition type works as well. Use a JSON object with a "type" field:
"transitions": {
"attacking": {
"type": "palladium:ability_enabled",
"ability": "mypack:hero#attack"
}
}
AND / list conditions
You can pass a list to require multiple conditions simultaneously:
"transitions": {
"sprint_jump": [
"query.is_sprinting()",
"query.is_on_ground() == false"
]
}
Adding Controllers to Powers
Animation controllers are assigned to powers through power renderers. Inside a power renderer file, use the animation_controller field to map one or more layers to animation controller IDs:
{
"animation_controller": {
"movement": "mypack:hero_movement",
"special": "mypack:hero_special"
}
}
The power renderer file must have the same resource ID as the power it belongs to. For example, the power at data/mypack/palladium/power/hero.json is paired with the renderer at assets/mypack/palladium/power_renderers/hero.json. See Power Renderers for more details.
Only the layers you specify are affected. Other layers continue to run whatever the next active power (or the defaults) provide.
If multiple active powers define a controller for the same layer, the controller from the more dominant power takes effect.
Animation Layers
Palladium registers three independent animation layers on the player model that powers can use. Each layer runs its own controller and they stack on top of each other by priority; higher-priority layers win when bones conflict.
| Layer | Key | Priority | Purpose |
|---|---|---|---|
| Movement | movement | 500 | Locomotion animations (walking, running, jumping). |
| Idle | idle | 1000 | Ambient standing / idle animations. |
| Special | special | 2000 | Ability-triggered animations (attacks, transformations, etc.). |
The Special layer automatically shows the third-person player model in first-person view whenever a non-empty animation is playing. This means animations on this layer are visible to the player themselves without any extra setup.
Examples
One-shot ability animation
A common pattern is triggering a single-play animation when an ability is active, then returning to the default state once it finishes. The idle state waits until the ability is enabled, then transitions to attack. Once the attack animation plays through, palladium.any_animation_finished() fires and the controller returns to idle.
{
"initial_state": "idle",
"states": {
"idle": {
"transitions": {
"attack": {
"type": "palladium:ability_enabled",
"ability": "mypack:hero#attack"
}
}
},
"attack": {
"animation": "mypack:hero_attack",
"transitions": {
"idle": "palladium.any_animation_finished()"
}
}
}
}