Animation Layers¶
Namespace: Brine2D.Animation
Animation layers let you run multiple independent animation tracks on the same entity simultaneously. Each layer has its own SpriteAnimator, AnimationStateMachine, AnimationParameters, and optional blend tree. The base animator is always priority 0; layers are applied in ascending priority order on top of it.
Typical uses:
- Upper-body attack animation on top of a lower-body walk cycle
- Separate facial expression track on a character
- Hit-flash or tint effect layer with an
Additiveblend mode - Driving a different texture atlas per layer
Adding a Layer¶
var layer = animComp.AddLayer("upper_body", priority: 1);
AddLayer throws ArgumentException if a layer with the same name already exists, and ArgumentOutOfRangeException if priority < 1.
Adding clips and playing¶
var slash = new AnimationClip("slash") { PlaybackMode = PlaybackMode.OnceHoldLast };
slash.AddFrame(new SpriteFrame(new Rectangle(0, 64, 48, 48)));
slash.AddFrame(new SpriteFrame(new Rectangle(48, 64, 48, 48)));
layer.Animator.AddAnimation(slash);
layer.Animator.Play("slash");
AnimationLayer Properties¶
| Property | Type | Default | Purpose |
|---|---|---|---|
Name |
string |
— | Human-readable identifier |
Priority |
int |
1 |
Evaluation order; higher values win. Must be ≥ 1 |
Weight |
float |
1.0 |
Blend weight [0, 1]; 1.0 = full effect, 0.0 = no effect |
Mask |
AnimationLayerMask |
Default |
Which SpriteComponent properties this layer writes |
BlendMode |
AnimationLayerBlendMode |
Override |
How values are combined with lower-priority results |
Animator |
SpriteAnimator |
— | The layer's animator |
StateMachine |
AnimationStateMachine |
— | The layer's state machine |
Parameters |
AnimationParameters |
— | Named parameter store for this layer's transitions |
BlendSelector1D |
AnimationBlendSelector1D? |
null |
Optional 1D blend tree for this layer |
BlendSelector2D |
AnimationBlendSelector2D? |
null |
Optional 2D blend tree for this layer |
AnimationLayerMask¶
AnimationLayerMask is a flags enum that controls exactly which SpriteComponent fields the layer is allowed to overwrite each frame.
| Flag | Description |
|---|---|
SourceRect |
The sprite source rectangle |
Origin |
The sprite pivot point |
FlipX |
Horizontal flip |
FlipY |
Vertical flip |
Tint |
Sprite tint colour |
Texture |
The sprite texture reference |
Default |
SourceRect \| Origin — the default for new layers |
All |
All of the above |
// Drive a full swap including texture (e.g. equipment overlay)
layer.Mask = AnimationLayerMask.All;
// Tint-only flash layer
layer.Mask = AnimationLayerMask.Tint;
// Combine flags explicitly
layer.Mask = AnimationLayerMask.SourceRect | AnimationLayerMask.Tint;
Texture flag
Texture is excluded from Default to avoid accidentally clobbering the base sprite texture.
Add it explicitly only when the layer is intended to drive a different atlas.
AnimationLayerBlendMode¶
| Value | Behaviour |
|---|---|
Override |
The layer's values overwrite the existing SpriteComponent values, lerped by Weight. This is the default |
Additive |
Tint RGB channels are added to the existing tint. Alpha is lerped by Weight. Non-tint properties fall back to Override behaviour |
// Hit-flash: briefly add a bright colour on top of the base tint
var flash = animComp.AddLayer("hit_flash", priority: 10);
flash.Mask = AnimationLayerMask.Tint;
flash.BlendMode = AnimationLayerBlendMode.Additive;
flash.Weight = 0f; // start invisible
// Animate the weight from code to pulse the flash
flash.Weight = MathF.Max(0f, flash.Weight - deltaTime * 4f);
Priority and Ordering¶
The base animator is always priority 0. Layers with higher priority are applied last and win. AnimatorComponent keeps the layer list sorted automatically whenever a layer's Priority changes.
var legs = animComp.AddLayer("legs", priority: 1);
var torso = animComp.AddLayer("torso", priority: 2);
var face = animComp.AddLayer("face", priority: 3);
// Change priority at runtime — list re-sorts automatically
torso.Priority = 5;
Removing a Layer¶
bool removed = animComp.RemoveLayer("upper_body"); // disposes the layer
Layer State Machines and Parameters¶
Each layer's StateMachine and Parameters work exactly like the base animator's — see State Machine for the full API. The layer state machine is evaluated independently each frame by AnimationSystem.
var layer = animComp.AddLayer("face", priority: 2);
var p = layer.Parameters;
var sm = layer.StateMachine;
sm.SetDefaultState("face_neutral");
sm.AddAnyTriggerTransition("face_happy", p, "playerScored", canInterrupt: true);
sm.AddOnCompleteTransition("face_happy", "face_neutral");
// Each frame:
if (scored) p.SetTrigger("playerScored");
Blend Trees on Layers¶
Layers support both 1D and 2D blend trees. BlendSelector1D takes priority when both are set.
var layer = animComp.AddLayer("legs", priority: 1);
var sel = new AnimationBlendSelector1D(layer.Animator);
layer.BlendSelector1D = sel;
sel.AddNode(0f, "idle_legs");
sel.AddNode(2f, "walk_legs");
sel.AddNode(5f, "run_legs");
// Update every frame
sel.Value = velocity.Length();
Full Example: Upper/Lower Body Split¶
// Base animator: lower body (walk/run driven by blend tree)
var baseSel = new AnimationBlendSelector1D(animComp.Animator);
animComp.BlendSelector1D = baseSel;
baseSel.AddNode(0f, "idle");
baseSel.AddNode(2f, "walk");
baseSel.AddNode(5f, "run");
// Layer: upper body (attacks as one-shots)
var upper = animComp.AddLayer("upper_body", priority: 1);
upper.Mask = AnimationLayerMask.SourceRect | AnimationLayerMask.Origin;
upper.Animator.AddAnimation(attackClip);
var p = upper.Parameters;
var sm = upper.StateMachine;
sm.AddAnyTriggerTransition("attack_upper", p, "attackPressed", canInterrupt: false);
sm.AddOnCompleteTransition("attack_upper", "idle_upper");
sm.SetDefaultState("idle_upper");
// Each frame
baseSel.Value = speed;
if (attackInput) p.SetTrigger("attackPressed");