Wallrun System not working?

Hi guys, I recently tried to make my first wallrun system on Roblox by wathcing some tutorials and then tried to make my own, the problem is that it’s not working properly and I don’t even know why, I spent at least an hour debugging the code to detect errors but I couldn’t find how to fix it.
https://i.gyazo.com/e73457d2a1eec4313deaf7e9d62e9202.mp4

Here’s the code:
-Local-

local event = game.ReplicatedStorage.Wallrun

local char = script.Parent
local rootPart : Part = char.HumanoidRootPart

local MAX_RADIUS = 3

local _walls = {}
for _, part in workspace:GetDescendants() do
	if part:GetAttribute("Wallrun") then
		table.insert(_walls, part)
	end
end

local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Include
params.FilterDescendantsInstances = _walls

game:GetService("RunService").RenderStepped:Connect(function()
	local raycast = workspace:Raycast(rootPart.Position, rootPart.CFrame.RightVector * MAX_RADIUS, params)
	if raycast and raycast.Instance then
		event:FireServer(true)
	else
		event:FireServer(false)
	end
end)

-Server-

local event = game.ReplicatedStorage.Wallrun

local WallrunNormals = {}
local WallrunDirections = {}

local RAY_PARAMS = RaycastParams.new()
local MAX_RADIUS = 3
local WALLRUN_SPEED = 15
local WALLRUN_GRAVITY = 0

local _walls = {}
RAY_PARAMS.FilterType = Enum.RaycastFilterType.Include
for _, _wall in game:GetDescendants() do
	if _wall:GetAttribute("Wallrun") then
		table.insert(_walls, _wall)
	end
end
RAY_PARAMS.FilterDescendantsInstances = _walls

local function InitPlayer(plr : Player)
	plr:SetAttribute("Wallrunning", false)
	
	local char = plr.Character or plr.CharacterAdded:Wait()
	local rootPart = char.HumanoidRootPart
	local LinearVelocity = Instance.new("LinearVelocity", rootPart)
	LinearVelocity.MaxForce = math.huge
	LinearVelocity.Enabled = false
	LinearVelocity.Name = "WallrunVelocity"
	
	local Attachment = Instance.new("Attachment", rootPart)
	LinearVelocity.Attachment0 = Attachment
end

local function Wallrun(plr : Player, active : boolean)
	local char = plr.Character or plr.CharacterAdded:Wait()
	local rootPart : Part = char.HumanoidRootPart
	local hum : Humanoid = char.Humanoid
	local LinearVelocity : LinearVelocity = rootPart:WaitForChild("WallrunVelocity")
	
	if active and plr:GetAttribute("Wallrunning") == false then
		local RightRaycast = workspace:Raycast(rootPart.Position, rootPart.CFrame.RightVector * MAX_RADIUS, RAY_PARAMS)
		if RightRaycast then
			plr:SetAttribute("Wallrunning", true)
			
			WallrunDirections[plr] = 1
			WallrunNormals[plr] = Vector3.new(RightRaycast.Normal.Z, 0, -RightRaycast.Normal.X)
			
			LinearVelocity.Enabled = true
			local Velocity = WallrunNormals[plr] * WALLRUN_SPEED * -WallrunDirections[plr]
			LinearVelocity.VectorVelocity = Vector3.new(Velocity.X, WALLRUN_GRAVITY, Velocity.Z)
		end
	elseif not active then
		plr:SetAttribute("Wallrunning", false)
		LinearVelocity.Enabled = false
		WallrunNormals[plr] = Vector3.zero
		WallrunDirections[plr] = 0
	end
end

game.Players.PlayerAdded:Connect(InitPlayer)
event.OnServerEvent:Connect(Wallrun)

It barely works and I legit don’t know why,
Thanks in advance!

4 Likes
local RightRaycast = workspace:Raycast(rootPart.Position, rootPart.CFrame.RightVector * MAX_RADIUS, RAY_PARAMS)
if not RightRaycast then RightRaycast = workspace:Raycast(rootPart.Position, -rootPart.CFrame.RightVector * MAX_RADIUS, RAY_PARAMS) end
 -- check left side as well maybe?
if not RightRaycast then return end
plr:SetAttribute("Wallrunning", true)

WallrunDirections[plr] = 1
WallrunNormals[plr] = Vector3.new(RightRaycast.Normal.Z, 0, -RightRaycast.Normal.X)

LinearVelocity.Enabled = true
local Velocity = WallrunNormals[plr] * WALLRUN_SPEED * -WallrunDirections[plr]
LinearVelocity.VectorVelocity = Vector3.new(Velocity.X, WALLRUN_GRAVITY, Velocity.Z)

maybe try this thing on your server script?
my idea is that maybe you’re not checking left direction as well so it barely works
in the wallrun function, the line after if active and plr:GetAttribute("Wallrunning") == false then

as for the actual problem (it just kinda doesn’t work) yea i have no idea what’s going on either

4 Likes

Thanks, this made it a bit better but it’s still acting like in the video I posted

2 Likes

The thing is, why is the raycast not detecting anything sometimes, am I doing something wrong? I legit don’t understand…

1 Like

Alright, alright, let’s investigate.

  1. The raycast might not be hitting the walls consistently. This could be due to the direction of the raycast or the position from where it’s being cast.

  2. The walls might not be properly tagged with the “Wallrun” attribute, causing the raycast to miss them.

  3. The wallrun system might not be properly handling the transition from normal movement to wallrunning.

In the client script, you’re casting the raycast in the right direction of the root part. This might not always hit the walls, especially if the player is not perfectly aligned with them. A potential fix would be to cast multiple raycasts in different directions and check if any of them hit a wall.

In the server script, you’re only enabling the wallrun if the player is not already wallrunning. This might cause issues if the player starts wallrunning, then briefly loses contact with the wall, and then hits it again. A potential fix would be to always enable the wallrun if the raycast hits a wall, regardless of whether the player is already wallrunning or not.

This may potentially fix it:

-- Client
game:GetService("RunService").RenderStepped:Connect(function()
    local hitWall = false
    for i = -1, 1, 0.5 do
        local direction = rootPart.CFrame:ToWorldSpace(Vector3.new(i, 0, 0)).Unit
        local raycast = workspace:Raycast(rootPart.Position, direction * MAX_RADIUS, params)
        if raycast and raycast.Instance then
            hitWall = true
            break
        end
    end
    event:FireServer(hitWall)
end)
-- Server
local function Wallrun(plr : Player, active : boolean)
    -- ...
    if active then
        local RightRaycast = workspace:Raycast(rootPart.Position, rootPart.CFrame.RightVector * MAX_RADIUS, RAY_PARAMS)
        if RightRaycast then
            plr:SetAttribute("Wallrunning", true)
            
            WallrunDirections[plr] = 1
            WallrunNormals[plr] = Vector3.new(RightRaycast.Normal.Z, 0, -RightRaycast.Normal.X)
            
            LinearVelocity.Enabled = true
            local Velocity = WallrunNormals[plr] * WALLRUN_SPEED * -WallrunDirections[plr]
            LinearVelocity.VectorVelocity = Vector3.new(Velocity.X, WALLRUN_GRAVITY, Velocity.Z)
        end
    elseif not active then
        plr:SetAttribute("Wallrunning", false)
        LinearVelocity.Enabled = false
        WallrunNormals[plr] = Vector3.zero
        WallrunDirections[plr] = 0
    end
end

From what I could imagine is that this shall make the wallrun system more robust and handle edge cases better. However, I cannot guarantee anything.

2 Likes

Thanks, but unfortunately the line where you defined the direction in the local script is giving: “CFrame expected, got Vector3”.
But I personally don’t think it’s a client issue, since everytime I tested the raycasting on the client it would always work perfectly, I might be wrong though…

1 Like
local direction = (rootPart.CFrame * Vector3.new(i, 0, 0)).Unit

maybe try this thing?

2 Likes

Yeah now it just doesn’t do anything ahahaha

1 Like

Ok, so I’ve tried changing the system a bit and switching from the RunService on the client to “Jump to wallrun”, and that seems to work better, but still the raycast on the server often doesn’t detect anything.
-LocalScript-

local UIS = game:GetService("UserInputService")
local event = game.ReplicatedStorage.Remotes.Wallrun

local plr = game.Players.LocalPlayer

local function Input()
	if plr:GetAttribute("Wallrunning") == false then
		event:FireServer(true)
	else
		event:FireServer(false)
	end
end

UIS.JumpRequest:Connect(Input)

-Server-

local event = game.ReplicatedStorage.Remotes.Wallrun

local _walls = {}
for _, part in workspace:GetDescendants() do
	if part:GetAttribute("Wallrun") == true then
		table.insert(_walls, part)
	end
end

local WallrunningNormals = {}
local WallrunningDirections = {}

local MAX_RADIUS = 4
local RAY_PARAMS = RaycastParams.new()
local WALLRUNNING_SPEED = 40
local WALLRUNNING_GRAVITY = 0

RAY_PARAMS.FilterType = Enum.RaycastFilterType.Include
RAY_PARAMS.FilterDescendantsInstances = _walls

local function InitPlayer(plr : Player)
	local char = plr.Character or plr.CharacterAdded:Wait()
	local rootPart = char.HumanoidRootPart
	local LinearVelocity = Instance.new("LinearVelocity", rootPart)
	local Attachment = Instance.new("Attachment", rootPart)
	
	LinearVelocity.Name = "WallrunVelocity"
	LinearVelocity.MaxForce = math.huge
	LinearVelocity.Enabled = false
	LinearVelocity.Attachment0 = Attachment
	LinearVelocity.VectorVelocity = Vector3.zero
	
	plr:SetAttribute("Wallrunning", false)
end

local function Wallrun(plr : Player, active : boolean)
	local char = plr.Character
	local hum : Humanoid = char.Humanoid
	local rootPart : Part = char.HumanoidRootPart
	local LinearVelocity : LinearVelocity = rootPart:FindFirstChild("WallrunVelocity")
	
	if active and hum:GetState() == Enum.HumanoidStateType.Freefall then
		local RightRaycast = workspace:Raycast(rootPart.Position, rootPart.CFrame.RightVector * MAX_RADIUS, RAY_PARAMS)
		local LeftRaycast = workspace:Raycast(rootPart.Position, -rootPart.CFrame.RightVector * MAX_RADIUS, RAY_PARAMS)
		if not LeftRaycast and not RightRaycast then print("a") return end
		
		plr:SetAttribute("Wallrunning", true)
		if RightRaycast then
			WallrunningDirections[plr] = 1
			WallrunningNormals[plr] = Vector3.new(RightRaycast.Normal.Z, 0, -RightRaycast.Normal.X)
		elseif LeftRaycast then
			WallrunningDirections[plr] = -1
			WallrunningNormals[plr] = Vector3.new(LeftRaycast.Normal.Z, 0, -LeftRaycast.Normal.X)
		end
		local Velocity = WallrunningNormals[plr] * WALLRUNNING_SPEED * -WallrunningDirections[plr]
		LinearVelocity.VectorVelocity = Vector3.new(Velocity.X, -WALLRUNNING_GRAVITY, Velocity.Z)
		LinearVelocity.Enabled = true
	end
end

game.Players.PlayerAdded:Connect(InitPlayer)
event.OnServerEvent:Connect(Wallrun)

I still don’t understand what’s wrong :C

WAIT, isn’t the problem Raycasting on the server? I just tried copy pasting everything from the server script to the local script and it works so smoothly, probably the server isn’t good at raycasting?

Okay I found a solution, basically Raycasting on the client and then setting the Velocity and everything on the server. For some reason (idk why) raycasting on the server isn’t the best.
Thank you everyone for your support!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.