Issue
Hello, with the recent release of IKControls, I wanted to attempt to create foot-planting for my r15 Characters. After about 1 1/2 hours of playing around, I ended up with a somewhat working result; however, it is very unreliable and unrealistic. Any tips or mistakes that I could fix that could improve it would be greatly appreciated because I’m not really the greatest at these things lol.
Screenshots of unrealistic footplanting:
https://gyazo.com/ef8e9383283cba00b9833bf64452c096
https://gyazo.com/7a53e995088b138164304b69819121ad
https://gyazo.com/d9f9f8f43e9ea9f16344df360b101986
Individual Leg Solver
Bound to RunService.Heartbeat
local function Update(Name: string)
--//LegLimbs
local UpperLeg = Character:FindFirstChild(`{Name}UpperLeg`)
local LowerLeg = Character:FindFirstChild(`{Name}LowerLeg`)
local Foot = Character:FindFirstChild(`{Name}Foot`)
if not UpperLeg or not LowerLeg or not Foot then return end
--//IkControl
local IkControl = Humanoid:FindFirstChild(`_{Name}_FootPlanting_Control`)
if not IkControl then
IkControl = Instance.new("IKControl")
IkControl.Name = `_{Name}_FootPlanting_Control`
IkControl.Weight = 1
IkControl.Priority = 0
IkControl.SmoothTime = .2
IkControl.Enabled = false
IkControl.Parent = Humanoid
end
local ChainRoot = IkControl.ChainRoot
if not ChainRoot then
ChainRoot = UpperLeg:FindFirstChild(`{Name}Hip`)
IkControl.ChainRoot = ChainRoot
end
local Pole = IkControl.Pole
if not Pole then
Pole = LowerLeg:FindFirstChild(`{Name}Knee`)
--IkControl.Pole = Pole
end
local EndEffector = IkControl.EndEffector
if not EndEffector then
EndEffector = Foot:FindFirstChild(`{Name}FootAttachment`)
IkControl.EndEffector = EndEffector
end
local Target = IkControl.Target
if not Target then
Target = Instance.new("Attachment")
IkControl.Target = Target
Target.Name = `_{Name}_FootPlanting_Control_Target`
Target.Parent = Humanoid.RootPart
end
local AnkleJoint = Foot:FindFirstChild(`{Name}Ankle`) --//try making this the endeffector?!?!?1 (nerp didnt work)
if not ChainRoot or not Pole or not EndEffector or not Target or not AnkleJoint then IkControl.Enabled = false; return end
local IsStateRunning = Humanoid:GetState() == Enum.HumanoidStateType.Running
if not IsStateRunning then IkControl.Enabled = false; return end
local IsMoving = false
if (Humanoid.MoveDirection * Vector3.new(1, 0, 1)).Magnitude > .1 and Humanoid.RootPart.AssemblyLinearVelocity.Magnitude > 1 then
IsMoving = true
end
if not IsMoving then return end
local HipJointWorldCFrame: CFrame = ChainRoot.Part0.CFrame * ChainRoot.C0
local KneePart1CF: CFrame = HipJointWorldCFrame * ChainRoot.Transform * ChainRoot.C1:Inverse()
local FootPart1CF: CFrame = KneePart1CF * Pole.C0 * Pole.Transform * Pole.C1:Inverse()
local ExpectedFootPlacement: CFrame = FootPart1CF * AnkleJoint.C0 * AnkleJoint.Transform * AnkleJoint.C1:Inverse()
local ExpectedTargetPlacement: CFrame = ExpectedFootPlacement * EndEffector.CFrame
--Frame --//remember endeffector is an att so .cframe is local
local RayDirection: Vector3 = ExpectedFootPlacement.Position - HipJointWorldCFrame.Position
local RaycastResult: RaycastResult = workspace:Raycast(HipJointWorldCFrame.Position, RayDirection, RayParams)
if RaycastResult and RaycastResult.Instance then
local rX, _, rZ = CFrame.lookAt(RaycastResult.Position, RaycastResult.Position + RaycastResult.Normal):ToOrientation()
rX = math.max(rX, 1.5)
rZ = math.max(rZ, 1.5)
IkControl.Target.WorldCFrame = (ExpectedTargetPlacement.Rotation + RaycastResult.Position) --* CFrame.Angles(rX, 0, rZ)
IkControl.Enabled = true
else
IkControl.Enabled = false
end
if DEBUG then
local DebugPart = workspace:FindFirstChild("_FootPlantingDebug")
if not DebugPart then
DebugPart = Instance.new("Part")
DebugPart.Name = "_FootPlantingDebug"
DebugPart.Anchored = true
DebugPart.CanCollide = false
DebugPart.Transparency = 1
DebugPart.Parent = workspace
end
local DebugAtt = Instance.new("Attachment")
DebugAtt.Name = `{Name}DebugAttachment`
DebugAtt.Parent = DebugPart
DebugAtt.WorldCFrame = IkControl.Target.WorldCFrame
DebugAtt.Visible = true
Debris:AddItem(DebugAtt, 60)
end
end
Explanation to Code
I created a function bound to heartbeat that calls a local function called Update(, passing the Name of the leg I want to update. The function would get the parts of the leg and then find or create an IKControl Instance and set it up. If it has everything it needs to update the leg, it will then see if the Humanoid’s state is running; otherwise, it would return and disable the IKControl so it would’nt ruin the action. Afterwards, it would check if the player is actually moving using a combination of Humanoid.MoveDirection and Humanoid.RootPart.AssemblyLinearVelocity. Then, if the character is “idle”, it would return and not disable the IKControl if it was enabled. Finally, we get to the complicated–at least to me–part based off this post (Prototype R15 Animation IKControl Footplant by @dthecoolest). If I’m truly honest, I have no idea what it really does other than subtract some CFrames. After raycasting, if there’s a result, the IKControl would stay enabled, and if there wasn’t it would be disabled. If the IKControl is enabled, then I get the angle of intersection using the RaycastResult.Normal and multiply it get only the X and Z. Afterwards, I set the IKControl.Target attachment’s WorldCFrame to the Intersection CFrame added to the ExpectedTargetPlacement CFrame and then added to RaycastResult.Position. Finally, I create an Attachment that shows where the Target was during the update.
Full Script
FootPlantingController.lua (4.8 KB)
I apologize for any mistakes with this post; it’s my first time lol.