Roblox Animation

IKControl: making the character's hand reach an object without animating the arm

Inverse kinematics lets you pose several bones by moving one. IKControl brings that outside the animation editor, at run time — and it has four properties that have to be right.

by Gustavo Cantino9 min readIntermediate

Produced with AI from the sources cited below, and checked by automated verification against those same sources. No person read this text before publication. Editorial Policy

Typographic plate with the Roblox Animation category in large type, the intermediate level below it, and a field of vertical lime green bars on a dark background.

Making a character put their hand on a door handle looks like an animation problem, and it is — until the handle changes height. Then the animation that fitted one door does not fit the other, and the solution becomes one animation per door.

Inverse kinematics solves that by flipping the question. Instead of "what angles do the bones need", it answers "where does the hand need to be" — and works out the angles by itself.

The problem

The official definition is short: inverse kinematics is a common animation technique for making characters move and interact with the environment realistically.

The reason it exists is written down too, and it is honest about the work it replaces: creating realistic movement by hand usually requires many iterations and fine adjustments to several joints. Anyone who has tried posing an arm bone by bone — shoulder, elbow, wrist, and back to the shoulder because the result came out crooked — recognises the description.

The promise is the inversion: with IK, you can pose and animate several parts of the character by adjusting a single object.

The documentation gives three examples, and they cover the space of uses well:

  • Posing the hand while the linked limbs adjust automatically — wrist, elbow and shoulder.
  • Making the feet interact realistically with different surfaces and slopes.
  • Moving a single target object to quickly create realistic interactions with characters and props.

The second is the one that most changes the perception of quality in a game, and the one fewest people implement: a character standing on a ramp with both feet at the same height is one of the things that makes a game look stiff, and almost nobody can name what is wrong.

Step by step

IKControl adds IK to rigs procedurally, outside the animation editor. That is the central point: it works at run time, reacting to the world, not in a recorded animation.

The documentation warns before listing: the mandatory properties have to be right to avoid unexpected and unnatural results. There are four.

Type specifies the control's behaviour type. The common types are Position and Transform. The practical difference: Position makes the effector chase the target's position; Transform chases position and orientation. A hand holding a lever needs to be in the right position and rotated correctly — that is Transform. A hand pointing at a spot is Position.

EndEffector is the rig's BasePart or Bone that chases the target. In the official example, the left hand bone as the effector, to reach a door handle.

Target is the object the effector reaches or points at. And the definition is generous: it can be any object with a world position — a part, an attachment, whatever is there.

ChainRoot defines the chain of parts or bones the control affects. Every part connected between the ChainRoot and the EndEffector is affected.

That last one is what gives fine control over the result, and the documentation's example explains it better than any description: putting the ChainRoot on the left upper arm makes the IK apply to the whole left arm; putting it on the left forearm makes it apply only from the elbow down.

It is a choice of style, not of correctness. A long chain reaches further and moves the shoulder, which can fight the animation that is playing. A short chain preserves the shoulder's pose and reaches less.

The rule of thumb that works: start with the shortest chain that solves the case, and only lengthen it if the reach falls short. Every extra bone in the chain is one more bone the IK can pose in a way you did not anticipate — and it is always the shoulder, always in one specific animation, always after the game is already live.

There is also a consequence that only appears with the rig in motion. A chain that includes the shoulder competes with the torso animation: a character who walks and reaches at the same time has the shoulder pulled from both sides, and the result is a sway that comes from nowhere. A short chain does not have that problem because the shoulder still belongs to the animation.

To test, the documentation suggests a route that needs no script at all: you can add and edit IKControl from a script or directly in the Explorer — and you can even add and change it during a playtest. Start the test, find your character model in the Workspace (it is named after your Roblox account), add an Attachment to the root, position it in front, and create the IKControl on the Humanoid pointing at it.

The artifact

The script below creates the IKControl at run time and makes the right hand reach the nearest tagged object, letting go when it moves away.

Two of its decisions are worth explaining. The first: the control is born switched off. With no target there is nothing to reach, and IK switched on with no target produces an arbitrary pose.

The second is the one that separates IK that looks good from IK that looks like a defect: the weight ramps up gradually. Switching the control on at once makes the arm jump to position, in the same frame. It is the most artificial effect IK can produce, and it is the first thing you notice in a game.

Common mistakes

The visually loudest mistake is the elbow bending the wrong way.

That happens because, mathematically, there is more than one way for the hand to reach the requested point — and nothing in the computation knows that human elbows only bend one way. The result is an arm that reaches the target perfectly and looks broken.

The official solution is constraints: they exist to limit how the joints move while reaching the target, and that is what ensures elbows and knees bend naturally.

But here is the detail that makes people give up: a constraint and IK only talk to each other if three conditions are met, and one of them is geometric and unforgiving.

The first is about parentage — the constraint and the IKControl have to share the same parent model.

The second is about the attachments the constraint references: they have to be on the same parts as the corresponding Motor6D's Part0 and Part1.

The third is the demanding one: the relative positions of the constraint's attachments have to be equal to the corresponding positions in the Motor6D's C0 and C1 CFrames. Not "similar" — equal. An attachment positioned by eye, dragged with the mouse until it looks right, almost certainly does not satisfy that. And the symptom of not satisfying it is not an error: it is a joint that behaves inexplicably.

The way to get it right is computing the position from the Motor6D, instead of positioning by hand. It is the kind of thing solved by a setup script run once over the rig, and never solved by dragging attachments in the viewport.

That is worth spelling out because it is the point at which most people abandon constraints: they are not hard to understand, they are hard to position. Anyone who tries by eye concludes constraints do not work, and goes back to living with the inverted elbow.

The second common mistake is IK fighting the animation. The control moves the same bones as the animation that is playing, and when the two disagree the result is a shaking limb. The way out is the weight: instead of on or off, a transition, and a weight below the maximum when you want partial influence on top of the animation.

The third is expecting IK to reach the unreachable. If the target is further away than the arm, the chain stretches to its limit and stays there, rigid. Checking the distance before switching on — as the artifact does — avoids the stretched-puppet pose.

Next step

The use with the biggest visual return is not the hand: it is the feet. A character standing on a ramp with both feet at the same height, or floating a centimetre above the step, is a defect the player feels without being able to name.

The recipe is the same as for the arm, with a raycast per foot to find the ground height and a target positioned there. The short chain is usually enough, and a weight proportional to how still the character is stops the effect fighting the walk animation.

After that, it is worth investing in constraints. They are laborious because of the position requirements, and they are what separates a character who reaches things from a character who looks like they reach things. The documentation describes the process with a hinge constraint at the elbow and a ball socket at the wrist, which is the pair that solves most arms.

Sources

How we correct mistakes

Ready-to-use artifact

Creating an IKControl at run time so the hand reaches a nearby target (ServerScriptService)
lua
-- ServerScriptService: makes the character's hand reach the nearest object
-- tagged as reachable, and let go when it moves away.
--
-- The four mandatory properties, and what each one decides:
--   Type       -> HOW to reach (Position follows position only;
--                 Transform follows position AND orientation)
--   EndEffector-> WHO reaches  (the hand)
--   Target     -> WHAT to reach (any object with a world position)
--   ChainRoot  -> HOW FAR the chain goes (the shoulder = whole arm;
--                 the forearm = elbow down only)

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")

local DISTANCE = 6 -- studs: how far the arm tries to reach
local TAG = "Reachable"

local function nearestTarget(root)
	local best, bestDist = nil, DISTANCE
	for _, object in CollectionService:GetTagged(TAG) do
		local position = object:IsA("BasePart") and object.Position
			or (object:IsA("Attachment") and object.WorldPosition)
		if position then
			local d = (position - root.Position).Magnitude
			if d < bestDist then
				best, bestDist = object, d
			end
		end
	end
	return best
end

local function prepareArm(character)
	local humanoid = character:WaitForChild("Humanoid")
	local root = character:WaitForChild("HumanoidRootPart")
	local hand = character:WaitForChild("RightHand", 10)
	local shoulder = character:WaitForChild("RightUpperArm", 10)
	if not hand or not shoulder then
		-- a rig without those parts: probably not R15
		warn("rig without RightHand/RightUpperArm: IK not applied")
		return
	end

	-- The IKControl lives in the Humanoid (or in the AnimationController).
	local ik = Instance.new("IKControl")
	ik.Name = "ReachWithRight"
	ik.Type = Enum.IKControlType.Position
	ik.EndEffector = hand
	ik.ChainRoot = shoulder
	ik.Enabled = false -- born off: with no target, there is nothing to reach

	ik.Parent = humanoid

	local connection
	connection = RunService.Heartbeat:Connect(function()
		if not character.Parent or humanoid.Health <= 0 then
			connection:Disconnect()
			ik:Destroy()
			return
		end

		local target = nearestTarget(root)
		if target then
			ik.Target = target
			-- Weight ramps up: switching on at once makes the arm JUMP
			-- to position, which is the most artificial effect IK can
			-- produce.
			ik.Enabled = true
			ik.Weight = math.min(ik.Weight + 0.08, 1)
		else
			ik.Weight = math.max(ik.Weight - 0.08, 0)
			if ik.Weight <= 0 then
				ik.Enabled = false
			end
		end
	end)
end

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(prepareArm)
end)
In this article

Keep reading