Any idea why I am getting this sporadic behavior? The gif doesn’t really show it because it doesn’t capture at full framerate, but the arm is flashing between at least two positions each frame.
Without constraints, it’s not sporadically flashing every other frame, but it is wandering continuously and it never settles on a solution:
Also, the two links on the homepage of your github documentation are broken.
Looks like it’s coming from a conflict against animations running on the rig.
I added a test character to the example file with the default animate script. With the animate script disabled, it works fine, but with the animate script running an idle animation on the rig, it shows the sporadic behavior. TestPlace.rbxlx (1.1 MB)
So far the fix I was able to come up with was to just remove the effects of the animations by setting the Transform property to CFrame.new() and running it within .Stepped of Runservice instead of heartbeat.
--within the CCDIK controller rotateJointFromTo and rotateJointFromToWithLerp
--Functions
motor6DJoint.Transform = CFrame.new()--Just remove it
motor6DJoint.C0 = goalC0CFrame
I think it’s a pretty dirty solution as well it doesn’t allow for animations to be blended with the IK, but it should work for now.
Awesome! One revision to this solution though, you need to put the transform resets in CCDIKIterateOnce and CCDIKIterateUntil, BEFORE the goal magnitude check. Otherwise the transform does not reset on frames where there is no calculation because the goal is already near the end effector, and in this case the jitteriness returns.
If anyone is curious and needs some tips, here’s an rbxm showing how I set up the constraints for the arms. I also modified the ballsocket hip constraints from the example, because the bending backwards limit was too far. This setup seems to work pretty well, and you don’t need to set up any extra attachments outside of what the R15 rig is already using, which is nice. Character.rbxm (15.3 KB)
I just happened to be looking at this right now for using it on arms and a boat steering wheel and was getting the strange spinning issue alongside unnatural arm rotations.
Thank you very much my dude
Do you animate server-side or are these movements only visible to the client?
I’m animating on the server currently so it replicates and it works perfect on the server, however the view on the client is offset from what I see on the server. The client’s local animation doesn’t get disabled by resetting the motor transform values on the server.
I think I’m going to have to reset motor transforms on the client too.
This is one of the most underrated and brilliant modules I’ve seen - really huge thanks for releasing this! My only “complaints” would be on whether you’d be able to setup more examples? I’m sure this module would gain tons of times more traction if you provided with sample footplanting code, either R6 or R15.
Other than that, I’ve no complaints! I’ll check out how this module fares in some of my projects
then the right arm is just permanently stuck as if it has no animation applied to it.
This is the test place: IK_Test.rbxl (49.1 KB)
If you could help figuring out what the issue is, that’d be great.
Many thanks!
EDIT: Nevermind! Only now noticed that there is a test place attached.
EDIT 2:
For those interested in what the issue is, for R6 characters, you must set CCDIKController.UseLastMotor to true.
I’ve been messing around with this module and it works fine EXCEPT it’s kind of choppy when the r15 rig I am using is running.
Video:
Script
local RUS = game:GetService("RunService")
local IKSolver = require(script.CCDIKController)
local Folder = script.Parent
local TestModel = Folder.Test
local EndEffector = TestModel.HumanoidRootPart.LeftEndEffector-- Folder.EndEffector
local Motors = {}
for i, v in pairs(TestModel:GetDescendants()) do
if v:IsA("Motor6D") then
Motors[v.Name] = v
end
end
local LeftHip = Motors.LeftHip
local LeftKnee = Motors.LeftKnee
local LeftAnkle = Motors.LeftAnkle
local RightHip = Motors.RightHip
local RightKnee = Motors.RightKnee
local RightAnkle = Motors.RightAnkle
local LeftLimb = {LeftHip, LeftKnee, LeftAnkle}
local RightLimb = {RightHip, RightKnee, RightAnkle}
local LeftLimbSolver = IKSolver.new(LeftLimb)
local RightLimbSolver = IKSolver.new(RightLimb)
local CoolDown = false
local PreviousGoal = nil
LeftLimbSolver:GetConstraints()
RightLimbSolver:GetConstraints()
local function SetCoolDown(Duration)
if CoolDown then return false end
CoolDown = true
delay(Duration, function()
CoolDown = false
end)
return true
end
local function UpdateSolver(dt, Solver)
local Goal = (CoolDown and PreviousGoal) or (SetCoolDown(0.3) and EndEffector.WorldPosition)
Solver.LerpAlpha = 0.7 * dt * 60
Solver:CCDIKIterateOnce(Goal)
PreviousGoal = Goal
end
RUS.Heartbeat:Connect(function(dt)
UpdateSolver(dt, LeftLimbSolver)
-- UpdateSolver(dt, RightLimbSolver)
end)
As you can see in the video, the solver stutters a little bit when the goal is close to it. The script proves that I haven’t put any yield functions like wait() that would pause its iteration. Am I doing something wrong (like changing the LerpAlpha or something) or is there a bug that I’m unaware of? Thanks for your help
p.s: I’m not sure if I should post here so let me know if I should switch this to scripting-support thanks
Sorry, I realized I haven’t made it clear that there is a parameter in the second parameter called tolerance.
CCDIKIterateOnce(goalPosition,tolerance,step)
If the limb is within stud distance goal of this tolerance it’ll stop updating as the goal is reached and reduce lag. However this also causes the limb to be unresponsive so try setting tolerance to zero.
Also you didn’t put dt or step into the step parameter as well of the iteration function so it’ll not lerp and not be smooth.
I believe you can try adjusting the position of the Motor6D Joint until you get the behavior you want, but with the default R6 Joint position with the joint located at the torso you will get some weird results.
For reference default R6 rig position with same end effector setup:
Thank you so much for the in-depth explanation! Works phenomenally now
I can’t get over how easy this module is to use… I’ve had so many issues trying to use FABRIK that after a week of trying I just gave up. Amazing module!
Oh just realized having step in IterateUntil is pretty weird, never tested IterateUntil, never had any use of it for now.
So yeah step is deltatime returned from a run service according to API naming conventions RunService | Roblox Creator Documentation. It’s expected to be between [0,1] but theres a safety measure with math.min there so it should be fine, no clamping should be required.
The only function in CCDIK which needs step:
rotate joint from to with Lerp
--Controls the primary CCDIK Method but instead of going fully towards the goal it lerps slowly towards it instead
function CCDIKController:rotateJointFromToWithLerp(motor6DJoint : Motor6D,u,v,axis,step)
local rotationCFrame = getRotationBetween(u,v,axis)
rotationCFrame = motor6DJoint.Part0.CFrame:Inverse()*rotationCFrame*motor6DJoint.Part1.CFrame
rotationCFrame = rotationCFrame-rotationCFrame.Position
local goalC0CFrame = CFrame.new(motor6DJoint.C0.Position)*rotationCFrame
local lerpAlpha = self.LerpAlpha
local currentC0 = motor6DJoint.C0
if step and self.ConstantLerpSpeed then
local angularDistance = VectorUtil.AngleBetween(currentC0.LookVector,goalC0CFrame.LookVector)
local estimatedTime = self.AngularSpeed/angularDistance
lerpAlpha = math.min(step*estimatedTime,1)
end
motor6DJoint.C0 = currentC0:Lerp(goalC0CFrame,lerpAlpha)
end
Ah, seems like I shouldn’t really be using it then. Is there an alternate method for smoothly ‘tweening’ an arm towards a target goal? I’ve noticed the properties such as AngularSpeed, but I’m not fully sure on how to utilize them properly.