2 Joint 2 Limb Inverse Kinematics

I fixed this problem by replacing this

return planeCF * cfNew(0, 0, l1 + l2 - l3), pi/2, 0

to this

return planeCF, math.pi/2, 0

on the solver

7 Likes

Kind of solves. But when goal is far away it doesn’t point in it’s direction
2020-05-21_173433

I really like your module and how simple it is, but I’m having so much trouble using it for foot planting. For the most part, it works except its literally backward. I’ve tried adding math.pi to the upper leg angle but it also produces weird results. I’ve been at this for a while now and I’m really lost.

https://i.gyazo.com/5ac959d44c697158c01231be91228d44.mp4

Code: (this is being run every renderstepped) (raycast class is well, just raycast module)

local offset = (self.Character.PrimaryPart.CFrame * CFrame.new(0.5, -1, -0)).Position
local hit, position, normal = RaycastClass.CastRayUsingIgnoreList(Ray.new(offset, Vector3.new(0,-5, 0)))
	
if (offset-position).Magnitude <= 1.9 then
	rightHip.Transform = CFrame.new()
	rightKnee.Transform = CFrame.new()
	rightAnkle.Transform = CFrame.new()
	
	local footCFrame = self.Character.LowerTorso.CFrame * HIP_C0_CACHE
	local planeCF, hipAngle, kneeAngle = solveIK(footCFrame, position, UPPER_LENGTH, LOWER_LENGTH)
	if lastRightTweens[1] then lastRightTweens[1]:Cancel() end
	if lastRightTweens[2] then lastRightTweens[2]:Cancel() end
	lastRightTweens[1] = TweenService:Create(rightHip, TweenInfo.new(0.2), {C0 = lowerTorso.CFrame:ToObjectSpace(planeCF) * CFrame.Angles(hipAngle, 0, 0)}):Play()
	lastRightTweens[2] = TweenService:Create(rightKnee, TweenInfo.new(0.2), {C0 = KNEE_C0_CACHE * CFrame.Angles(kneeAngle, 0, 0)}):Play()
else
	if lastRightTweens[1] then lastRightTweens[1]:Cancel() end
	if lastRightTweens[2] then lastRightTweens[2]:Cancel() end
	lastRightTweens[1] = TweenService:Create(rightHip, TweenInfo.new(0.5), {C0 = HIP_C0_CACHE}):Play()
	lastRightTweens[2] = TweenService:Create(rightKnee, TweenInfo.new(0.5), {C0 = KNEE_C0_CACHE}):Play()
end
--Code being run before:
local rightHip =        char:WaitForChild("RightUpperLeg"):WaitForChild("RightHip")
local rightKnee	= char:WaitForChild("RightLowerLeg"):WaitForChild("RightKnee")
local rightAnkle	= char:WaitForChild("RightFoot"):WaitForChild("RightAnkle")

local solveModule	= ReplicatedStorage.Modules:WaitForChild("SolveIK")
local solveIK		= require(solveModule)

local HIP_C0_CACHE		= rightHip.C0
local KNEE_C0_CACHE		= rightKnee.C0
local ANKLE_C0_CACHE    = rightAnkle.C0

local UPPER_LENGTH			= math.abs(rightHip.C1.Y) + math.abs(rightKnee.C0.Y)
local LOWER_LENGTH			= math.abs(rightKnee.C1.Y) + math.abs(rightAnkle.C0.Y) + math.abs(rightAnkle.C1.Y)

Sorry for replying really late, but I need help with this. Thanks!

try

lastRightTweens[1] = TweenService:Create(rightHip, TweenInfo.new(0.2), {C0 = lowerTorso.CFrame:ToObjectSpace(planeCF) * CFrame.Angles(-hipAngle, 0, 0)}):Play() 

Not sure if it’d work but I’m guessing by negating the angle, it’d rotate oppositely thereby fixing your problem

It looks right in certain situations but that doesn’t affect how the angle limitations are determined, so once you get to higher limit angles that are either too short or too far then the calculations are reversed so the leg goes short when it should extend the furthest and the leg goes long when it should extend the least.


this is how the IK module looks, (i removed the 90 degree addition from the -a1 near the end on purpose, it doesn’t change the result in a way I need)

1 Like

Hmm. I just edited some stuff in the IK module and now it works perfectly.image
I just made a1 positive and subtracted 90 degrees from a2-a1, seems to be working fine.

https://gyazo.com/d05ad99a9b1689272c7010c6671d093a

This isn’t really what i want though, I was the leg to lift up all the way and kinda crouch instead of kneel, so the knee is up and isn’t into the ground

I’d love to help but I’m not too keen on trig, so most of it is just going over my head

Well, this is the best result so far, thanks for helping though. I simply added a “base” value and did my calculations off of that angle, (math.pi/2) I also added 4 different positions, one being in between super close and perfect, so there weren’t that many artifacts.

External Media

This method of IK might not be ideal for what you’re trying to do. The method this thread depicts is pretty strict and what you want might be something more adaptive.

Adopting FABRIK style IK might be more what you’re looking for.

7 Likes

This is actually quite useful, however one issue I am having with it, is the hand touching the inner edge rather the centre.

How would I go about fixing this?

1 Like

It’s because the solve isn’t accounting for the outward offset the C1 of the shoulder has. Technically it’s correct since the line from the shoulder C0 to the point is still in line.

You can correct this by offsetting the originCF by the shoulder C1 offset outward (c1.X) or by rotating the final planeCF to compensate for the offset (think of the offset + arm lengths as a right angle triangle: tan(l1+l2 / c1.X)). Rotating means you’ll also need to adjust for the loss in distance by pushing the planeCF forwards.

4 Likes

A = math.acos((-a^2 + b^2 - c^2) / (2 * b * c))
wouldn’t it be:
A = math.acos((-a^2 + b^2 + c^2) / (2 * b * c))

2 Likes

This is a tutorial, not a project. This topic is intended to teach you the basics of IK, and it’s up to you to apply it yourself.

One case that needs to be adressed is when the axis is equal to Vector3.new()
A simple solution to this:

local axis = Vector3.new(0, 0, -1):Cross(localizedUnit)
local angle = math.acos(-localizedUnit.Z)
if axis == Vector3.new() then
	axis = (angle == PI and Vector3.new(-1, 0, 0) or Vector3.new(0, 0, -1))
end
local planeCF = originCF * CFrame.fromAxisAngle(axis, angle)

This addresses two very specific cases where the math breaks :slight_smile: Just wanted to add this on.

3 Likes

No, the first equation is correct

So I am very unfamiliar with IK, however, I am getting a grasp. How would I go about adding another limb?

This is the only code I changed and it works fine except the goal part is at the joint I do not want it to be at. How do I fix this?

Code:

local workspace		= game:GetService("Workspace")
local storage		= game:GetService("ReplicatedStorage")
local runService	= game:GetService("RunService")

local goalPart		= workspace:WaitForChild("GoalPosition")
local shoulderPart	= workspace:WaitForChild("ShoulderPosition")
local upperArm		= workspace:WaitForChild("UpperArm")
local lowerArm		= workspace:WaitForChild("LowerArm")
local grapple = workspace:WaitForChild("Grapple")

local solveModule	= storage:WaitForChild("SolveIK")
local solveIK		= require(solveModule)

----

local SHOULDER_OFFSET		= CFrame.new(1.5, 0.5, 0)
local VECTOR3_UP			= Vector3.new(0, 1, 0)

----

runService.Heartbeat:Connect(function()
	local shoulderCFrame = shoulderPart.CFrame * SHOULDER_OFFSET
	local goalPosition = goalPart.Position	
	
	local upperV3 = VECTOR3_UP * upperArm.Size
	local lowerV3 = VECTOR3_UP * lowerArm.Size
	local grappleV3 = VECTOR3_UP * grapple.Size

	local planeCF, shoulderAngle, elbowAngle, otherelbow = solveIK(shoulderCFrame, goalPosition, upperV3.Y, lowerV3.Y, grappleV3.Y)
	local shoulderAngleCFrame = CFrame.Angles(shoulderAngle, 0, 0)
	local elbowAngleAngleCFrame = CFrame.Angles(elbowAngle, 0, 0)

	upperArm.CFrame = planeCF * shoulderAngleCFrame * CFrame.new(-upperV3 * 0.5)
	lowerArm.CFrame = upperArm.CFrame * CFrame.new(-upperV3 * 0.5) * elbowAngleAngleCFrame * CFrame.new(-lowerV3 * 0.5)
	grapple.CFrame = lowerArm.CFrame * CFrame.new(-upperV3 * 0.5) * elbowAngleAngleCFrame * CFrame.new(-lowerV3 * 0.5)

end)

Thanks!

How would I make this R6 Because I Don’t Really See Many Posts?

It isn’t bad, it’s just older.

What am I supposed to do in this situation?

What is the issue, you gotta give context instead of just showing an image and saying “What am I supposed to do in this situation?”