Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commitdd348ac6c3

README.md

BlendSpace2D

2D-coordinate-driven mix of N sample nodes (Node subclass). Each sample is anchored at a (x, y) coordinate; the current parameter (px, py) lands inside one of the Delaunay triangles formed over the anchors, and barycentric weights for that triangle drive a Mixer-style weighted blend. Parameter outside the hull → nearest sample with weight 1.

Exports

  • BlendSpace2D.new(layout: Layout, samples: { Sample }) -> BlendSpace2D — construct a BlendSpace2D with N anchored sample nodes. Triangulation runs once at construction.
  • BlendSpace2D:setParams(x: number, y: number) — set the parameter that drives the per-frame blend.
  • BlendSpace2D:update(dt: number) — cascade update(dt) to every sample source.
  • BlendSpace2D:evaluate() -> TypedBuffer — compute weights and blend. Caller must NOT destroy the returned buffer.
  • BlendSpace2D:destroy() — destroy the internal Mixer and clear state. Sample sources are not owned and not destroyed.

Types:

  • Layout = { boneOrder: { string }, stride: number?, slotLayout: { any }? }
  • Sample = { x: number, y: number, source: any }

Usage

local BlendSpace2D = require("@builtin::systems.anim.AnimGraph.Node.BlendSpace2D")

local bs = BlendSpace2D.new(layout, {
    { x = 0, y = 0, source = idleClip },
    { x = 1, y = 0, source = walkFwd },
    { x = 1, y = 1, source = runFwd  },
})

bs:setParams(0.5, 0.0)
bs:update(dt)
local pose = bs:evaluate()

Notes

  • Triangulation is a brute-force O(n⁴) Delaunay check. Animation blend-spaces are tiny (n ≤ 20 in practice) so the cost is < 1ms in Luau at construction; runtime cost is just triangle containment plus one Mixer evaluate per frame.
  • Internal Mixer is owned by the BlendSpace2D and destroyed on destroy(). Sample sources are owned by the surrounding AnimGraph, not the BlendSpace2D.
  • When the parameter is outside the triangulated hull, the closest sample (by squared XY distance) gets weight 1 and the rest weight 0 — there is no extrapolation.