R6 IK Directional Procedural Footplanting (like phantom forces)

Hey everyone.

I’ve been working on an R6 IK Procedural Footplanting system, however what I made is not what I want to accomplish.

I’ve made the system to where it sends a ray from your RootPart to the ground and solve the IK for your legs if you’re going up a slope or something.

It is not very optimized but I’ll do the optimizations later.

What I really want is some directional movement in here. Similar to phantom forces.

5zfcxf

(random gif i found)

If you look at the legs, (yes I know it’s sort of hard to see cuz of the fps) you can see some directional IK going on there.

THAT’S what I want to make.

Do they use animations for all the directional movement or is it just math that I probably don’t know about?

Anyways, here’s the script I made so far

StarterPlayerScripts

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid : Humanoid = character:WaitForChild("Humanoid")

local inverseKinematicsModule = require(game.ReplicatedStorage.R6IK)

local newInverseKinematics = inverseKinematicsModule.New(character)

local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = {character}

local function IKLeftLeg()
	local ray = workspace:Raycast(character:FindFirstChild("Left Leg").Position, character:FindFirstChild("HumanoidRootPart").CFrame.UpVector * -100, params)
	if ray then
		newInverseKinematics:LegIK("Left", ray.Position)
	else
		return
	end
end

local function IKRightLeg()
	local ray = workspace:Raycast(character:FindFirstChild("Right Leg").Position, character:FindFirstChild("HumanoidRootPart").CFrame.UpVector * -100, params)
	if ray then
		newInverseKinematics:LegIK("Right", ray.Position)
	else
		return
	end
end



game["Run Service"].PreRender:Connect(function()
	IKLeftLeg()
	IKRightLeg()
	character:FindFirstChild("Left Leg").LocalTransparencyModifier = 0
	character:FindFirstChild("Right Leg").LocalTransparencyModifier = 0
end)

Here’s the module I used

Any help is greatly appreciated. Thank you!

4 Likes

bump bump bump bippity bump bippity bump

I found a post of a (sort of) explanation on how to do this R6 Inverse Kinematics - #79 by TruestBlu

I understand it well just I don’t know where to put stuff and some stuff he’s referring to. (like rfplace or lfplace)

Going off of that post, this is what I’ve built. I’m tired so I don’t have the exact math to make the foot planting right, but this might help you.

--other variables before this

local torsoSwing = 0
local side = false

local RlerpedFoot,LlerpedFoot = Vector3.new(), Vector3.new()

local LeftLegOffset = CFrame.new(-0.5, -3, 0)
local RightLegOffset = CFrame.new(0.5, -3, 0)

local torsoPosition =  Character:WaitForChild("Torso").CFrame
local rfplace, lfplace = (torsoPosition*RightLegOffset).Position, (torsoPosition*LeftLegOffset).Position

game:GetService("RunService").Heartbeat:Connect(function(dt)
	local torsoPosition =  Character:WaitForChild("Torso").CFrame

	torsoSwing = math.sin(tick()*Character.Humanoid.WalkSpeed)
	
	if torsoSwing > 0 and side then
		side = not side
		lfplace = (torsoPosition*LeftLegOffset).Position
	end

	if torsoSwing < 0 and not side then
		side = not side
		rfplace = (torsoPosition*RightLegOffset).Position
	end

	RlerpedFoot = RlerpedFoot:Lerp(rfplace, math.min(1/0.075*dt, 1)/2)
	LlerpedFoot = LlerpedFoot:Lerp(lfplace, math.min(1/0.075*dt, 1)/2)

	local raise = math.sin((RlerpedFoot - rfplace).magnitude/2)
	local raise2 = math.sin((LlerpedFoot - lfplace).magnitude/2)

	IKController:LegIK("Left", LlerpedFoot + Vector3.new(0, (raise2/2), 0))
	IKController:LegIK("Right", RlerpedFoot + Vector3.new(0, (raise/2), 0))
end)

Hope this helps.

1 Like

Cannot exactly write the code right now but I think I know a good starting point.

Essentially, you stretch one leg towards where you want the foot to be planted.
If the foot is closer than the leg’s length, you can move the hip joint forward so that the foot can move closer to the torso.

The torso tilting effect is essentially just reading Part.Velocity or comparing distance between frames and using that to generate tilt to the left/right for the torso.

To break it down into steps…

  • Cast a ray towards the foot’s new position.

  • Displace the hip forward if the distance between the hip and foot position is less than 2 studs, assuming that that’s the length of your leg.

    This will make it look like as if the leg is “bending” it’s knee.

  • Use something like CFrame.lookAt() or CFrame.lookAlong() to make the leg point towards the new foot location.

    Use the Torso.CFrame.LookVector and plug that into the UpVector of CFrame.lookAt().

    This ensures that the leg won’t ever be placed backwards in case the foot is behind the character’s center of mass.

Psuedo code, might not work correctly.

local hip_restpose = right_hip_motor6d.C0.Position -- Make X negative for the other leg.

local hip_bendpose = hip_restpose + Vector3.new(0, 0.5, -.05)

local leg_length = leg.Size.Y -- The leg's length.


local distance = (foot_position - hip_position).Magnitude / leg_length -- Value should range between 0 and 1.

local hip_lerp = hip_restpose:Lerp(hip_bendpose, distance)

local new_hippose = (CFrame.new(torso.Position) * right_hip_motor6d.C0) * CFrame.new(hip_lerp)

new_hippose = CFrame.lookAt(new_hippose.Position, foot_position, Torso.CFrame.LookVector)

right_hip_motor6d.Transform = Torso.CFrame:ToObjectSpace(new_hippose)


This code might not be correct but should get the idea across.

1 Like

I’ll try both of these when I get home. Thanks guys

I understand this a little bit… (not exactly) Im not new to coding, just never dealt with these things before

I think I know how to do the footplanting part, I have to cast a ray downwards from the HumanoidRootPart but a little further, like 3-5 studs forward of the look vector, that will be where it places its foot. The ray will change based on your direction.
Do you think this will work?

As long as you move them by the velocity, so the legs don’t just stay planted forward, moving or not

1 Like

OK I GOT IT WORKING (kind of)

the only problem i really have is that the ray fires in one direction and wont follow your lookvector

heres a video and my script

External Media
local player = game.Players.LocalPlayer
local Character = player.Character or player.CharacterAdded:Wait()
local humanoid : Humanoid = Character:FindFirstChild("Humanoid")

local userInputService = game:GetService("UserInputService")
local runService = game:GetService("RunService")

local IKModule = require(game.ReplicatedStorage.Modules.IKModule)

local IKController = IKModule.New(Character)

local torsoSwing = 0
local side = false

local RlerpedFoot,LlerpedFoot = Vector3.new(), Vector3.new()

local LeftLegOffset = CFrame.new(-0.5, -3, 0)
local RightLegOffset = CFrame.new(0.5, -3, 0)

local torsoPosition : CFrame =  Character:WaitForChild("Torso").CFrame
local rfplace, lfplace = (torsoPosition*RightLegOffset).Position, (torsoPosition*LeftLegOffset).Position

local params = RaycastParams.new()
params.FilterDescendantsInstances = {Character}
params.FilterType = Enum.RaycastFilterType.Exclude

game:GetService("RunService").RenderStepped:Connect(function(dt)
	Character = player.Character or player.CharacterAdded:Wait()
	local torsoPosition : CFrame = Character:WaitForChild("Torso").CFrame
	
	local raycastdir = torsoPosition.LookVector + Vector3.new(0,-5,-5)
	
	print(torsoPosition.LookVector)
	
	local rightray = workspace:Raycast(humanoid.RootPart.CFrame.Position, raycastdir,params)
	local leftray = workspace:Raycast(humanoid.RootPart.CFrame.Position, raycastdir,params)
	
	print(rightray)
	
	torsoSwing = math.sin(tick()*Character.Humanoid.WalkSpeed)
	
	if rightray then
		spawn(function()
			local distance = (humanoid.RootPart.CFrame.Position - rightray.Position).Magnitude
			local p = Instance.new("Part")
				
			p.Anchored = true
			p.CanCollide = false
			p.Size = Vector3.new(0.1, 0.1, distance)
			p.CFrame = CFrame.lookAt(humanoid.RootPart.CFrame.Position, rightray.Position)*CFrame.new(0, 0, -distance/2)
			p.Parent = workspace
		end)
	end
	
	if torsoSwing > 0 and side then
		side = not side
		lfplace = (torsoPosition*LeftLegOffset).Position
		if rightray and rightray.Instance then
			rfplace = (rightray.Position + Vector3.new(RightLegOffset.Position.X,0,0))
		end
	end

	if torsoSwing < 0 and not side then
		side = not side
		rfplace = (torsoPosition*RightLegOffset).Position
		if leftray and leftray.Instance then
			lfplace = (leftray.Position + Vector3.new(LeftLegOffset.Position.X,0,0))
		end
	end

	RlerpedFoot = RlerpedFoot:Lerp(rfplace, math.min(1/0.075*dt, 1))
	LlerpedFoot = LlerpedFoot:Lerp(lfplace, math.min(1/0.075*dt, 1))

	local raise = math.sin((RlerpedFoot - rfplace).magnitude/2)
	local raise2 = math.sin((LlerpedFoot - lfplace).magnitude/2)

	IKController:LegIK("Left", LlerpedFoot + Vector3.new(0, (raise2/2), 0))
	IKController:LegIK("Right", RlerpedFoot + Vector3.new(0, (raise/2), 0))
end)

im happy i got this far, just this one problem to combat and im fully done with this problem

nevermind just fixed it using assemblylinearvelocity

I have one more problem. I swear ill stop bugging you after this. Usually if you go too fast in different directions the legs will stay lifted and not react to the ground or whatever.

Script:

local player = game.Players.LocalPlayer
local Character = player.Character or player.CharacterAdded:Wait()
local humanoid : Humanoid = Character:FindFirstChild("Humanoid")

local userInputService = game:GetService("UserInputService")
local runService = game:GetService("RunService")

local IKModule = require(game.ReplicatedStorage.Modules.IKModule)

local IKController = IKModule.New(Character)

local torsoSwing = 0
local side = false

local RlerpedFoot,LlerpedFoot = Vector3.new(), Vector3.new()

local LeftLegOffset = CFrame.new(-0.5, -3, 0)
local RightLegOffset = CFrame.new(0.5, -3, 0)

local torsoPosition : CFrame =  Character:WaitForChild("Torso").CFrame
local rfplace, lfplace = (torsoPosition*RightLegOffset).Position, (torsoPosition*LeftLegOffset).Position

local params = RaycastParams.new()
params.FilterDescendantsInstances = {Character}
params.FilterType = Enum.RaycastFilterType.Exclude

humanoid.RootPart:WaitForChild("Running"):Destroy()

local Force
local Direction
local Value1
local Value2

local rootjoinc0 = humanoid.RootPart.RootJoint.C0

local raise
local raise2



game:GetService("RunService").RenderStepped:Connect(function(dt)
	local ticknew = tick()
	Character = player.Character or player.CharacterAdded:Wait()
	local torsoPosition : CFrame = Character:WaitForChild("Torso").CFrame
	local torso : BasePart = Character:WaitForChild("Torso")

	local leftraycastdir = torso.AssemblyLinearVelocity + (torsoPosition.RightVector * -2) + (Vector3.new(0,-10,0) + torsoPosition.LookVector * 2)
	local rightraycastdir = torso.AssemblyLinearVelocity + (torsoPosition.RightVector * 2) + (Vector3.new(0,-10,0) + torsoPosition.LookVector * 2)

	local rightray = workspace:Raycast(humanoid.RootPart.Position, rightraycastdir,params)
	local leftray = workspace:Raycast(humanoid.RootPart.Position, leftraycastdir,params)
	
	local lastlfplace = lfplace
	local lastrfplace = rfplace
	
	local rayStandingRight = workspace:Raycast(Character['Right Leg'].Position, Character['Right Leg'].Position - lastrfplace, params)
	local rayStandingLeft = workspace:Raycast(Character['Left Leg'].Position, Character['Left Leg'].Position - lastlfplace, params)
	
	print(humanoid:GetState())
	torsoSwing = math.sin(ticknew*Character.Humanoid.WalkSpeed)

	humanoid.HipHeight = -.25

	if humanoid.MoveDirection.Magnitude < 0.1 then
		if rayStandingLeft then

			lfplace = rayStandingLeft.Position
			local distance = (Character['Left Leg'].Position - rayStandingLeft.Position).Magnitude
			local p = Instance.new("Part")
			p.Anchored = true
			p.CanCollide = false
			p.Size = Vector3.new(0.1, 0.1, distance)
			p.CFrame = CFrame.lookAt(Character['Left Leg'].Position, rayStandingLeft.Position)*CFrame.new(0, 0, -distance/2)

		end
		if rayStandingRight then

			rfplace = rayStandingRight.Position


		end
		if raise or raise2 then
			raise = nil
			raise2 = nil
		end
		--[[if  then
			rayStandingLeft = workspace:Raycast(torsoPosition.Position, leftstanddir ,params)
			lfplace = rayStandingLeft.Position
		end	]]	
	end
	print(leftray.Distance)
	if torsoSwing > 0 and side then
		side = not side
		--lfplace = (torsoPosition*LeftLegOffset).Position
		if rightray and rightray.Instance and humanoid.MoveDirection.Magnitude > 0.1 then
			rfplace = (rightray.Position)
			
			local newfootstep = script.Footstep:Clone()
			newfootstep.Parent = humanoid.RootPart
			newfootstep.PlaybackSpeed = Random.new():NextNumber(.8, 1.2)
			newfootstep:Play()
			game.Debris:AddItem(newfootstep,newfootstep.TimeLength)
		end
		--game.TweenService:Create(humanoid.RootPart.RootJoint,TweenInfo.new(.1,Enum.EasingStyle.Sine,Enum.EasingDirection.Out),{C0 = CFrame.Angles(math.rad(45),0,0)}):Play()
	end

	if torsoSwing < 0 and not side then
		side = not side
		--rfplace = (torsoPosition*RightLegOffset).Position
		if leftray and leftray.Instance and humanoid.MoveDirection.Magnitude > 0.1 then
			lfplace = (leftray.Position)
			local newfootstep = script.Footstep:Clone()
			newfootstep.Parent = humanoid.RootPart
			newfootstep.PlaybackSpeed = Random.new():NextNumber(.8, 1.2)
			newfootstep:Play()
			game.Debris:AddItem(newfootstep,newfootstep.TimeLength)
		end
		--game.TweenService:Create(humanoid.RootPart.RootJoint,TweenInfo.new(.1,Enum.EasingStyle.Sine,Enum.EasingDirection.Out),{C0 = CFrame.Angles(math.rad(-45),0,0)}):Play()
	end

	RlerpedFoot = RlerpedFoot:Lerp(rfplace, math.min(1/0.075*dt, 1)/1.5)
	LlerpedFoot = LlerpedFoot:Lerp(lfplace, math.min(1/0.075*dt, 1)/1.5)

	raise = math.sin((RlerpedFoot - rfplace).magnitude/2.5)
	raise2 = math.sin((LlerpedFoot - lfplace).magnitude/2.5)

	IKController:LegIK("Left", LlerpedFoot + Vector3.new(0, (raise2/2), 0))
	IKController:LegIK("Right", RlerpedFoot + Vector3.new(0, (raise/2), 0))

	Force = humanoid.RootPart.AssemblyLinearVelocity * Vector3.new(1,0,1)
	if Force.Magnitude > 2 then
		
		Direction = Force.Unit	
		Value1 = humanoid.RootPart.CFrame.RightVector:Dot(Direction)
		Value2 = humanoid.RootPart.CFrame.LookVector:Dot(Direction)
	else
		Value1 = 0
		Value2 = 0
	end

	

	--humanoid.RootPart.RootJoint.C0 = humanoid.RootPart.RootJoint.C0:Lerp(rootjoinc0 * CFrame.Angles(math.rad(Value2 * 10), math.rad(-Value1 * 10), 0), 0.2)
end)

bumpppppppingggggggggggggggggg

still no soltuon yettttttttttttttt

im just goign to close this post