R15 IK Foot Placement

How does one do this? Are there any examples I could use to implement it into my game?

16 Likes

Inverse kinematics is a lot of math - but there are some examples you can use if you’re having difficulty wrapping your head around it.

Wiki article on Inverse Kinematics:
[Documentation - Roblox Creator Hub]

Video that goes with the wiki article on inverse kinematics:
[https://www.youtube.com/watch?v=UNoX65PRehA]

Open-sourced inverse kinematics method by Kodran:
[Inverse Kinematics - Roblox]

Personally, I use FABRIK for inverse kinematics, and the video really helped me to get the concept into my brain as to why and how it works.

8 Likes

Yeah, I do kind of understand how inverse kinematics work, I just don’t know how to apply it to the legs of an R15 character and make it look nice as he walks.

rewritten and better explained here: 2 Joint 2 Limb Inverse Kinematics


If you’re just looking to to basic knees and elbows you don’t really need anything much more complicated than cosine law. You can use circle intersection to do it too. If we’re doing it via law of cosines:

We’re basically just going to solve this triangle. We know lengths a, b, c, we just need to figure out angles A and C. To do that we can use the following:

As far as using this in roblox you can define the variables as:

b and a are the segment length of the arm/leg. ie think of b as the upper arm, and a as the lower arm.

c is a “flat” plane that we’ll use to solve our triangle on. So think of c as being the distance between the shoulder position and the target position.

So your values might look like

b = 1.255
a = 0.735
c = (origin - target).magnitude

A = math.acos((-a^2 + b^2 - c^2) / (2 * b * c))
C = math.acos((a^2 + b^2 - c^2) / (2 * a * b))

Now as far as using those angles goes you’d go about constructing a CFrame pointing from the shoulder position to the target, Rotating by angle (A + pi/2), and offseting the first arm piece outwards. Position your second arm piece rotate by angle (C - A) and you get offset outwards. If done right the end of the “arm” grabs the target point.

Things to consider with this approach:

If the length between the origin and target points is more than the length of both arms, your angles should be 0, 0.

if the length between the origin and target points is more than one arm length but less than the other arm length, you need to handle that case in a way that works best for you.

A “flat” CFrame.new(origin, target) for the base of your plane doens’t look natural, you’ll need some sort of rolling to have your limbs look realistic.

If you’d like an actual code example, this is what I use for Apocalypse Rising 2:

-- OriginCF is the CFrame of the shoulder, direction is important
-- TargetPos is the Vector3 position we're reaching to
-- l1 and l2 are both numbers that represent the length of the segments of the arm
local function solveIK(originCF, targetPos, l1, l2)	
	-- put our position into local space in regards to the originCF
	local localized = originCF:pointToObjectSpace(targetPos)
	
	-- this breaks if OriginCF and targetPos are at the same position
	local localizedUnit = localized.unit
	
	-- the distance to from originCF.p to targetPos
	local l3 = localized.magnitude
	
	-- build an axis of rotation for rolling the arm
	-- forwardV3 is Vector3.new(0, 0, -1)
	local axis = forwardV3:Cross(localizedUnit)
	
	-- find the angle to rotate our plane at
	local angle = acos(-localizedUnit.Z)
	
	-- create a rotated plane to base the angles off of
	local planeCF = originCF * fromAxisAngle(axis, angle)
	
	-- L3 is between the length of l1 and l2
	-- return an offset plane so that one part reaches the goal and "folded" angles
	if l3 < max(l2, l1) - min(l2, l1) then
		return planeCF * cfNew(0, 0,  max(l2, l1) - min(l2, l1) - l3), -pi/2, pi
		
	-- l3 is greater than both arm lengths
	-- return an offset plane so the arm reaches its goal and flat angles
	elseif l3 > l1 + l2 then
		return planeCF * cfNew(0, 0, l1 + l2 - l3), pi/2, 0
		
	-- the lengths check out, do the law of cosines math and offset the plane to reach the targetPos
	-- return the offset plane, and the 2 angles for our "arm"
	else
		local a1 = -acos((-(l2 * l2) + (l1 * l1) + (l3 * l3)) / (2 * l1 * l3))
		local a2 = acos(((l2  * l2) - (l1 * l1) + (l3 * l3)) / (2 * l2 * l3))

		return planeCF, a1 + pi/2, a2 - a1
	end
end


-- simple demo for how its called

local TARGET_POS = Vector3.new(0, 3, 0)
local SHOULDER_C0_VALUE = CFrame.new(1, 0.4, 0)

while wait() do
	local shoulderCF = upperTorso.CFrame * SHOULDER_C0_VALUE
	local plane, shoulderAngle, elbowAngle = solveIK(shoulderCF, TARGET_POS, 0.515, 1.1031)

	shoulderMotor.C0 = upperTorso.CFrame:toObjectSpace(plane) * cfAngles(shoulderAngle, 0, 0)
	elbowMotor.C0 = lowerC0 * cfAngles(elbowAngle, 0, 0)

	-- keep the C1 values consitent, they shouldn't need to be set every frame
end

When it comes to modeling foot and hand movement, it’s best to just study the movements of the hands relative to the torso of a person. Figure out how to model those and use them for your target position. I won’t go into that because there are a bunch of different ways of doing it, but learning how to model ovals would be a good start.

78 Likes

beautiful

8 Likes

Oh boy. Kodran here (I made that third link you put), please if anyone wants to learn don’t bother looking through that code. I wrote it a few years ago and it’s absolute crap; there’s literally a comment that says this:
“–too lazy to explain this”

Not even entirely convinced it works in all cases lol.

That being said, WhoBloxedWho gave a very comprehensive answer which should be more than enough to get you started :+1:

1 Like

Okay, so I’m starting to understand this, except I don’t really get what you mean by “modeling foot and hand movement”? Am I supposed to create an invisible target part for each foot in my character and then animate it so the IK feet know where to move? Or am I not understanding this correctly?

2 Likes

You can think of it as a part, but you’re really only need a position. So if you wanted to model a foot based off an oval, you could do something like:

-- calculate where the floor is
local hipCF = lowerTorso.CFrame * leftHip.C0
local floorCF = hipCF * CFrame.new(0, -2, 0) -- raycasting for this value is probably better

-- you can use cosine and sine to model circles,
-- if you clamp the height of the circle to never go below 0
-- you end up making a half circle shape,
-- stretching the length (in this case Z) gives us an oval shape
local offsetFromGround = Vector3.new(
	0, 
	math.max(0, math.cos(time() * math.pi * walkSpeed ) * 3),
	math.sin(time() * math.pi * walkSpeed) * 3
)

-- use this value for a TargetPosition for your IK
local footGoal = floorCF * offsetFromGround

The example isn’t an ideal shape for the path of a foot as you’re walking relative to the torso, but it’s a start. It’s well worth doing some research and figuring out how you want it to look.

Being able to visualize these shapes really helps too, and I’d absolutely recommend playing with it in studio or some kinda graphing program and just trying to move a point/part back and forth via math until you get some form of motion you really like.

9 Likes

What is lowerC0 in “elbowMotor.C0 = lowerC0 * cfAngles(elbowAngle, 0, 0)”?

Also I’ve done everything you’ve said except the “lowerC0” bit and the legs are kinda getting detached :smiley:

https://i.gyazo.com/f47d16eb427266024526dce4fff8e81c.gif

2 Likes

Correct me if I’m wrong, @WhoBloxedWho, but wouldn’t you have to disable the default Roblox animation when doing this. That may be why it’s messing up for you @vsnry

I have made it so the leg movement in the default running animation is disabled so I’m pretty sure that’s not what’s detaching the legs

Edit:

As you can see it still does it after I disabled the animation :confused:

https://i.gyazo.com/54a81a0f7ad72ab76b1330a6afe7f2b8.gif

I tried swapping shoulderCF with TARGET_POS and it stopped it, but the leg was turned upside down and behaved weirdly

image

2 Likes

Guys, are you seriously trying to get IK to work without disconnecting the character’s leg motors?
I feel I should remind you that a client can move their character’s parts any way they want, and it’ll replicate.

What are you talking about?

And no Motor6D’s don’t replicate.


@WhoBloxedWho so I’m trying to work with your code, but it doesn’t seem to work properly for some reason :confused: can you check it out and see what’s wrong? Or am I the one doing it wrong?

https://i.gyazo.com/48f3e05dca64dd566b04a95892ee3f8f.gif

Could you DM me a code snippet or the rbxl of that place?

There’s a way to make character leg stay in place, by using roblox animation. Looping it in normal position.

Motor6D doesn’t need to be disconnected anymore. Just need to be moved.

You can also just set a motors .Transform CFrame to an empty CFrame every runService.Stepped. It’s something that should be done client side, so it’s not a big deal as you should also be doing procedural animation client side.

2 Likes

What I’m talking about is that there does not need to be any physical justification of how a character is moving their limbs.
Motor6Ds are a pain to work with, but you can disconnect them and move the character’s legs by setting CFrames clientside, and the server will happily replicate that.

Ok, so I’ve figured out how to do most of it and it works pretty well, but now I can’t figure out how to make the foot properly adjust itself so the leg looks like it’s actually sitting ontop of something. I tried cross product, but it didn’t really work. Any ideas?


image

2 Likes

You can shoot a ray downward. The third variable findpartonray returns is the surface normal

Yes, but how do I compensate for the leg rotation? The foot is already rotated by how much the lowerleg is rotated so if I were to rotate it to the surface normal it still wouldn’t be right.