Prototype R15 Animation IKControl Footplant

I have seen multiple posts 1, 2, and 3 on people wanting to know how recreate Roblox’s IK Control demo.

So I spent 1 hour to make a quick and dirty prototype. Enjoy.

Placefile:
IK Control requires basic setup from hip to foot. This place file should make it convenient to test out the code.

AnimationFootPlant.rbxl (134.0 KB)

Code
local model = script.Parent

--local animationID = "http://www.roblox.com/asset/?id=507777826"
local animationID = "http://www.roblox.com/asset/?id=507767714" --run
local animation = Instance.new("Animation")

animation.AnimationId = animationID

local animator = model.Humanoid.Animator

local walkAnim = animator:LoadAnimation(animation)
walkAnim:AdjustSpeed(0.5)
walkAnim:Play()
--walkAnim:AdjustSpeed(0.25)

local RunService = game:GetService("RunService")

local target = model.HumanoidRootPart.IKTarget1

local IKControl = model.Humanoid.IKControlRightLeg
local chainRoot : Motor6D = IKControl.ChainRoot
local endEffector = IKControl.EndEffector

local RightHip = model:FindFirstChild("RightHip", true)
local RightKnee = model:FindFirstChild("RightKnee", true)
local RightAnkle = model:FindFirstChild("RightAnkle", true)

--motor.part0*c0*transform = motor.Part1*C1

local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances= {model}


print(IKControl.SmoothTime)

IKControl.SmoothTime = 0

----motor.part0*c0*transform = motor.Part1*C1
--Solve for Part1
--motor.part0*c0*transform*C1:Inverse() = motor.Part1
--Repeat all the way down to calculate end effector position where it should be without IKControl effecting it
RunService.Stepped:Connect(function(dt)
	
	local rightHipJointWorldCFrame = RightHip.Part0.CFrame*RightHip.C0
	local rightkneePart1CF = rightHipJointWorldCFrame*RightHip.Transform*RightHip.C1:Inverse()
	local rightFootPart1CF = rightkneePart1CF*RightKnee.C0*RightKnee.Transform*RightKnee.C1:Inverse()
	local test = rightFootPart1CF*RightAnkle.C0*RightAnkle.Transform*RightAnkle.C1:Inverse()
	
	local goalTargetWorldCF = test*endEffector.CFrame
	
	local direction = goalTargetWorldCF.Position - rightHipJointWorldCFrame.Position
	local raycastResult = workspace:Raycast(rightHipJointWorldCFrame.Position, direction, rayParams)
	
	if raycastResult then
		goalTargetWorldCF = goalTargetWorldCF.Rotation + raycastResult.Position
		target.WorldCFrame = goalTargetWorldCF
		IKControl.Enabled = true
	else
		
		IKControl.Enabled = false

	end
	
	--target.WorldCFrame = goalTargetWorldCF
end)


--Repeat for Left Leg
local target2 = model.HumanoidRootPart.IKTarget2

local IKControl2 = model.Humanoid.IKControlLeftLeg
local chainRoot : Motor6D = IKControl2.ChainRoot
local endEffector = IKControl2.EndEffector

local LeftHip = model:FindFirstChild("LeftHip", true)
local LeftKnee = model:FindFirstChild("LeftKnee", true)
local LeftAnkle = model:FindFirstChild("LeftAnkle", true)

--motor.part0*c0*transform = motor.Part1*C1

local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances= {model}


print(IKControl2.SmoothTime)

IKControl2.SmoothTime = 0

target2.Visible = true
target.Visible = true

RunService.Stepped:Connect(function(dt)

	local leftHipJointWorldCFrame = LeftHip.Part0.CFrame*LeftHip.C0
	local leftkneePart1CF = leftHipJointWorldCFrame*LeftHip.Transform*LeftHip.C1:Inverse()
	local leftFootPart1CF = leftkneePart1CF*LeftKnee.C0*LeftKnee.Transform*LeftKnee.C1:Inverse()
	local test = leftFootPart1CF*LeftAnkle.C0*LeftAnkle.Transform*LeftAnkle.C1:Inverse()

	local goalTargetWorldCF = test*endEffector.CFrame

	local direction = goalTargetWorldCF.Position - leftHipJointWorldCFrame.Position
	local raycastResult = workspace:Raycast(leftHipJointWorldCFrame.Position, direction, rayParams)

	if raycastResult then
		local goalTargetWorldCF = CFrame.new() + raycastResult.Position
		target2.WorldCFrame = goalTargetWorldCF
		IKControl2.Enabled = true
	else
		IKControl2.Enabled = false
	end

	--target.WorldCFrame = goalTargetWorldCF
end)
45 Likes

Thank you so much this really helps me understand how it works more

1 Like

Currently getting “Part0 is not a valid member of MeshPart” and such. I’m pretty stumped on how to use this resource on a custom model/rig.

1 Like

robloxapp-20240318-2149246.wmv (2.1 MB)
Having some trouble… Do I need to relink the joints to the IK control?

Is there any way you can create a new file or edit the ```` code in your post so that there’s comments that explain each line? I’m really confused what all this means

Thanks

Sure,

The purpose of this code portion is to calculate the world position of the foot starting from the base hip of the character including the effects of animation CFrames which is the .Transform of a motor6D.

The reason for not just doing foot.Position is that this is the IK target if if you just use foot.Position the target position will stay the same due to the IK and will probably cause a weird cyclical effect bug and will not include the effects of movement ( .Transform and hip movement)

The formula for welds is here:

For a motor6D the only thing different is that in the formula .Transform is added based on which part is closest to the root part which is usually part0:

Part0.CFrame * C0 * Motor6D.Transform = part1.CFrame * C1 

You can visualize it as position vectors starting from world origin (0,0,0)

It starts from world origin which is the red part, goes to the hip position (RightHip.Part0.CFrame), moves down to the right hip joint where the leg roates from (C0 CFrame position vector), adds an extra animation CFrame (RightHip.Transform), moves down to the ankle (RightHip.C1:Inverse() it is inverse because the direction of this position vector starts from the ankle towards the hip so we need to reverse this direction to go from hip to ankle instead). This continues till it reaches the foot.

That should be the jist of it, I’ve also tried explaining it in my CFrame tutorial.

1 Like