R15 Footplanting system (Inverse Kinematics)

image

Pretty nice R15 footplanting system I made for fun, feel free to put it into a local script if you want it to be a bit smoother, but I just chose to make it a server script to save myself the troubles of making replication.


Works with terrain as well. You can see the feet align with the slope of whatever surface they are resting on.

image

Script:

---///Variables\\\---

--Limbs
local Character = script.Parent
local Human = Character:WaitForChild("Humanoid")
local RFoot = Character:WaitForChild("RightFoot")
local LFoot = Character:WaitForChild("LeftFoot")
local Root = Character:WaitForChild("HumanoidRootPart")

--Caches
local TargetCache = Instance.new("Folder", Character)
TargetCache.Name = "Targets"
local IKCache = Instance.new("Folder", Character)
IKCache.Name = "IKControls"


--Target Creation Func
local function CreateTargetFor(Limb : BasePart)
	local Target = Instance.new("Part", TargetCache)
	Target.Anchored = true
	Target.CanCollide = false
	Target.CanQuery = false
	Target.BrickColor = BrickColor.Red()
	Target.Size = Limb.Size
	Target.Transparency = 1
	return Target
end

--Targets
local RTarget = CreateTargetFor(RFoot)
RTarget.Name = "RightTarget"
local LTarget = CreateTargetFor(LFoot)
LTarget.Name = "LeftTarget"


--Control Creation Func
local function CreateIK(Chain, End, Target)
	local Control = Instance.new("IKControl", IKCache)
	Control.ChainRoot = Chain
	Control.EndEffector = End
	Control.Target = Target
	Control.Type = Enum.IKControlType.Transform
	Control.Priority = 999
	Control.Weight = 1
	Control.Enabled = true
	Control.SmoothTime = 0.1
	return Control
end

--Controls
local RControl = CreateIK(Character:WaitForChild("RightUpperLeg"), RFoot, RTarget)
local LControl = CreateIK(Character:WaitForChild("LeftUpperLeg"), LFoot, LTarget)


--Function For Casting
local function getFootCFrame(position, rootLookVector)
	local forward = Vector3.new(rootLookVector.X, 0, rootLookVector.Z).Unit
	local up = Vector3.new(0, 1, 0)
	local right = up:Cross(forward).Unit
	local fixedForward = right:Cross(up).Unit

	return CFrame.fromMatrix(position, right, up)
end


--Raycast Parameters
local RayParams = RaycastParams.new()
RayParams.FilterType = Enum.RaycastFilterType.Exclude
RayParams.FilterDescendantsInstances = {Character}

local Direction = Vector3.new(0, -3, 0)

--Script
local function PlantFeet()
	local LOrigin = Root.Position - Root.CFrame.RightVector * 0.5 - Vector3.new(0, 1, 0)
	local ROrigin = Root.Position + Root.CFrame.RightVector * 0.5 - Vector3.new(0, 1, 0)
	local LRay = workspace:Raycast(LOrigin, Direction, RayParams)
	local RRay = workspace:Raycast(ROrigin, Direction, RayParams)

	if LRay then
		local Position = LRay.Position + Vector3.new(0, LFoot.Size.Y / 2, 0)
		local Normal = LRay.Normal
		local Forward = Root.CFrame.LookVector
		local Right = Normal:Cross(Forward).Unit
		local Up = Normal
		local AlignedForward = Right:Cross(Up).Unit
		LTarget.CFrame = CFrame.fromMatrix(Position, AlignedForward, Up) * CFrame.fromEulerAngles(0, math.rad(-90), 0)
	else
		local position = LOrigin + Direction
		local rootLook = Root.CFrame.LookVector
		LTarget.CFrame = getFootCFrame(position, rootLook) * CFrame.Angles(math.rad(50), math.rad(180), 0)
	end

	if RRay then
		local Position = RRay.Position + Vector3.new(0, RFoot.Size.Y / 2, 0)
		local Normal = RRay.Normal
		local Forward = Root.CFrame.LookVector
		local Right = Normal:Cross(Forward).Unit
		local Up = Normal
		local AlignedForward = Right:Cross(Up).Unit
		RTarget.CFrame = CFrame.fromMatrix(Position, AlignedForward, Up) * CFrame.fromEulerAngles(0, math.rad(-90), 0)
	else
		local position = ROrigin + Direction
		local rootLook = Root.CFrame.LookVector
		RTarget.CFrame = getFootCFrame(position, rootLook) * CFrame.Angles(math.rad(50), math.rad(180), 0)
	end
end


--Smoothing
local TweenService = game:GetService("TweenService")
local Info = TweenInfo.new(0.15)

local R_On = TweenService:Create(RControl, Info, {Weight = 1})
local R_Off = TweenService:Create(RControl, Info, {Weight = 0.001})

local L_On = TweenService:Create(LControl, Info, {Weight = 1})
local L_Off = TweenService:Create(LControl, Info, {Weight = 0.001})

local function Toggle(val : boolean)
	if val == true then
		L_On:Play()
		R_On:Play()
		task.wait(Info.Time)
		LControl.Enabled = true
		RControl.Enabled = true
	else
		L_Off:Play()
		R_Off:Play()
		task.wait(Info.Time)
		LControl.Enabled = false
		RControl.Enabled = false
	end
end

game:GetService("RunService").Heartbeat:Connect(function()
	if Human.MoveDirection == Vector3.zero and Human:GetState() == Enum.HumanoidStateType.Running then
		PlantFeet()
		Toggle(true)
	else
		Toggle(false)
	end
end)
6 Likes