Inverse Kinematics Control Instance for animation is now in Beta!

Question, will we also be able to limit how far body parts can stretch or rotate?

Blender has this neat feature where you can set knees to not bend further than X degrees on Y axis.
And you can configure how much a bone is allowed to dislocate/stretch to reach it’s target.

It can make the IK feel more stretchy and flexible (useful for more cartoony/expressive animations)
but can also be used to constrain it so unnatural limb positions and rotations aren’t possible.

2 Likes

You’re way ahead of us :grin: Yes, we are working on implementing joint limits that will allow to specify just that, so that shoulders/elbows/… will bend more realistically.
Can you elaborate on what you mean “a bone dislocates/stretches”?

7 Likes

The axis is dependent on what orientation the rig is imported from Blender, right?

I tried to set up a 3-part rig to mimic a tentacle. Then, I set the root and end and a target. I tried out a few settings but it didn’t work, or in the case of the LookAt mode, didn’t actually bend.

Any idea why?

2 Likes

yes, it’s the local space of the joints/bones. you can select when exporting what the convention should be, e.g. exporting fbx from blender
image

2 Likes

Do you have an Animator under your Humanoid/AnimationController?

1 Like

i’ll send the entire script that i’m using, it might be a little messy, since i haven’t completed it completely.

-- StarterCharacterScripts
-- <3 dwifte 
local Char = script.Parent
local RunService = game:GetService("RunService")

local LeftIKObj = workspace:FindFirstChild("LeftIK") or Instance.new("Part")
LeftIKObj.CanCollide = false
LeftIKObj.Transparency = 0.8
LeftIKObj.Color = Color3.new(0,0.4,0.8)
LeftIKObj.Size = Vector3.new(0.4,0.4,0.4)
LeftIKObj.Name = "LeftIK"
LeftIKObj.Parent = workspace

local RightIKObj = workspace:FindFirstChild("RightIK") or Instance.new("Part")
RightIKObj.CanCollide = false
RightIKObj.Transparency = 0.8
RightIKObj.Color = Color3.new(0,0.4,0.8)
RightIKObj.Size = Vector3.new(0.4,0.4,0.4)
RightIKObj.Name = "RightIK"
RightIKObj.Parent = workspace

local LeftLegIK = Instance.new("IKControl")
local RightLegIK = Instance.new("IKControl")

LeftLegIK.Name = "LeftLeg"
LeftLegIK.Parent = Char:FindFirstChild("Humanoid")
LeftLegIK.Type = Enum.IKControlType.Position

LeftLegIK.EndEffector = Char:FindFirstChild("LeftFoot")
LeftLegIK.ChainRoot = Char:FindFirstChild("LeftUpperLeg")
LeftLegIK.Weight = 0.5
LeftLegIK.Target = LeftIKObj

RightLegIK.Name = "RightLeg"
RightLegIK.Parent = Char:FindFirstChild("Humanoid")
RightLegIK.Type = Enum.IKControlType.Position

RightLegIK.Weight = 0.5

RightLegIK.EndEffector = Char:FindFirstChild("RightFoot")
RightLegIK.ChainRoot = Char:FindFirstChild("RightUpperLeg")

RightLegIK.Target = RightIKObj

local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Blacklist
params.FilterDescendantsInstances = {Char,LeftIKObj,RightIKObj,workspace.CurrentCamera} -- workspace camera if you are using a view model

RunService.RenderStepped:Connect(function()
	local LeftRay = workspace:Raycast(Vector3.new(Char.LeftFoot.Position.X,Char.LeftFoot.Position.Y+2,Char.LeftFoot.Position.Z),Vector3.new(0,-1,0)*300,params)
	if LeftRay then
		LeftIKObj.CFrame = CFrame.new(LeftRay.Position,LeftRay.Normal)
	end
	local RightRay = workspace:Raycast(Vector3.new(Char.RightFoot.Position.X,Char.RightFoot.Position.Y+2,Char.RightFoot.Position.Z),Vector3.new(0,-1,0)*300,params)
	if RightRay then
		RightIKObj.CFrame = CFrame.new(RightRay.Position,RightRay.Normal)
	end
end)

I’m pretty sure its because of your weight property is set too high, so it overwrites animations, for me 0.5 seems to work. If it doesn’t for you, then try and change it until it works.

3 Likes

So with “a bone dislocates/stretches”.

Basically, when a goal is further away than a arm or leg can stretch, it will dislocate the hips or knees slightly (often small enough to not be super noticeable but is fully customizable) to give it just that extra little bit of stretch/length that it needs to reach it’s target.

This can also happen when a joint reaches it’s rotational limit.
When a joint can’t bend/rotate further as 90 degrees for example, the IK system looks if there is any bone that can be dislocated or slightly pulled out of it’s socket just to give it that tiny extra bit of reach.

This can also prevent jittering in some cases where joint/bone constraints are very tight and limited in movement.
That little bit of stretch/dislocation can give it that tiny bit of extra space that it needs to finish the pose it was trying to get to, but only does so if it absolutely has to.

other than that, if you exaggerate this stretch/dislocate effect, you can make it more cartoony by allowing characters to have spaghetti / rubberhose legs for example.

2 Likes

So it’s not going to work with R6?

It does! According to this post:

1 Like

How are you getting the raw part position without the IK adjustments from the previous Stepped for doing the calculations? Having trouble figuring this out myself, and I don’t want to just follow the Motor6D transforms because that would require searching to find the entire chain. Is there a nice way to do this?

4 Likes

I do. I have a group of parts, with an animation controller and an animator under it.

Adding on to @TheNexusAvenger’s report, Motor6D rigs do in fact seem to break the starting position of the IK chain on all types. The chain start appears to teleport towards 0,0,0 but continues to point towards the proper goal, leading to undesirable results.

Here’s a place file: IK Issue.rbxl (39.4 KB)

2 Likes

Same here. I’ve been hacking at it for half of today and there doesn’t seem to be a workaround. It “teleports” to (0, 0, 0).

The position of the Bones seems to be at the right place, but the apparent placement of the mesh is at 0,0,0.

1 Like

Seconding this, it looks like stepped is either not after the animation step or the part positions are not updated correctly before stepped is called.

image
Here is my character in motion with no IK active, the yellow circle is showing the foot’s reported position on stepped and the blue circle is heartbeat.

When using IK, since the position has not been updated, it shows as the previous frame’s modified IK position

image
Here I would expect the yellow circle to be lower down, next to where the leg should be idling without IK

3 Likes

Heehehehehe legs

I still need to work in springs or something to smooth the transitions, but it looks pretty cool like this already. This is likely a very common use case, maybe Roblox can provide some built in easing behaviors.

Some things I definitely wished for though was to be able to offset the endeffector so it can be at the bottom of the foot. Missing this ended up getting confusing at some point for a while while untangling something broken.

I also could not get the pure animated positions without IK influence on Stepped from just the datamodel. I needed to collect the entire motor graph and run the parent-child transform calculations all myself every Stepped to get the pure values.

The code for this is not at all sharable yet though.


Some people above are having trouble with their models. I can probably provide the code for this / a stripped version of the character model tomorrow if needed for comparison. @xtntt @PoptartNoahh

12 Likes

Although I believe my post to be a bug that will get ironed out, I would still love to see how you rigged your model to avoid this issue when you get a chance!

I see, that’s a good suggestion. Currently we force joints to be in the exact same position, but I do see value in providing some type of control that allows you to slightly overreach/stretch. I’ll do some experiments and see how it looks, thanks

4 Likes

This is an issue we are currently working on. The forward kinematics for Motor6D set during the animation step are resolved during the physics step (even though it’s fully kinematic), so currently the only way is to compute FK yourself during stepped, (which is also the solution I’m using for the foot planting above). This is a peculiarity of Motor6D, other kinematic objects like Bone have TransformedWorldCFrame. It’s obviously not ideal so we’re working on adding a simpler way. The two options we’re discussing are

  • expose something like Motor6D.WorldCFrame : CFrame
  • expose Animator:GetWorldCFrame(BasePart) : CFrame

I’m interested in knowing your thoughts. Thanks

2 Likes