How To Make Wall-Climb/Mantling System

Lets not waste time and get straight into it!
How my wall climb/mantling works is by using Raycasts

I have a function that sets up attachments inside the humanoidrootpart
something like this

now everytime the player tries to jump we will shoot ray from each attachment to the lookvector
giving us a result like this

We can check how many rays detected a part and then decide if we should perform a mantle or wall climb (something like this

now for actually performing the action
many people use gyro and stuff
while some is accurate many arent

we wont be using gyro
to perform the action
we will check how many rays detected a part from bottom to top
then we just simple teleport the player on thr Y axis based on that information

then we just simply teleport it 5 studs ahead

Giving us a result like this

This is the final script:

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local hrp: Part = character:WaitForChild("HumanoidRootPart") 
local humanoid: Humanoid = character:WaitForChild("Humanoid")

-- Configure Attachments
local attachments = {}  
local rayDistance = 5   
local rayOriginOffset = 1  
local rayVisualDuration = .5

local function createAttachments()
	for i = -4, 5 do  
		local attachment = Instance.new("Attachment")
		attachment.Position = Vector3.new(0, i * rayOriginOffset, 0)
		attachment.Visible = true
		attachment.Parent = hrp
		table.insert(attachments, attachment)
	end
end

local function createRayVisual(startPos, endPos)
	local rayPart = Instance.new("Part")
	rayPart.Anchored = true
	rayPart.CanCollide = false
	rayPart.Material = Enum.Material.Neon
	rayPart.Color = Color3.new(1, 0, 0)
	rayPart.Size = Vector3.new(0.1, 0.1, (endPos - startPos).Magnitude)

	rayPart.CFrame = CFrame.new(startPos, endPos) * CFrame.new(0, 0, -(endPos - startPos).Magnitude / 2)

	rayPart.Parent = workspace
	game.Debris:AddItem(rayPart, rayVisualDuration) 
end

local function castRays()
	local hitCount = 0
	local results = {} 

	for _, attachment in pairs(attachments) do
		local rayOrigin = attachment.WorldPosition
		local rayDirection = hrp.CFrame.LookVector * rayDistance

		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {character} 
		raycastParams.FilterType = Enum.RaycastFilterType.Exclude

		local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
		local rayEndPos = raycastResult and raycastResult.Position or (rayOrigin + rayDirection)
		createRayVisual(rayOrigin, rayEndPos) 
		
		if raycastResult then
			hitCount = hitCount + 1
			table.insert(results, true) 
		else
			table.insert(results, false) 
		end
	end

	return hitCount, results
end

local function checkActionType(hitCount, results)
	if hitCount <= 5 and hitCount > 0 then
		return "Mantle"
	elseif hitCount > 5 and hitCount <= 9 then
		return "WallClimb"
	else
		return nil
	end
end

local function executeAction(hitCount, actionType)
	local climbHeight = hitCount * rayOriginOffset 
	local forwardMovement = hrp.CFrame.LookVector * 5
	
	if actionType == "Mantle" then
		print("Performing Mantle!")

		hrp.Anchored = true
		
		hrp.CFrame = hrp.CFrame + Vector3.new(0, climbHeight, 0)
		hrp.CFrame = hrp.CFrame + forwardMovement
		
		hrp.Anchored = false
	elseif actionType == "WallClimb" then
		print("Performing Wall Climb!")

		hrp.Anchored = true
		
		hrp.CFrame = hrp.CFrame + Vector3.new(0, climbHeight, 0)
		hrp.CFrame = hrp.CFrame + forwardMovement

		hrp.Anchored = false
	end
end

local function onJumpRequest()
	local hitCount, rayResults = castRays()
	local actionType = checkActionType(hitCount, rayResults)

	if actionType then
		executeAction(hitCount, actionType)
	else
		humanoid.JumpPower = 50
		humanoid.Jump = true
		wait(1)
		humanoid.JumpPower = 0
	end
end

game.UserInputService.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.Space then  -- On spacebar press
		onJumpRequest()
	end
end)

humanoid.JumpPower = 0
createAttachments()

Ofc this have its flaws cuz I made it in 10 mins or smt
like it will act weirdly with parts like this

I highly doubt it will bug with meshes
you can use this as a base for ur mantling/wallclimb stuff

one more thing,
I am using a single part for everything so idk how it will work with full character bodies

Goodluck.

10 Likes

I feel like this could be made more performant.

How I would do it is: (Mantling only right now)
Cast a ray 2 studs from the front of the player and 5 studs above, downwards till 6 studs. Then cast another ray upwards 7 studs, another ray 5 studs above the player (a sphere cast at radius 3) going in the direction the player is looking.

It takes the following if statement:

if ray1 and not ray2 and not ray3 then -- mantle
3 Likes

you can do it if you want to
I just gave u a base to work on
its ur task to work on that base and make it better

2 Likes