Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit44fd9e59bd

readme.md

ik

Inverse kinematics: solving a bone chain backwards, from where you want its tip to end up to the rotations that put it there.

An animation clip plays the same joint angles every time it runs. That is what makes it a performance, and it is also its limit — a clip cannot know where the doorknob is, how steep the ground is, or where the player is standing. IK fills that gap. It takes the pose an animation produced and corrects it against the world the character is actually in.

The two tiers

Generic. IKChain, IKLimb, IKLookAt, and IKAim name bones directly. They work on any rig at all — a humanoid, a quadruped, a crane, a tentacle, a desk lamp. Nothing about them assumes a skeleton shape.

crane.component.add("IKChain", {
    rootBone = "boom_base",
    tipBone = "boom_tip",
    target = hookRef,
    solver = "ccd",
})

Humanoid. IKRig resolves everything through the body's canonical roles, so one configuration works on any humanoid mesh regardless of how its bones are named. It drives both legs, both arms, a look-at, and ground adaptation on a single solve pass.

hero.component.add("IKRig", {
    grounding = true,
    rightHandTarget = doorknobRef,
    lookTarget = playerRef,
})

Choosing a solver

SolverUse it forWhy
twoBonearms, legsExact and single-pass. The law of cosines gives the answer outright, so there is nothing to converge.
ccdshort chains that must respect joint limitsEvery step is one bone's rotation, so a limit can be enforced immediately rather than reconciled afterwards.
fabriktentacles, ropes, cables, long spinesWorks in joint positions rather than rotations, which converges far faster on long runs.
aimweapons, turrets, sensorsDrives an orientation rather than a position: it points an axis, it does not reach a point.
lookAtheads and spinesSplits one clamped turn between body and head, so the character turns rather than the neck swivelling.

Finding a rig's bone names

The generic components name bones the way the rig does, and rigs rarely use the word you would guess — the standard humanoid here calls its feet Ankle_L and Ankle_R, with no bone containing "foot" at all. Ask the body rather than guessing:

for _, j in ipairs(skeleton.jointTransforms(bodyId)) do
    print(j.name, j.parent)
end

That also gives each joint's world-space position, which is what you measure against when checking a solve from outside.

IKRig and the role form of IKLimb do not need this — they resolve through canonical roles, which is the point of the humanoid tier.

Every field

IKChain — rootBone, tipBone (strings), target (ref), solver ("ccd" / "fabrik" / "twoBone"), weight, iterations, tolerance.

IKLimb — role ("leftarm" / "rightarm" / "leftleg" / "rightleg") or rootBone + tipBone, target (ref), pole (ref), weight.

IKLookAt — target (ref), rootBone + tipBone (optional; roles are used when absent), axisX / axisY / axisZ (the tip's forward, default +Z), bodyWeight (share of the turn taken below the head), clampAngle (degrees), weight.

IKAim — role or rootBone + tipBone, target (ref), axisX / axisY / axisZ (the tip's aiming direction, default +Z), weight, iterations.

IKRig — leftFootTarget, rightFootTarget, leftHandTarget, rightHandTarget and a …Weight for each; lookTarget, lookWeight, lookClampAngle, lookBodyWeight; grounding, groundingMaxSlope, groundingFootHeight, groundingHalfLife, groundingRayUp, groundingRayDown.

Naming the wrong axis is the classic way to get a mangled pose: the solver faithfully points the axis you named, however strange the result. If a look-at or aim twists the model, check the axis before anything else — qrotvec the tip bone's rotation by each candidate and compare against the direction the model visibly faces.

Targets, weights, and poles

Targets are EntityRefs; bones are named by string. A chain names bones the way the rig does and points at the world by ref.

weight blends against the incoming animation. Zero is an exact pass-through and costs nothing — the solve is skipped entirely. Ramp it to fade IK in and out rather than snapping between the animated pose and the solved one.

A pole steers which way a joint bends. Put one behind the elbow or in front of the knee. Without a pole the bend plane the incoming animation produced is preserved, which is stable but not always the plane you want.

Grounding

IKRig.grounding casts a ray under each foot, moves that foot's target to the surface, tilts it to the normal within groundingMaxSlope, and drops the pelvis far enough that neither leg is asked to over-reach.

An explicit foot target wins over the grounder. The grounder still counts that foot toward the pelvis drop, so planting one foot on a ledge while the other follows the terrain behaves sensibly.

Reach is finite

A limb can only reach as far as its bones are long. A target beyond that has no solution, and the chain will extend straight toward it — correct, but it looks like nothing is happening. If a solve seems inert, measure the distance from the chain's root to the target against chainState().reach before looking anywhere else.

Checking a solve

Every component exposes a state query, because a screenshot cannot distinguish a hand that is tracking a target from a hand that happens to be near one:

local s = hero.component.get("IKLimb"):limbState()
-- { ready, boneCount, reach, distanceToTarget, hasPole, weight, error }

status says in words what the component is doing, and is the field to read first when something is not happening. A body spawned from a bundle takes a few frames to arrive, so ready is briefly false with status explaining that it is still waiting — which is a different situation from a chain that genuinely could not resolve. error is set only in the latter case.

IKChain:chainState(), IKLookAt:lookState(), IKAim:aimState(), and IKRig:rigState() are the equivalents. lookState().clamped is worth singling out: it is the difference between watching a target and having turned as far as allowed while still facing away.

Demos

demos/ik_showcase has four runnable scenes: ik_reach (a hand tracking a moving sphere with a weight fade), ik_grounded (feet planting on a staircase and a ramp), ik_lookat (a head tracking an orbiting target into the clamp), and ik_chain (a fox — 25 bones, no humanoid roles at all — with a FABRIK tail and a clamped look-at).

Notes

  • IK layers onto whatever animation graph is driving a body. A body nothing animates gets a graph built around its rest pose, so IK reaches it the same way; if an animator publishes its own graph later, IK moves onto it.
  • The solvers run over the pose buffer inside that graph, so the solved pose reaches skinning, bone attachments, colliders, and debug drawing through the path the animation already took.
  • A chain runs forward kinematics over its own bones and the bones above its root, not the whole skeleton. An arm costs a handful of joints, not sixty.