Wiki 9Minecraft Wiki 9Minecraft

Converting Molang Values to Scoreboards via Animation Controllers

Bridge Molang queries and variables into scoreboard objectives using binary decomposition in behaviour packs.

6Sections
2Code examples
4Known errors

Minecraft Bedrock evaluates Molang expressions and slash commands in completely separate environments, meaning you cannot directly pass a Molang query like health or rotation into a `/scoreboard` command string. When your add-on needs to read an entity's internal Molang data inside a command system or function without relying on the Script API, you can bridge the two using a behaviour pack animation controller. By breaking a Molang value down into binary powers of two, an animation controller can reconstruct the exact integer inside a scoreboard objective within a single game tick.

Why Molang and Scoreboards Do Not Mix Directly

In a behaviour pack animation controller, the `on_entry` and `on_exit` arrays accept both slash commands (prefixed with `/`) and raw Molang expressions. New creators often try to combine the two on a single line, writing something like `/scoreboard players set @s health (query.health)`. The command parser does not evaluate Molang; it reads `(query.health)` as a literal string, throws a syntax error because it expects an integer, and fails to run the command.

Going the other way—from a scoreboard into Molang—is only partially supported in modern format versions, and writing from Molang out to a scoreboard has no native syntax at all. Because animation controllers evaluate state transitions using Molang and execute commands when entering those states, the state machine itself has to act as the translator. Instead of creating hundreds of individual states to check if a value equals 1, 2, 3, and so on, you can use binary decomposition inside a single state transition loop.

How Binary Decomposition Works in One Tick

Every positive integer can be represented as the sum of powers of two (1, 2, 4, 8, 16, 32, 64, 128, etc.). If you copy a Molang query into a temporary Molang variable (`variable.temp_val`), you can test whether that variable is greater than or equal to each power of two, starting from the largest power you expect to handle and working down to 1.

However, you cannot run both `/scoreboard players add` and `variable.temp_val = variable.temp_val - 64;` inside the same `on_entry` array if they depend on each other dynamically, because `on_entry` runs everything unconditionally once the state is entered. Instead, you use a chain of states, or better yet, you compute individual bit flags in Molang variables first and then use a cascade of states—or you use the `animations` array on the entity to run sub-controllers in parallel.

The fastest pure-controller method uses one sub-controller per bit, or a linear chain of states that transitions multiple times if needed. Because a single animation controller only takes one transition per tick, a linear chain of 8 states would take 8 ticks to finish. To make the conversion happen instantly within a single tick, we split each binary bit into its own tiny controller inside the same file, all triggered simultaneously by a master variable.

Bit PositionPower of TwoMax Cumulative ScoreTypical Use Case
Bit 011Boolean flags (e.g., query.is_baby, query.is_sneaking)
Bit 0 to Bit 41 to 1631Player hunger, armor points, or small effect amplifiers
Bit 0 to Bit 71 to 128255Standard mob health, rotation buckets, or item stack counts
Bit 0 to Bit 101 to 10242047Boss health pools, world time queries, or relative coordinates

Building a Single-Tick 8-Bit Converter

To convert a value between 0 and 255 in a single tick, we create one preparation controller and eight bit-check controllers inside `BP/animation_controllers/molang_to_score.ac.json`. In the preparation controller, we floor the target Molang query into an integer, store it in `variable.score_input`, and reset the entity's scoreboard objective to 0.

Because Bedrock runs controllers in the order they are listed in the entity's `scripts.animate` array, the preparation controller runs first, followed immediately in the exact same tick by the controllers from `bit_7` (128) down to `bit_0` (1). Each bit controller checks if `variable.score_input` is at least its power of two. If true, it enters its `add` state, adds that power of two to the scoreboard, and subtracts the same amount from `variable.score_input` so the smaller bits only see the remainder.

BP/animation_controllers/molang_to_score.ac.json
{
  "format_version": "1.10.0",
  "animation_controllers": {
    "controller.animation.molang_score.prepare": {
      "initial_state": "idle",
      "states": {
        "idle": {
          "transitions": [
            { "convert": "variable.convert_trigger == 1" }
          ]
        },
        "convert": {
          "on_entry": [
            "variable.score_input = math.clamp(math.floor(query.health), 0, 255);",
            "/scoreboard players set @s hp_score 0"
          ],
          "transitions": [
            { "idle": "1.0" }
          ]
        }
      }
    },
    "controller.animation.molang_score.bit_7": {
      "initial_state": "idle",
      "states": {
        "idle": {
          "transitions": [
            { "add": "variable.convert_trigger == 1 && variable.score_input >= 128" }
          ]
        },
        "add": {
          "on_entry": [
            "variable.score_input = variable.score_input - 128;",
            "/scoreboard players add @s hp_score 128"
          ],
          "transitions": [
            { "idle": "1.0" }
          ]
        }
      }
    },
    "controller.animation.molang_score.bit_6": {
      "initial_state": "idle",
      "states": {
        "idle": {
          "transitions": [
            { "add": "variable.convert_trigger == 1 && variable.score_input >= 64" }
          ]
        },
        "add": {
          "on_entry": [
            "variable.score_input = variable.score_input - 64;",
            "/scoreboard players add @s hp_score 64"
          ],
          "transitions": [
            { "idle": "1.0" }
          ]
        }
      }
    },
    "controller.animation.molang_score.bit_0": {
      "initial_state": "idle",
      "states": {
        "idle": {
          "transitions": [
            { "add": "variable.convert_trigger == 1 && variable.score_input >= 1" }
          ]
        },
        "add": {
          "on_entry": [
            "variable.score_input = variable.score_input - 1;",
            "/scoreboard players add @s hp_score 1",
            "variable.convert_trigger = 0;"
          ],
          "transitions": [
            { "idle": "1.0" }
          ]
        }
      }
    }
  }
}

Wiring the Controllers to the Entity Description

In the code above, `bit_5` through `bit_1` (for values 32, 16, 8, 4, and 2) follow the exact same pattern as `bit_6`. Note that resetting `variable.convert_trigger = 0;` if placed inside `bit_0` would only run when the number is odd. To ensure the trigger resets cleanly whether `bit_0` fires or not, you should either leave `variable.convert_trigger` driven by a continuous condition (such as `query.is_alive`) or add one final cleanup controller at the end of the chain that sets `variable.convert_trigger = 0;` unconditionally.

Inside your entity's behaviour file (for example, `BP/entities/player.json`), you register the controllers in `description.animations` and list them in strict descending order inside `description.scripts.animate`. If you list `bit_0` before `bit_7`, `bit_0` will see that a health of 20 is `>= 1`, subtract 1, and leave 19, ruining the binary math for the rest of the chain.

BP/entities/player.json
{
  "format_version": "1.16.0",
  "minecraft:entity": {
    "description": {
      "identifier": "minecraft:player",
      "is_spawnable": false,
      "is_summonable": false,
      "animations": {
        "score_prep": "controller.animation.molang_score.prepare",
        "score_b7": "controller.animation.molang_score.bit_7",
        "score_b6": "controller.animation.molang_score.bit_6",
        "score_b5": "controller.animation.molang_score.bit_5",
        "score_b4": "controller.animation.molang_score.bit_4",
        "score_b3": "controller.animation.molang_score.bit_3",
        "score_b2": "controller.animation.molang_score.bit_2",
        "score_b1": "controller.animation.molang_score.bit_1",
        "score_b0": "controller.animation.molang_score.bit_0"
      },
      "scripts": {
        "initialize": [
          "variable.convert_trigger = 0;",
          "variable.score_input = 0;"
        ],
        "animate": [
          "score_prep",
          "score_b7",
          "score_b6",
          "score_b5",
          "score_b4",
          "score_b3",
          "score_b2",
          "score_b1",
          "score_b0"
        ]
      }
    },
    "components": {}
  }
}

Handling Negative Numbers and Decimals

Scoreboards in Minecraft Bedrock only store 32-bit signed integers; they cannot hold floating-point decimals. When converting a decimal query like `query.head_x_rotation` or `query.modified_move_speed`, multiply the value by 10 or 100 before calling `math.floor()` in the preparation step. For example, `math.floor(query.modified_move_speed * 100)` turns `0.75` into `75`, preserving two decimal places of precision inside your integer scoreboard.

If the Molang query can return negative numbers (such as pitch rotation or relative coordinates), you have two options. The simplest is adding a constant offset before decomposing—for example, adding 180 to a rotation query that ranges from -180 to 180 so the input is always between 0 and 360, then subtracting 180 from the scoreboard at the very end using `/scoreboard players remove @s rot_score 180`. Alternatively, you can add a sign-check controller before the bit controllers that tests if the value is `< 0`, multiplies `variable.score_input` by `-1`, and sets a flag so you know the resulting score is negative.

Performance and When to Use the Script API Instead

Every time a bit controller enters its `add` state, it transitions back to `idle` on the following tick. That means a continuous conversion can only run once every two ticks (10 times per second) because the state machine has to spend one tick in `add` and one tick returning to `idle` before it can fire `on_entry` again. If you truly need 20-ticks-per-second updates, you must replaces the `idle` state with two alternating active states (`add_a` and `add_b`) that bounce back and forth every tick while the condition holds.

Executing up to nine `/scoreboard` commands per entity every couple of ticks carries noticeable overhead if applied to dozens of mobs at once. Limit this technique to players, boss entities, or on-demand triggers (such as converting the score only when an entity takes damage or interacts). If your add-on already uses a `manifest.json` with a `@minecraft/server` script module, reading an entity property or rotation in JavaScript and calling `ScoreboardObjective.setScore()` is much cleaner and avoids command execution overhead altogether.

Which versions this applies to

Applies to all modern Bedrock versions supporting behaviour pack animation controllers (format_version 1.10.0 for animation controllers and 1.16.0+ for entity scripts.animate). No experimental toggles are required.

When it does not work

What you seeWhyThe fix
Content Log Error: [Animation][error]-controller.animation.molang_score.prepare | Failed to parse command: /scoreboard players set @s hp_score (query.health)You placed a Molang expression directly inside a slash command string in `on_entry`. The command system cannot evaluate Molang variables or queries.Use integer literals in `/scoreboard` commands and let the animation controller's state transitions evaluate the Molang logic using binary powers of two.
The scoreboard value only updates once and gets stuck, or updates at half the expected speed.An animation controller state only runs `on_entry` when transitioning into the state from a different state. If the condition remains true and there is no transition back to `idle` (or to an alternating state), `on_entry` will never fire a second time.Ensure every `add` state has an unconditional transition (`"1.0"`) back to `idle`, or bounce between two identical `add_a` and `add_b` states if you need updates on consecutive ticks.
The scoreboard objective remains empty or throws 'No objective was found by the name of hp_score' in chat.The `/scoreboard players set` and `add` commands inside the controller will fail silently if the dummy objective has not been created in the world yet.Run `/scoreboard objectives add hp_score dummy` in a setup function (`tick.json` or an entity's `on_entry` initialization state) before running the conversion controllers.
The resulting score is completely wrong (for example, health of 20 converts to 15 or 7).The bit controllers in the entity's `scripts.animate` array are listed in ascending order (1 to 128) or out of order instead of strict descending order (128 down to 1).Order the controllers in `description.scripts.animate` starting from thepreparation controller, then the highest power of two (`bit_7`), all the way down to `bit_0`.

Reference, straight from the game files

Read out of the game itself, so the ids match the version you are building for.