Inverse kinematics' with animation supported

hello I’m seek and i am trying to make a IK with animation support but there is a couple bugs
I’m using IK controls for the positioning

local model = script.Parent -- character
local RunService = game:GetService("RunService")

-- IK Target Attachments
local iktarget1 = Instance.new("Attachment")
local iktarget2 = Instance.new("Attachment")
local pole1 = Instance.new("Attachment")
local pole2 = Instance.new("Attachment")
iktarget1.Parent = model.HumanoidRootPart
iktarget2.Parent = model.HumanoidRootPart
pole1.Parent = model.HumanoidRootPart
pole2.Parent = model.HumanoidRootPart
iktarget1.Name = "iktarget1"
iktarget2.Name = "iktarget2"
iktarget1.Position = Vector3.new(0.482, -3.154, -0.05)
iktarget2.Position = Vector3.new(-0.482, -3.154, -0.05)
pole1.Position = Vector3.new(0.446, -1.639, -2)
pole2.Position = Vector3.new(-0.446, -1.639, -2)
iktarget1.Visible = true
iktarget2.Visible = true
pole1.Visible = true
pole2.Visible = true

-- IK Control for Left and Right Legs
local IKControlLeftLeg = Instance.new("IKControl")
IKControlLeftLeg.Parent = model:FindFirstChild("Humanoid")
IKControlLeftLeg.Name = "IKControlLeftLeg"
IKControlLeftLeg.Type = Enum.IKControlType.Transform
IKControlLeftLeg.SmoothTime = 0.05
IKControlLeftLeg.Weight = 1
IKControlLeftLeg.Priority = 0
IKControlLeftLeg.Pole = pole2
IKControlLeftLeg.EndEffector = model:FindFirstChild("LeftFoot").LeftFootAttachment
IKControlLeftLeg.Target = iktarget2
IKControlLeftLeg.ChainRoot = model:FindFirstChild("LeftUpperLeg").LeftHip

local IKControlRightLeg = Instance.new("IKControl")
IKControlRightLeg.Parent = model:FindFirstChild("Humanoid")
IKControlRightLeg.Name = "IKControlRightLeg"
IKControlRightLeg.Type = Enum.IKControlType.Transform
IKControlRightLeg.SmoothTime = 0.05
IKControlRightLeg.Weight = 1
IKControlRightLeg.Priority = 0
IKControlRightLeg.Pole = pole1
IKControlRightLeg.EndEffector = model:FindFirstChild("RightFoot").RightFootAttachment
IKControlRightLeg.Target = iktarget1
IKControlRightLeg.ChainRoot = model:FindFirstChild("RightUpperLeg").RightHip

-- Raycasting Params
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {model, iktarget1, iktarget2}

-- Function to handle IK leg adjustments using local position and rotation
local function updateLegIK(leg, ikControl, target, pole, rayParams)
	local chainRoot = ikControl.ChainRoot
	local endEffector = ikControl.EndEffector
	local hip = model:FindFirstChild(leg .. "Hip", true)
	local knee = model:FindFirstChild(leg .. "Knee", true)
	local ankle = model:FindFirstChild(leg .. "Ankle", true)

	-- Calculate CFrame for the leg joints in local space
	local hipJointWorldCFrame = hip.Part0.CFrame * hip.C0
	local kneePart1CF = hipJointWorldCFrame * hip.Transform * hip.C1:Inverse()
	local footPart1CF = kneePart1CF * knee.C0 * knee.Transform * knee.C1:Inverse()
	local legGoalCF = footPart1CF * ankle.C0 * ankle.Transform * ankle.C1:Inverse()

	-- Calculate the goal target CFrame locally (relative to model root)
	local goalTargetLocalCF = legGoalCF * endEffector.CFrame

	-- Raycast direction from hip to target
	local direction = goalTargetLocalCF.Position - hipJointWorldCFrame.Position
	local raycastResult = workspace:Raycast(hipJointWorldCFrame.Position, direction, rayParams)

	-- Adjust the position based on raycast hit
	if raycastResult and raycastResult.Instance then
		-- Apply raycast position adjustment
		-- Get the raycast position in local space (relative to the character)
		local localRaycastPosition = model.HumanoidRootPart.CFrame:pointToObjectSpace(raycastResult.Position)

		-- Use the local position and apply rotation locally (without world space transformations)
		local newPosition = localRaycastPosition

		-- Set the target position using the adjusted local position
		-- Apply the local position and ensure it's not affected by world transformations
		target.Position = newPosition
		ikControl.Enabled = true
	else
		ikControl.Enabled = false
	end
end

-- Update function on each frame
RunService.Heartbeat:Connect(function(dt)
	-- Update Right Leg
	updateLegIK("Right", IKControlRightLeg, iktarget1, pole1, rayParams)

	-- Update Left Leg
	updateLegIK("Left", IKControlLeftLeg, iktarget2, pole2, rayParams)
end)

this script can be fully functional but has one problem
its legs is stuttering because of the IKs turning off and on


in the video its way less buggy but when you look at it in game its way worse

if anyone can help its appreciated
and thanks to dthecoolest for the basic script
hopefully we can turn this into a module when its done.

One way to alleviate this issue is to smooth/lerp the IK rather than turn it on and off by frame. Keep track of the current local raycast position of each foot with a persistent variable and ease it towards the newPosition every frame according to the speed you’d like and deltaTime (so the easing speed remains consistent between varying framerates).

This should make it so the character’s foot position is pushed up/down more smoothly and will gracefully & quickly glide towards the target position rather than snapping up/down quite jarringly.

Another way to improve foot detection is to use a SphereCast or BoxCast rather than a line, as right now it seems like the moment the centre of the character’s foot hovers off the spawnpoint it will snap down to the baseplate.

1 Like

great solution to my problem thanks ill add it to the final version

2 Likes