Wall Climbing System Help

Hello I’ve started yesterday working on a wall climb system Its almost done but the only problem i’m trying to make the player climb around a wall

this is what i want to achieve: https://gyazo.com/ecaff3fb9efaccaf4b6300c4bad51554
this is what i have currently: https://gyazo.com/70b810dae528efdc2b96d2f580f66720

this is the code I made:

--// Services
local UIS = game:GetService('UserInputService')

--// Variables
local plr = game.Players.LocalPlayer
local Char = plr.Character or plr.CharacterAdded:Wait()
local Root = Char:WaitForChild('HumanoidRootPart')
local Head = Char:WaitForChild('Head')

--// Settings
local HittingWall = false
local OnWall = false
local HoldingW = false

local Normal
local Pos
local Wall
local previousWall

--// Tables/Dictionnaries
local Movement = {
	MovingUp = false,
	MovingDown = false,
	MovingRight = false,
	MovingLeft = false
}

--// Animations
local Idle = Char:WaitForChild('Humanoid'):LoadAnimation(script:WaitForChild('Idle'))
local MU = Char:WaitForChild('Humanoid'):LoadAnimation(script:WaitForChild('MU'))
local MD = Char:WaitForChild('Humanoid'):LoadAnimation(script:WaitForChild('MD'))
local ML = Char:WaitForChild('Humanoid'):LoadAnimation(script:WaitForChild('ML'))
local MR
local JU = Char:WaitForChild('Humanoid'):LoadAnimation(script:WaitForChild('JU'))

-- Ray casting and Stop RayCasting if you 
spawn(function()
	while true do
		local r = Ray.new(Head.CFrame.p, Head.CFrame.LookVector * 5)
		local hit,pos,normal = workspace:FindPartOnRay(r,Char)

		local r2 = Ray.new(Root.CFrame.p, Root.CFrame.RightVector * 5)
			
		if hit then
			if hit:FindFirstChild('Climable') then
				print(hit.Name)
				HittingWall = true
				
				Pos = pos
				Normal = normal
				Wall = hit
				previousWall = hit
			end
		end

		if not hit then

			if Root:FindFirstChild('bv') and OnWall then
				Idle:Stop()
				JU:Play()
				MU:Stop()
				MD:Stop()
				ML:Stop()
						
			
				local bv = Root:FindFirstChild('bv')
				bv.Velocity = Vector3.new(0,0,0)
				bv.Velocity = Root.CFrame.LookVector * 10 + Vector3.new(0, 40, 0)
				
				game.Debris:AddItem(bv,.15)
				OnWall = false
				Char.Humanoid.AutoRotate = true
				Char.Humanoid.PlatformStand = false
								 
		elseif Root:FindFirstChild('bv') == nil and OnWall then
				Idle:Stop()
				JU:Play()
				MU:Stop()
				MD:Stop()
				ML:Stop()
	
				local bv = Instance.new('BodyVelocity',Root)
				bv.MaxForce = Vector3
				bv.Velocity = Root.CFrame.LookVector * 10 + Vector3.new(0, 40, 0)
				
				game.Debris:AddItem(bv,.15)
				OnWall = false
				Char.Humanoid.AutoRotate = true
				Char.Humanoid.PlatformStand = false
			end 
			--HittingWall = false
			Pos = nil
			Normal = nil
		end
		wait()
	end
end)

-- Movement and Start/Stop Climbing
UIS.InputBegan:Connect(function(Key,Chatting)
	if Key.KeyCode == Enum.KeyCode.X and not Chatting then
		if HittingWall == true then
			if not OnWall then
				if Normal == nil then return end
				OnWall = true
				Idle:Play()

				
				local bv = Instance.new('BodyVelocity',Root)
				bv.MaxForce = Vector3.new(1,1,1) * math.huge
				bv.Velocity = Root.CFrame.LookVector * 0
				bv.Name = 'bv'
				
				Char.Sit = true
				Char.Humanoid.AutoRotate = false
				Char.Humanoid.PlatformStand = true


			else
				Idle:Stop()
				Root:FindFirstChild('bv'):Destroy()
				Char.Humanoid.AutoRotate = true
				Char.Humanoid.PlatformStand = false
				OnWall = false
			end
		end
	elseif Key.KeyCode == Enum.KeyCode.W and not Chatting then
		if HittingWall == true then
			if OnWall then
				Idle:Stop()
				MU:Play()
				
				local bv = Root:FindFirstChild('bv')
				bv.Velocity = Root.CFrame.LookVector * 0 + Vector3.new(bv.Velocity.X, 12.5, bv.Velocity.Z)
				

			end
		end
	elseif Key.KeyCode == Enum.KeyCode.S and not Chatting then
		if HittingWall == true then
			if OnWall then
				Idle:Stop()
				MD:Play()

				local bv = Root:FindFirstChild('bv')
				bv.Velocity = Root.CFrame.LookVector * 0 + Vector3.new(bv.Velocity.X, -12.5, bv.Velocity.Z)
				

			end
		end
	elseif Key.KeyCode == Enum.KeyCode.A and not Chatting then
		if HittingWall == true then
			if OnWall then
				Idle:Stop()
				ML:Play()
				
				local bv = Root:FindFirstChild('bv')
				bv.Velocity = Root.CFrame.LookVector * 0 + Vector3.new(bv.Velocity.X, bv.Velocity.Y, -12.5)
				

			end
		end
	elseif Key.KeyCode == Enum.KeyCode.D and not Chatting then
		if HittingWall == true then
			if OnWall then
				Idle:Stop()
				--MU:Play()
				
				local bv = Root:FindFirstChild('bv')
				bv.Velocity = Root.CFrame.LookVector * 0 + Vector3.new(bv.Velocity.X, bv.Velocity.Y, 12.5)
				

			end
		end
	end
end)

UIS.InputEnded:Connect(function(Key,Chatting)
	if Key.KeyCode == Enum.KeyCode.W and OnWall then
		Idle:Play()
		MU:Stop()
		
		local bv = Root:FindFirstChild('bv')
		bv.Velocity = Root.CFrame.LookVector * 0 + Vector3.new(bv.Velocity.X, bv.Velocity.Y - 12.5, bv.Velocity.Z)
	
elseif Key.KeyCode == Enum.KeyCode.S and OnWall then
		Idle:Play()
		MD:Stop()
			
		local bv = Root:FindFirstChild('bv')
		bv.Velocity = Root.CFrame.LookVector * 0 + Vector3.new(bv.Velocity.X, bv.Velocity.Y + 12.5, bv.Velocity.Z)

elseif Key.KeyCode == Enum.KeyCode.D and OnWall then
		Idle:Play()
		

		local bv = Root:FindFirstChild('bv')
		bv.Velocity = Root.CFrame.LookVector * 0 + Vector3.new(bv.Velocity.X, bv.Velocity.Y, bv.Velocity.Z - 12.5)

elseif Key.KeyCode == Enum.KeyCode.A and OnWall then
		Idle:Play()
		ML:Stop()
		
		local bv = Root:FindFirstChild('bv')
		bv.Velocity = Root.CFrame.LookVector * 0 + Vector3.new(bv.Velocity.X, bv.Velocity.Y, bv.Velocity.Z + 12.5)

	end
end)

there is also another probleme when i use the body velocity it doesnt work good on other side

5 Likes

You need to get the bounds of the surface the player touched, e.g. if it touched the Front surface get the max and min x/y coordinates → if they get to the edge of the x coordinates (in this case, let’s say towards the right surface), switch the position to right surface → get the bounds of that surface (now the z/y) and repeat. Obviously, as you do this, ensure to rotate them to the opposite surface normal e.g. if they’re climbing the front surface, rotate them so they’re orientated towards the back surface

Do the same for the Y axis, if they get to the top, tween them to the top surface and release the BV/Weld or whatever method you use. If they get to the bottom, break it so they can walk again.

waw thnx alot but i tried doing

Root.CFrame = CFrame.new(Vector3.new() -Normal)

but it didnt work do you know why??

i don’t know what you mean by ‘-Normal’ but try this

Root.CFrame = CFrame.new(Vector3.new(), -Normal)

and also the vector3 is empty, you should put something in there

thats what i just typed i’m confused lol also -Normal mean - the surface normal

also this didnt work unfortunetly it just positions me weirdly

Not the most efficient method, but something I’ve used in the past:

local toOppositeFace = (function (Part, Pos)
	local Surface   = 'Front'
	local Intersect = Part.CFrame:pointToObjectSpace(Pos)
	local Size      = Part.Size / 2
	
 	if math.abs(Intersect.x - Size.x) < 0.01 then
		Surface = 'Right'
	elseif math.abs(Intersect.x + Size.x) < 0.01 then
		Surface = 'Left'
	elseif math.abs(Intersect.y - Size.y) < 0.01 then
		Surface = 'Top'
	elseif math.abs(Intersect.y + Size.y) < 0.01 then
		Surface = 'Bottom'
	elseif math.abs(Intersect.z - Size.z) < 0.01 then
		Surface = 'Front'
	elseif math.abs(Intersect.z + Size.z) < 0.01 then
		Surface = 'Back'
	end
	
	local Faces = {
		Front  = CFrame.Angles(math.pi, 0, 0);
		Back   = CFrame.Angles(-math.pi, 0, 0);
		Left   = CFrame.Angles(0, 0, math.pi);
		Right  = CFrame.Angles(0, 0, -math.pi);
		Top    = CFrame.Angles(0, 0, 0);
		Bottom = CFrame.Angles(0, 0, 0);
	}
	
	return (CFrame.new(Pos) * (Part.CFrame - Part.CFrame.p)) * Faces[Surface]
end)


-- init
local A, B = script.Parent, game.Workspace.B
game:GetService('RunService').Heartbeat:connect(function ()
	A.CFrame = toOppositeFace(
		B,
		(B.CFrame * CFrame.new(0, 0, -2)).p
	)
end)

However, I’d recommend you look into Vector3.fromNormalId - you could get the normal surface of the part’s faces by multiplying it with its CFrame and removing the position of the obj to get the face normal.

Similarly, if you don’t want to do the math, just raycast from the character to the part, get the surface (invert it as described e.g. front->back, back->front, left->right and so forth), and modify the character angle as I do in the above code.

Also this is repurposed code, you don’t need the sanity checks for x/y/z values - it’s just because I was using that to check for intersections. Again, hopefully this will just give you an idea of what I mean so you can look into the method I suggested above.

why did you remove?? we can debate my friend

Did you fix your problem yet dude?

nah imma work on it tmr i gtg soon

Well i’ll update you if I finish mine so I can help you with yours

aight cool thnx alot meh really appreciate the help

1 Like

No problem man of course! :joy:

yo do you know how to make the player going around the wall

Thats exactly what Im trying to figure out, well I got it but it’s buggy as crap

basically you just need to raycast for both when the player is pressing d and a since thats where we want them to go and just rotate the player to face the wall and then raycast to that wall and position them accordingly, it’s really as simple as that really

1 Like

how did you do that, how did you detect the person being at the side of the wall

first of i raycast to see if there is nothing there and if there isn’t i rotate them and then i cast another ray to position them to the wall

yo can i ask you something how did type to position the player to the wall cuz i did

Root.CFrame = CFrame.new(Pos + Normal * 2)

when i tried this it brings me to the opposite site of the wall like if i use in the front it will tp me to back of the wall

this sould work :stuck_out_tongue:

Root.CFrame = CFrame.new(Intersection,Intersection-Normal)

3 Likes

yh i found that way it works thnx