How would I make a wall climbing system where player's can rotate around a wall

  1. What do you want to achieve? Keep it simple and clear!

I’m trying to make a wall climbing system.

  1. What is the issue? Include screenshots / videos if possible!

I don’t know any methods to how I would make a player’s character rotate around the part or change surfaces.

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I’ve tried looking at different topics but I’m still not sure.

3 Likes

I would need to know how you want your climb to look. My recommendation would be to use raycasts to check for the walls, and use align orientation and align position to move the character based on the raycast result

2 Likes

I’ve already made my wall climbing system but I don’t know how to make it rotate around walls.

Can you show us the script used?

I’m not looking for solutions but I’m looking for methods.

Client
local UIS = game:GetService("UserInputService")
local CS = game:GetService("CollectionService")
local RS = game:GetService("ReplicatedStorage")
local runservice = game:GetService("RunService")
local players = game:GetService("Players")
local debounce = false
local changedwall = false

--Player stats
local plr = players.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local HRP = char.PrimaryPart
local hum = char:WaitForChild("Humanoid")

local OnWall = plr:WaitForChild("OnWall")

local keyboardtable = {
	[Enum.KeyCode.A] = Enum.KeyCode.A,
	[Enum.KeyCode.D] = Enum.KeyCode.D
}

local anglestable = {
	[Enum.KeyCode.A] = CFrame.Angles(0,math.rad(-90),0),
	[Enum.KeyCode.D] = CFrame.Angles(0,math.rad(90),0)
}

for i, v in pairs(CS:GetTagged("Wall")) do
	v.Touched:Connect(function()
		if not debounce and OnWall.Value == false then
			local params = RaycastParams.new()
			params.FilterDescendantsInstances = {char}
			params.FilterType = Enum.RaycastFilterType.Exclude
			local origin = HRP.Position
			local direction = HRP.CFrame.LookVector * 3
			local raycast = workspace:Raycast(origin,direction, params)
			if raycast then
				local touched = RS:WaitForChild("Climb"):InvokeServer("Touch", OnWall, HRP)
				debounce = true
			end
		end
	end)
end

local function movebutton(input)
	if input.KeyCode == Enum.KeyCode.W or Enum.KeyCode.A or Enum.KeyCode.S or Enum.KeyCode.D then
		if OnWall.Value == true then
			local controlstable = {
				[Enum.KeyCode.W] = HRP.CFrame.UpVector * 16,
				[Enum.KeyCode.A] = HRP.CFrame.RightVector * -16,
				[Enum.KeyCode.S] = HRP.CFrame.UpVector * -16,
				[Enum.KeyCode.D] = HRP.CFrame.RightVector * 16,
			}
			local name = input.KeyCode.Name
			local control = controlstable[input.KeyCode]
			RS:WaitForChild("Climb"):InvokeServer("Move",control,name,OnWall)
		end
	end
end
local function stop(input)
	if input.KeyCode == Enum.KeyCode.W or Enum.KeyCode.A or Enum.KeyCode.S or Enum.KeyCode.D then
		if OnWall.Value == true then
			local name = input.KeyCode.Name
			RS:WaitForChild("Climb"):InvokeServer("Stop",name,OnWall)
		end
	end
end

local function boostjump(input)
	if input.KeyCode == Enum.KeyCode.Space then
		RS:WaitForChild("Climb"):InvokeServer("Jump",OnWall)
		debounce = false
	end
end

local function check()
	runservice.RenderStepped:Connect(function()
		if OnWall.Value == true then
			local origin = HRP.Position
			local direction = HRP.CFrame.LookVector * 5
			local params = RaycastParams.new()
			params.FilterDescendantsInstances = {char}
			params.FilterType = Enum.RaycastFilterType.Exclude
			local raycast = workspace:Raycast(origin,direction,params)
			if raycast == nil then
				for i,v in pairs(keyboardtable) do
					if UIS:IsKeyDown(v) and changedwall == false then
						local angle = anglestable[v]
						local lvtable = {
							[Enum.KeyCode.A] = HRP.CFrame.RightVector * -16,
							[Enum.KeyCode.D] = HRP.CFrame.RightVector * 16,
						}
						local chosenlv = lvtable[v]
						changedwall = true
						local swapside = RS:WaitForChild("Climb"):InvokeServer("Change",angle,chosenlv)
						if swapside == true then
							local core = coroutine.wrap(function()
								task.wait(1)
								changedwall = false
							end)
							core()
						end
					elseif UIS:IsKeyDown(Enum.KeyCode.W) or UIS:IsKeyDown(Enum.KeyCode.S) then
						RS:WaitForChild("Climb"):InvokeServer("End",OnWall)
					end
				end
			end
		end
	end)
end
task.spawn(check)
UIS.InputBegan:Connect(boostjump)
UIS.InputBegan:Connect(movebutton)
UIS.InputEnded:Connect(stop)
Server
local RS = game:GetService("ReplicatedStorage")
local runservice = game:GetService("RunService")

--Modules
local wallclimb = require(RS.WallClimb)

local function playeradded(plr)
	local bool = Instance.new("BoolValue")
	bool.Parent = plr
	bool.Name = "OnWall"
	bool.Value = false
end

RS:WaitForChild("Climb").OnServerInvoke = function(plr, identifier, identifier2, identifier3,identifier4)
	local char = plr.Character or plr.CharacterAdded:Wait()
	local HRP = char.PrimaryPart
	local hum = char:WaitForChild("Humanoid")
	if identifier == "Touch" and identifier2.Value == false then
		hum.AutoRotate = false
		identifier2.Value = true
		task.spawn(wallclimb.anchor,char,true)
	elseif identifier == "Move" then
		task.spawn(wallclimb.anchor,char,false)
		wallclimb.move(char,HRP,identifier2,identifier3)
	elseif identifier == "Stop" then
		task.spawn(wallclimb.anchor,char,true)
		wallclimb.stop(char, identifier2)
	elseif identifier == "Jump" then
		task.spawn(wallclimb.jump,HRP,char,identifier2)
		task.spawn(wallclimb.anchor,char,false)
		identifier2.Value = false
		hum.AutoRotate = true
	elseif identifier == "Change" then
		wallclimb.stop2(char)
		task.spawn(wallclimb.rotate,HRP,identifier2)
		task.spawn(wallclimb.anchor,char,false)
		wallclimb.move2(char,HRP,identifier3)
		return true
	elseif identifier == "End" then
		identifier2.Value = false
		wallclimb.completestop(char,hum)
	end
end

game:GetService("Players").PlayerAdded:Connect(playeradded)
Module
local runservice = game:GetService("RunService")

local module = {
	move = function(char, HRP, control,name)
		if control ~= nil then
			local LV = Instance.new("LinearVelocity")

			LV.Parent = char

			LV.Attachment0 = HRP.RootAttachment
			LV.VectorVelocity = control
			LV.MaxForce = 1e6
			LV.Name = name
		end
	end,
	move2 = function(char, HRP, control)
		if control ~= nil then
			local LV = Instance.new("LinearVelocity")

			LV.Parent = char

			LV.Attachment0 = HRP.RootAttachment
			LV.VectorVelocity = control
			LV.MaxForce = 1e6
		end
	end,
	stop = function(char,name)
		for i,v in pairs(char:GetChildren()) do
			if v:IsA("LinearVelocity") and v.Name == name and v.Name == "LinearVelocity" then
				v:Destroy()
			end
		end
	end,
	stop2 = function(char)
		for i,v in pairs(char:GetChildren()) do
			if v:IsA("LinearVelocity") then
				v:Destroy()
			end
		end
	end,
	completestop = function(char,hum)
		for i,v in pairs(char:GetChildren()) do
			if v:IsA("LinearVelocity") then
				v:Destroy()
			end
		end
		hum.AutoRotate = true
	end,
	anchor = function(char,bool)
		for i,v in pairs(char:GetDescendants()) do
			if v:IsA("BasePart") then
				v.Anchored = bool
			end
		end
	end,
	jump = function(HRP,char,OnWall)
		if OnWall.Value == true then
			HRP.Position = HRP.Position + HRP.CFrame.LookVector * -4
			for i,v in pairs(char:GetChildren()) do
				if v:IsA("LinearVelocity") then
					v:Destroy()
				end
			end
		end
	end,
	rotate = function(HRP,angle)
		print("Rotated")
		HRP.CFrame = HRP.CFrame * angle
	end,
}

return module