Skip to main content

Layers and Blending

Layers are the foundation of Motix's animation blending system. Every animation belongs to exactly one layer. Layers determine which animations can play simultaneously, how their weights combine, and in what order they are applied to the rig.


What Layers Do

Without layers, only one animation could play at a time. With layers, you can have a locomotion animation on one layer and a weapon animation on another. Both contribute to the final pose independently.

Each layer has:

  • A name, used in animation configs and state definitions
  • An order, which determines evaluation priority
  • A base weight, the default contribution value when active
  • A lerp rate, controlling how quickly weight changes interpolate
  • Optional flags for additive blending and isolation

Defining Layers

Define layers with :Layer(name) on the builder:

:Layer("Base")
:Order(0)
:BaseWeight(1.0)
:LerpRate(8.0)
:Done()

:Layer("UpperBody")
:Order(1)
:BaseWeight(1.0)
:Additive(true)
:LerpRate(6.0)
:Done()

Every animation config must reference a layer that is defined in the same behavior:

:Animation("Run")
:AssetId("rbxassetid://RUN_ID")
:Layer("Base") -- must match a defined layer name
-- ...
:Done()

Layer Order

Order is an integer. Lower values are evaluated first. All orders must be unique per behavior. Duplicate orders cause a build-time validation error.

Order determines which layer takes precedence in blending. A layer at order 1 can override or blend on top of the layer at order 0.

:Layer("Base"):Order(0):Done()         -- evaluated first
:Layer("UpperBody"):Order(1):Done() -- evaluated on top of Base
:Layer("Face"):Order(2):Done() -- evaluated on top of both

Weight and LerpRate

Each layer has a current weight and a target weight. Every frame, the current weight interpolates toward the target at LerpRate units per second.

BaseWeight is the target weight when the layer is active (not suppressed). Setting BaseWeight = 1.0 means the layer contributes fully when active.

LerpRate = 8.0 means the weight can move 8 units per second. A layer at weight 0 takes roughly 0.125 seconds to reach full weight. Set LerpRate = math.huge for an instant snap.

:Layer("Overlay")
:Order(2)
:BaseWeight(1.0)
:LerpRate(math.huge) -- instant weight changes
:Done()

Additive Layers

When Additive = true, the layer composes with lower layers rather than replacing them. Use additive layers for animations that should augment the base pose: upper-body recoil, facial expressions, procedural lean.

:Layer("UpperBody")
:Order(1)
:Additive(true)
:BaseWeight(1.0)
:LerpRate(6.0)
:Done()

Animations assigned to an additive layer should themselves be marked additive:

:Animation("RecoilOverlay")
:AssetId("rbxassetid://RECOIL_ID")
:Layer("UpperBody")
:Additive(true)
:Looped(false)
:Done()

Isolated Layers

When Isolated = true, the layer does not interact with other layers during blending. Isolation is useful for layers that should operate completely independently of the rest of the rig.


State Control Over Layers

States activate and suppress layers. This is the primary way layer weights change at runtime.

When a state is entered:

  • ActiveLayer sets the layer's TargetWeight to its BaseWeight
  • SuppressLayer sets the layer's TargetWeight to 0

Layers not mentioned by either directive are not touched during the transition.

:State("Combat")
:ActiveLayer("Base")
:ActiveLayer("UpperBody")
:SuppressLayer("Face")
-- ...
:Done()

:State("Cutscene")
:ActiveLayer("Base")
:ActiveLayer("Face")
:SuppressLayer("UpperBody")
-- ...
:Done()

When transitioning from "Combat" to "Cutscene", "UpperBody" fades to zero and "Face" fades in to its base weight. "Base" stays at its base weight throughout.


Conflicts Within a Layer

Animations on the same layer compete with each other. Only one non-group animation can play on a given layer at a time. When a new animation requests the same layer as a playing animation, the conflict resolver compares their priorities.

Higher priority wins. Lower priority animations are stopped. If the priorities are equal, the incoming animation replaces the incumbent.

Use exclusive groups when you need finer control over which animations within a layer can replace each other.


A Practical Setup

A typical game character might have three layers:

LayerOrderAdditivePurpose
Base0falseFull-body locomotion: idle, walk, run, jump
UpperBody1trueWeapon animations: aim, fire, reload
Face2trueFacial expressions, lip sync
:Layer("Base"):Order(0):BaseWeight(1.0):LerpRate(8.0):Done()
:Layer("UpperBody"):Order(1):BaseWeight(1.0):Additive(true):LerpRate(6.0):Done()
:Layer("Face"):Order(2):BaseWeight(1.0):Additive(true):LerpRate(4.0):Done()

With this setup, a character can run (Base), hold their weapon ready (UpperBody), and emote (Face) simultaneously, each layer contributing independently to the final pose.