Help with Wall Running Script Raycasting

I am trying to implement a wall running feature into my game but the problem is that it only works for one side of a part, for the other sides my character is flung to a different direction or completely breaks the debounce I have in my script. Here is my script and a couple of videos.

Wall Running working on one side but breaking on the other

Wall Running sending me flying to a completely different direction

local uis = game:GetService("UserInputService")

local hrp = script.Parent:FindFirstChild("HumanoidRootPart")
local humanoid = script.Parent:FindFirstChild("Humanoid")
local attachment = Instance.new("Attachment")
attachment.Parent = hrp

local db = false
local velocity

hrp.Touched:Connect(function(hit)
	
	if uis:IsKeyDown(Enum.KeyCode.Space) then
		
		if humanoid:GetState(Enum.HumanoidStateType.Freefall) then
			
			if hit.Name == "wall" then
				
				if not db then
					db = true
					
					local raycastResult = workspace:Raycast(hrp.Position, hit.Position)
					local gravity = Instance.new("LinearVelocity")
					
					gravity.Name = "gravity"
					gravity.Parent = hrp
					gravity.Attachment0 = attachment

					if raycastResult then
						
						if math.abs(raycastResult.Normal.Z) > math.abs(raycastResult.Normal.X) then
							velocity = Vector3.new(
								humanoid.MoveDirection.X * 50, 
								-3, 
								-raycastResult.Normal.Z * 1000
							)
							print("normal z is LESS than x")
							print(raycastResult.Normal)
						end
						
						if math.abs(raycastResult.Normal.Z) < math.abs(raycastResult.Normal.X) then
							velocity = Vector3.new(
								-raycastResult.Normal.X * 1000, 
								-3, 
								humanoid.MoveDirection.Z * 50
							)
							print("normal z is MORE than x")
							print(raycastResult.Normal)
						end
						
						gravity.MaxForce = 3000
						gravity.VectorVelocity = velocity
						
						repeat
							wait()
						until humanoid:GetState() == Enum.HumanoidStateType.Landed
						
						gravity:Destroy()
						db = false
						print("db false")
					end
				else
					print("delay")
				end	
			end
		end
	end
end)

edit: here is some new code while i was trying to figure out how to fix my problem

local uis = game:GetService("UserInputService")

local hrp = script.Parent:FindFirstChild("HumanoidRootPart")
local humanoid = script.Parent:FindFirstChild("Humanoid")
local attachment = Instance.new("Attachment")
attachment.Parent = hrp

local isGrounded = script.Parent.Values.isGrounded

local function isZero(numA, numB)
	local sol = numA / numB
	if sol == 0 then
		return numB
	else
		return sol
	end
end

humanoid.StateChanged:Connect(function()
	if humanoid:GetState() == Enum.HumanoidStateType.Freefall then
		isGrounded = false
	elseif humanoid:GetState() == Enum.HumanoidStateType.Landed or humanoid:GetState() == Enum.HumanoidStateType.Climbing then
		isGrounded = true
	end
end)

hrp.Touched:Connect(function(hit)
	if not isGrounded then

		if uis:IsKeyDown(Enum.KeyCode.Space) then

			local raycastParams = RaycastParams.new()
			raycastParams.FilterType = Enum.RaycastFilterType.Whitelist
			raycastParams.FilterDescendantsInstances = {workspace.wall}
			local raycastResult = workspace:Raycast(hrp.Position, hit.Position, raycastParams)
			
			local gravity = Instance.new("LinearVelocity")
			gravity.Name = "gravity"
			gravity.Parent = hrp
			gravity.Attachment0 = attachment

			if raycastResult then
				local velocity = Vector3.new(
					isZero(-raycastResult.Normal.X, humanoid.MoveDirection.X), -3/50,
					isZero(-raycastResult.Normal.Z, humanoid.MoveDirection.Z)
				) * 50
				print("wallforce	"..tostring(raycastResult.Normal))
				print("movedirection	"..tostring(humanoid.MoveDirection))
				gravity.MaxForce = 3000
				gravity.VectorVelocity = velocity
				print("NEWvelocity		"..tostring(velocity))
				wait(2)
				gravity:Destroy()
			end	
		end
	end
end)
1 Like

I’m also making a wall running system and while making something similar i encountered the same problem.
So I think this could possibly help you.

--This doesn't need to check the axis
local A = Vector3.new(raycastResult.Normal.Z,0,raycastResult.Normal.X)

local RayCastDirection = (hrp.Position - hit.Position).unit
--Gets the directional Vector between the two points

local B = Vector3.new(RayCastDirection.Z,0,RaycastDirection.X)

local VelocitySpeed = 3

local Velocity = (((A * B) * script.Parent.Humanoid.WalkSpeed * Velocity) * LookVector) * -1
Velocity = Vector3.new(Velocity.X, -3, Velocity.Z)
gravity.VectorVelocity = Velocity

--Walk Speed isn't necessary, just there because my system is based off speed.

I hope this solves your problem.

unfortunately it hasn’t, i am thinking it might have to do with the math we used or a problem regarding the raycasting. thanks anyways

Ok so trying to clean up my code, I broke my script and realized what was wrong.

--RayCastDirection needs to be replaced with this
local RayCastDirection = (raycastResult.Position - hrp.Position).unit 

--and also, there was a typo in this of "Velocity" instead of "VelocitySpeed"
local Velocity = (((A * B) * VelocitySpeed) * LookVector) * -1

--LookVector can be from any body part, but I use Head.CFrame.LookVector

Hopefully this finally fixes it and if not oh well, and if not it’s probably due to the difference in the way our scripts work. I would try having a jump or double jump to trigger the raycast instead of using the touched event and seeing how that works.

sorry but it still doesnt fix the problem, but i have found another solution that could help the both of us. So i have the script fire two rays from the sides of the player detecting collisions instead of a single ray pointing towards a wall’s position.

local rayL = workspace:Raycast(hrp.Position, hrp.CFrame.RightVector * -10, raycastParams)
local rayR = workspace:Raycast(hrp.Position, hrp.CFrame.RightVector * 10, raycastParams)

if rayL or rayR then
				
	if rayL then
		-- insert code if the left ray detects a collision
	elseif rayR then
		-- insert code if the right ray detects a collision
	end

end

and as well as my full code (works with angled walls too)

local uis = game:GetService("UserInputService")

local hrp = script.Parent:FindFirstChild("HumanoidRootPart")
local humanoid = script.Parent:FindFirstChild("Humanoid")

-- Values is a folder I created in StarterCharacterScripts
local isGrounded = script.Parent:WaitForChild("Values").isGrounded

humanoid.StateChanged:Connect(function()
	if humanoid:GetState() == Enum.HumanoidStateType.Freefall then
		isGrounded = false
	elseif humanoid:GetState() == Enum.HumanoidStateType.Landed or humanoid:GetState() == Enum.HumanoidStateType.Climbing then
		isGrounded = true
	end
end)

hrp.Touched:Connect(function(hit)
	
	if isGrounded then
		return
	end
	
	if uis:IsKeyDown(Enum.KeyCode.Space) and uis:IsKeyDown(Enum.KeyCode.LeftShift) then

		local raycastParams = RaycastParams.new()
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
		raycastParams.FilterDescendantsInstances = {script.Parent}
		
		local attachment = Instance.new("Attachment")
		attachment.Parent = hrp
		
		local wallrunForce = Instance.new("LinearVelocity")
		wallrunForce.Name = "wallrunForce"
		wallrunForce.Attachment0 = attachment
		
		
		
		local rayL = workspace:Raycast(hrp.Position, hrp.CFrame.RightVector * -10, raycastParams)
		local rayR = workspace:Raycast(hrp.Position, hrp.CFrame.RightVector * 10, raycastParams)

		if rayL or rayR then
					
			local velocity
	
			wallrunForce.Parent = hrp
			
			-- swapped axis is intentional
			if rayL then
							
				if math.abs(rayL.Normal.Z) == 1 then
					
					velocity = Vector3.new(rayL.Normal.Z, -3/50, rayL.Normal.X) * 50
					
				elseif math.abs(rayL.Normal.X) == 1 then
					
					velocity = -Vector3.new(rayL.Normal.Z, 3/50, rayL.Normal.X) * 50
					
				else
					
					velocity = Vector3.new(rayL.Normal.Z, -3/50, -rayL.Normal.X) * 50
					
				end

			elseif rayR then

				if math.abs(rayR.Normal.Z) == 1 then
					
					velocity = -Vector3.new(rayR.Normal.Z, 3/50, rayR.Normal.X) * 50
					
				elseif math.abs(rayR.Normal.X) == 1 then
					
					velocity = Vector3.new(rayR.Normal.Z, -3/50, rayR.Normal.X) * 50
					
				else	
					
					velocity = Vector3.new(-rayR.Normal.Z, -3/50, rayR.Normal.X) * 50
					
				end

			end
			
			wallrunForce.MaxForce = 3000
			wallrunForce.VectorVelocity = velocity 
			
		end	
				
		repeat
			
			local loopRayL = workspace:Raycast(hrp.Position, hrp.CFrame.RightVector * -10, raycastParams)
			local loopRayR = workspace:Raycast(hrp.Position, hrp.CFrame.RightVector * 10, raycastParams)
			wait()
			
		until isGrounded or not uis:IsKeyDown(Enum.KeyCode.LeftShift) or not loopRayR and not loopRayL

		wallrunForce:Destroy()
		
	end
	
end)
2 Likes