Parkour Ledge Script

Hello there.
I began thinking about how to create a proper ledge climb script, however, I got some problems with it.

It only ledges properly on one side

If I try to ledge on the other sides of that wall, however, the rotation will be weird and the character won't face the wall like on the first image


Also when I rotate my camera in first person, it rotates the entire avatar, despite the HumanoidRootPart being anchored

image

If you spot any problems in the script attached below, please let me know. Thank you for your time!

local plr = game.Players.LocalPlayer
local Character = plr.Character or plr.CharacterAdded:Wait()
local Root = Character:WaitForChild("HumanoidRootPart")
local Head = Character:WaitForChild("Head")
local Humanoid = Character:WaitForChild("Humanoid")
local CA = Humanoid:LoadAnimation(script:WaitForChild("ClimbAnim"))
local HA = Humanoid:LoadAnimation(script:WaitForChild("HoldAnim"))
local TouchGui = plr:WaitForChild("PlayerGui"):FindFirstChild("TouchGui")
local UIS = game:GetService("UserInputService")

local ledgeAvailable = true -- can you ledge or not
local isHolding = false -- is ledging or not
local ledgeDeb = false -- ledge debounce
local isClimbing = false -- is climbing or not

local function detachFromLedge()
	Root.Anchored = false
	isHolding = false
	isClimbing = false
	HA:Stop()
	CA:Stop()
end

local function climb()
	local velocity = Instance.new("BodyVelocity", Root)
	velocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
	velocity.Velocity = Root.CFrame.LookVector * 10 + Vector3.new(0, 40, 0)
	isHolding = false
	CA:Play()
	HA:Stop()
	Root.Anchored = false
	game.Debris:AddItem(velocity, .15)
	isClimbing = true
end

UIS.InputBegan:Connect(function(input, gameProcessed)
	if gameProcessed then return end
	if not isHolding then return end
	if input.KeyCode == Enum.KeyCode.Space then
		climb()
	end
end)

while wait() do
	local ray = Ray.new(Head.CFrame.p, Head.CFrame.LookVector * 5)
	local part, position = workspace:FindPartOnRay(ray, Character)

	if part and ledgeAvailable and not isHolding then
		if part.Size.Y >= 7 then
			local ledgeYPos = part.Position.Y + (part.Size.Y / 2)
			local headYPos = Head.Position.Y
			local isHeadWithinLedgeYPos = headYPos >= ledgeYPos - 1 and headYPos <= ledgeYPos

			if isHeadWithinLedgeYPos and Humanoid.FloorMaterial == Enum.Material.Air and Root.Velocity.Y <= 0 then
				Root.Anchored = true
				isHolding = true
				HA:Play()
				ledgeAvailable = false
			end
		end
	end

	if isHolding and not isClimbing and not ledgeDeb then
		Root.CFrame = CFrame.new(position) * CFrame.new(0, -1.5, 0) *  CFrame.Angles(0, -math.pi, 0)
		ledgeDeb = true
	end

	if isClimbing and CA.IsPlaying == false then
		detachFromLedge()
		ledgeAvailable = true
		ledgeDeb = false
	end
end

With raycasting i would do 2 raycasts like this -

1 Like