Parkour System Problem

I’ve been making a parkour system, mainly for testing, and I’ve found a problem. Everything was working fine, but after I added in a wall run script, the wall climb, ledge climb, and vault all broke kind of.
Here is a video of what is broken:

(I apologize about the odd formatting)

Here are all of the scripts:
LedgeClimb Local Script:

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

ledgeavailable = true
holding = false

while game:GetService("RunService").Heartbeat:Wait() do
	local r = Ray.new(Head.CFrame.p, Head.CFrame.LookVector * 5)
	local part,position = workspace:FindPartOnRay(r,Character)

	if part and ledgeavailable and not holding then
		if part.Parent.Name ~= "vaultableObjects" and part.Parent.Name ~= "Climbable" and part.Parent.Name ~= "RUNIT" then
			if part.Parent.Name =="Ledges" then
		if part.Size.Y >= 7 then
			if Head.Position.Y >= (part.Position.Y + (part.Size.Y / 2)) - 1 and Head.Position.Y <= part.Position.Y + (part.Size.Y / 2) and Hum.FloorMaterial == Enum.Material.Air and Root.Velocity.Y <= 0 then
				Root.Anchored = true holding = true HA:Play() ledgeavailable = false
			end
		end
	end

	function climb()
		local Vele = Instance.new("BodyVelocity",Root)
		Root.Anchored = false
		Vele.MaxForce = Vector3.new(1,1,1) * math.huge
		Vele.Velocity = Root.CFrame.LookVector * 10 + Vector3.new(0,30,0)
		HA:Stop() CA:Play()
		game.Debris:AddItem(Vele,.15)
		holding = false
		wait(.75)
		ledgeavailable = true
	end

	UIS.InputBegan:Connect(function(Key,Chat)
		if not holding then return end 
		if Key.KeyCode == Enum.KeyCode.Space and not Chat then
			climb()
		end
	end)

	if TouchGui then
		TouchGui:WaitForChild("TouchControlFrame"):WaitForChild("JumpButton").MouseButton1Click:Connect(function()
			if not holding then return end climb()
		end)
	end
	end
	end
	end

Vault Local Script:

local plr = game:GetService("Players").LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local HRP = char:WaitForChild("HumanoidRootPart")
local Hum = char:WaitForChild("Humanoid")
local CA = Hum:LoadAnimation(script:WaitForChild("ClimbAnim"))

local vaultavail = true

while game:GetService("RunService").RenderStepped:Wait() do
	local r = Ray.new(HRP.Position, HRP.CFrame.LookVector * 7 + HRP.CFrame.UpVector * -5)
	local part = workspace:FindPartOnRay(r,char)

	if part and vaultavail then
		if part.Name ~= "Baseplate" then
			if part.Name ~= "SpawnLocation" then
			if part.Parent.Name ~= "Climbable" and part.Parent.Name ~= "Ledges" and part.Parent.Name ~= "RUNIT" then
			if part.Parent.Name == "vaultableObjects" then
			if Hum.FloorMaterial ~= Enum.Material.Air then
				vaultavail = false
				local Vel = Instance.new("BodyVelocity")
				Vel.Parent = HRP
				Vel.Velocity = Vector3.new(0,0,0)
				Vel.MaxForce = Vector3.new(1,1,1) * math.huge
				Vel.Velocity = HRP.CFrame.LookVector * 20 + Vector3.new(0,30,0)
				CA:Play()
				game.Debris:AddItem(Vel, .15)
				wait(0.75)
				vaultavail = true
					end
			end
		end
	end
	end
	end
	end

WallClimb Local Script

local UIS = game:GetService("UserInputService")

local plr = game.Players.LocalPlayer
local Char = plr.Character or plr.CharacterAdded:Wait()
local HRP = Char:WaitForChild("HumanoidRootPart")
local Hum = Char:WaitForChild("Humanoid")
local Head = Char:WaitForChild("Head")

local HittingWall = false
local OnWall = false
local climbing = false

local Normal
local Pos
local Wall
local previousWall

function upclimb()
	if HittingWall == true then
		HRP.Velocity = Vector3.new(0,0,0)
		HRP.Anchored = false
		if OnWall then
			if playAnim then 
				playAnim:Stop()
			end
			local bv = HRP:FindFirstChild('BV')
			bv.Velocity = HRP.CFrame.LookVector * 0 + Vector3.new(0, 12.5, 0)
			local animation = Instance.new("Animation")
			animation.AnimationId = 'rbxassetid://7747483051'
			local playAnim = Char.Humanoid:LoadAnimation(animation)
			playAnim:Play()
		end
	end
end

function downclimb()
	if HittingWall == true then
		HRP.Velocity = Vector3.new(0,0,0)
		HRP.Anchored = false
		if OnWall then
			if playAnim then 
				playAnim:Stop()
			end
			local bv = HRP:FindFirstChild('BV')
			bv.Velocity = HRP.CFrame.LookVector * 0 + Vector3.new(0, -12.5, 0)
			local animation = Instance.new("Animation")
			animation.AnimationId = 'rbxassetid://7747483051'
			local playAnim = Char.Humanoid:LoadAnimation(animation)
			playAnim:Play()
		end
	end
end

function upclimboff()
	HRP.Anchored = true
	if playAnim then 
		playAnim:Stop()
	end
	local bv = HRP:FindFirstChild('BV')
	bv.Velocity = HRP.CFrame.LookVector * 0 + Vector3.new(0, bv.Velocity.Y - 12.5, 0)
end

function downclimboff()
	HRP.Anchored = true
	if playAnim then 
		playAnim:Stop()
	end
	local bv = HRP:FindFirstChild('BV')
	bv.Velocity = HRP.CFrame.LookVector * 0 + Vector3.new(0, bv.Velocity.Y + 12.5, 0)
end

spawn(function()
	while game:GetService('RunService').RenderStepped:Wait() do
		local r = Ray.new(Head.CFrame.p,Head.CFrame.LookVector * 5)
		local hit, pos, normal = workspace:FindPartOnRay(r,Char)

		if hit then
			if hit.Parent.Name ~= "Ledges" and hit.Parent.Name ~= "RUNIT" and hit.Parent.Name ~= "vaultableObjects" then
			if hit.Parent.Name == "Climbable" then
				HittingWall = true
				if playAnim then 
					playAnim:Stop()
				end

				Pos = Pos 
				Normal = normal
				Wall = hit
				previousWall = hit
				end
				end
		end

		if not hit then
			if HRP:FindFirstChild("BV") and OnWall then
				local bv = HRP:FindFirstChild("BV")
				bv.Velocity = Vector3.new(0,0,0)
				bv.Velocity = HRP.CFrame.lookVector * 10 + Vector3.new(0,40,0)

				game.Debris:AddItem(bv,.15)
				OnWall = false
				Hum.AutoRotate = true
				Hum.PlatformStand = false

			elseif HRP:FindFirstChild("BV") == nil and OnWall then
				local bv = Instance.new("BodyVelocity",HRP)
				bv.MaxForce = Vector3.new(1,1,1) * math.huge
				bv.Velocity = HRP.CFrame.LookVector * 10 + Vector3.new(0,40,0)

				game.Debris:AddItem(bv,.15)
				OnWall = false
				Hum.AutoRotate = true
				Hum.PlatformStand = false

			end

			Pos = nil
			normal = nil
		end
		wait()
	end
end)


UIS.InputBegan:Connect(function(Key,Chat)
	if Key.KeyCode == Enum.KeyCode.Space then
		climbing = not climbing
		if HittingWall then
			if climbing == true then
				OnWall = true
				HRP.CFrame = CFrame.new(HRP.CFrame.p,Vector3.new(HRP.Position.X - Normal.x,HRP.Position.Y,HRP.Position.Z - Normal.z)) 

				local bv = Instance.new("BodyVelocity",HRP)
				bv.MaxForce = Vector3.new(1,1,1) * math.huge
				bv.Velocity = HRP.CFrame.LookVector * 0
				bv.Name = "BV"

				Char.Humanoid.AutoRotate = false
				Char.Humanoid.PlatformStand = true
			else
				HRP:FindFirstChild("BV"):Destroy()
				Char.Humanoid.AutoRotate = true
				Char.Humanoid.PlatformStand = false
				OnWall = false
			end
		end

	elseif Key.KeyCode == Enum.KeyCode.W and not Chat then
		upclimb()
	elseif Key.KeyCode == Enum.KeyCode.S and not Chat then
		downclimb()
	end
end)

UIS.InputEnded:Connect(function(Key,Chat)
	if Key.KeyCode == Enum.KeyCode.W and not Chat and OnWall then
		upclimboff()

	elseif Key.KeyCode == Enum.KeyCode.S and not Chat and OnWall then
		downclimboff()
	end
	
end)

Finally, the infamous Wall Run local script:

local char = game:GetService("Players").LocalPlayer.Character or game:GetService("Players").LocalPlayer.CharacterAdded:Wait()
local humRP = char:WaitForChild("HumanoidRootPart")
local Hum = char:WaitForChild("Humanoid")
local ava = true
local has = false
local hold = false
local left = false
local right = false
local leftplaying = false
local rightplaying = false
local LA = Hum:LoadAnimation(script:WaitForChild("RunAnim"))
local RA = Hum:LoadAnimation(script:WaitForChild("RunRightAnim"))
local Vel = Instance.new("BodyVelocity", humRP)
local  BGO = Instance.new("BodyGyro")
BGO.D = 100
BGO.P = 10000
BGO.MaxTorque = Vector3.new(0,0,0)
BGO.Name = "Rot"
Vel.MaxForce = Vector3.new(0,0,0)
Vel.Name = "Vel"

function render()
	local r = Ray.new(humRP.CFrame.p, humRP.CFrame.rightVector * -3)
	local r2 = Ray.new(humRP.CFrame.p, humRP.CFrame.rightVector * 3)
	local part,position,normal = workspace:FindPartOnRayWithIgnoreList(r, {char})
	local part2,position2,normal2 = workspace:FindPartOnRayWithIgnoreList(r, {char})
	local cross = Vector3.new(0,1,0):Cross(normal)
	local cross2 = Vector3.new(0,1,0):Cross(normal2)
	if (part and ava) or (part2 and ava) then
		if (part and part.Parent.Name == "RUNIT")  or  (part2 and part2.Parent.Name == "RUNIT")  then
			if part then
				humRP.CFrame = CFrame.new(humRP.CFrame.p, Vector3.new(humRP.Position.X + cross.x, humRP.Position.Y, humRP.Position.Z + cross.Z))
			elseif part2 then
				humRP.CFrame = CFrame.new(humRP.CFrame.p, Vector3.new(humRP.Position.X - cross2.x, humRP.Position.Y, humRP.Position.Z - cross2.Z))
			end
			has = true
			hold = true
			if hold then
				BGO.CFrame = humRP.CFrame
				BGO.MaxTorque = Vector3.new(1,1,1) * math.huge
				Vel.MaxForce =  Vector3.new(1,1,1) * math.huge
				hold = false
				if part then
					Vel.Velocity = cross * 50
					left = true
				else if part2 then
						Vel.Velocity = -cross2 * 50
						right = true
					end
				end
				
			end
			
		end
	elseif (ava and not part and not part2 and has and not hold) then
		BGO.MaxTorque = Vector3.new(0,0,0)
		Vel.MaxForce = Vector3.new(0,0,0)
		ava = false
		left = false
		right = false
		LA:Stop() RA:Stop()
		if has then 
			wait(0.5)
			ava = true
			has = false
			
		end
	end

	if left and not leftplaying then
		LA:Play() leftplaying = true
	elseif not left then
		LA:Stop() leftplaying = false
	end
	if right and not rightplaying then
		RA:Play() rightplaying = true
	elseif not left then
		RA:Stop() rightplaying = false
	end
end

game:GetService("RunService").RenderStepped:Connect(function()
	render()
end)

Here is an image of it all in the explorer, the parts are organized in folders, so that if I need to add another part I can just put it into one of the folders, and the mechanics for the folder will work on it:

Have you tried putting a value to see if the player is already doing an action? Also, it might be better to just do the raycast on jump request. It might also be best to just have everything in one script

Well, I could make a bool value if that’s what your suggesting. Also, how would I alter it to do the ray cast on jump idea?

I forgot what it’s parented to but it’s .JumpRequest. It might be UserInputService

edit: I got the api page UserInputService | Roblox Creator Documentation

Okeh, Let me attempt those ideas real quick. Do you know though, what error is occuring that’s causing these problems?

Well, is there any errors in the output? If not I’m assuming it’s just the scripts overlapping each other

Apparently, I messed up adding bool values in.
Could you check what is wrong in the scripts?
It says this error: 15:45:35.794 Workspace.NarutoandGokuUi101.WallClimb:81: attempt to index boolean with 'Value' - Client - WallClimb:81
Here’s a download file to the game:parkourtesting.rbxl (47.0 KB)

I think the main issue is that all three scripts are seperately running, and it’s possible that they could be activating functions at the same time, causing problems.

What you could try doing is making a handler/single script for all of these so you can try controlling where/when the functions can be used, maybe with boolean checks too to determine if one move can be used or not.

Welp, I tried and was unable to. Do you have a simpler way of going about this?

Ah! Sorry, I didn’t see that, but as of now I’m quite tired so I’ll try helping tomorrow

Oh, thats fine. I also was very tired.

I’ve also found a new issue, where after wall climbing, spamming space, the key that I use to essentially latch the player onto the wall, causes them to start ascending.

Not too sure unfortunately. A lot of the scripts do use Runservice loops for running code that would check if the player can do the parkour moves. I would suggest making making those into variables or using :BindToRenderStep so you can easily disconnect the loops when possible.

local WallRunCode = RunService.HeartBeat:Connect(function()
         -- Code here
end)

WallRunCode:Disconnect()
function WallRunCode()
       -- expected code here
end

-- Bind the function above to the binding named "WallRunning"
RunService:BindToRenderStep("WallRunning", 1, WallRunCode)
-- Unbind the function bound to "WallRunning"
RunService:UnbindFromRenderStep("WallRunning")

For inputs, I would suggest making seperate functions that you can activate with either ContextActionService/UserInputService, granted the runservice loops above would instead enable/disable boolvalues, so using the seperate functions here would only work when those boolvalues are true or not.

Okeh. So what I can understand, is making a boolean value and store it in the character, then check if the boolean value is true, for example a vaulting boolean value, then if it is, it would run the code? And if not, it would disconnect? Did I understand properly? Sorry if not, I’m still learning.

Er, not really.

For example, the wall run script has a function that constantly checks for walls, and will automatically force the player to wall run on that wall. However, instead you could make it only check for the walls, in which you can probably make it assign the found wall data to a few empty variables:

local WallFound
local WallDirection
local WallSide

…or something like that.

To be honest though, I think it’s better if you’re able to reform the system/remake a parkour system of your own, since the issues present here are a little difficult to fix without trying to manage them all at once (they operate seperately after all, so there’s no telling if other moves are being used while you’re able to use a current move, resulting in some bugs.)

Oh, okay. Welp, time to pull my hair out trying to remake a better version lol. Thanks for trying to help though. :sad:

1 Like

Of course, no hard feelings whatsoever! I noticed that the code itself is a little hard to read, so I figured it would’ve been difficult for you to be able to manage them easily.

Best of luck! :wink:

1 Like