How can I lock character in front of a wall?

I’m attempting to make a wall-climbing system. I don’t have any idea on how to make the character be locked in front of a wall while still being able to move up, down, right and left. (I’m gonna worry about movements later.)

humanoid.Jumping:Connect(function(isActive)
	if not isActive then return end
	
	local raycastResult = workspace:Raycast(rootPart.Position, rootPart.CFrame.LookVector * 3)
	
	if raycastResult and not onWall then
		onWall = true
		print(onWall)
		
		character:SetPrimaryPartCFrame(CFrame.lookAt(raycastResult.Position + raycastResult.Normal, raycastResult.Position))
	else
		onWall = false
		print(onWall)
	end
end)

I’ve tried doing this a while ago and found this post Making a "Wall Climbing" System - #12 by Stealthied which was really helpful if you haven’t came across this already you should check it out it could help you out.

1 Like

That’s what I’ve actually found before posting. I still don’t understand it. :frowning:

Yeah I didn’t understand it at first, but once you start messing with the code removing and changing things you figure out what does what or at least what I did. Anyways here’s the code I ended up with feel free to use it it’s not great, but yeah lol it works.

--| Services
local UIS = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

--| Variables
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local HRP = Character:WaitForChild("HumanoidRootPart")
local Humanoid = Character:WaitForChild("Humanoid")
local IsClimbing = false
local Connections = {}

local function IsKeyDown(Key)
	return UIS:IsKeyDown(Enum.KeyCode[Key]) 
end

UIS.InputBegan:Connect(function(Input,IsTyping)
	if IsTyping then return end
	
	local Key = UIS:GetStringForKeyCode(Input.KeyCode)
	if Key == "F" then --You can replace this with any key
		--warn("WALL CLIMB")	
		local Face = nil
		local Actions = {0;0;0;0;}
		local Buttons = {"A";"D";"S";"W"}

		if not IsClimbing then
			--warn("START WALL CLIMB")
			local Params = RaycastParams.new()
			Params.CollisionGroup = ("Climbable")
			Params.FilterDescendantsInstances = {Character}
			Params.FilterType = Enum.RaycastFilterType.Blacklist
			Params.IgnoreWater = true

			--Climb Moving/Speed
			local BV = Instance.new("BodyVelocity")
			BV.Name = "WallClimbBV"
			BV.MaxForce = Vector3.new(1,1,1)*math.huge
			BV.P = math.huge
			BV.Velocity = Vector3.new()
			--BV.Name = ("Climb Speed")
			
			--Keeps the player position the same (so they don't rotate around and stuff)
			local BG = Instance.new("BodyGyro")
			BG.Name = "WallClimbBG"
			BG.CFrame = CFrame.new()
			BG.D = 100
			BG.MaxTorque = Vector3.new(1,1,1)*math.huge
			BG.P = 3000

			local Origin = HRP.Position
			local Direction = HRP.CFrame.LookVector

			local Result = workspace:Raycast(Origin, Direction, Params)
			if Result then
				if not Result.Instance.Parent:FindFirstChild("Humanoid") then
					Connections["Connection1"] = UIS.InputBegan:Connect(function(Input,IsTyping)
						if IsTyping then return end
						local Key = UIS:GetStringForKeyCode(Input.KeyCode)
						--warn(Key)
						local Test = table.find(Buttons, Key)

						if Test then
							Actions[Test]=1
						end
					end)

					Connections["Connection2"] = UIS.InputEnded:Connect(function(Input,IsTyping)
						if IsTyping then return end
						local Key = UIS:GetStringForKeyCode(Input.KeyCode)
						--warn(Key)
						local Test = table.find(Buttons, Key)

						if Test then
							Actions[Test]=0
						end
					end)

					Connections["Connection3"] = RunService.RenderStepped:Connect(function()
						--warn("TEST")
						local Strafe = Actions[2] - Actions[1]
						local Surge = Actions[3] - Actions[4]

						BV.Velocity=HRP.CFrame.RightVector*(Strafe*8)+Vector3.new(0, Surge*-8, 0)		
					end)

					IsClimbing = true
					HRP.CFrame = CFrame.new(HRP.CFrame.p, Vector3.new(HRP.Position.X - Result.Normal.X, HRP.Position.Y, HRP.Position.Z - Result.Normal.Z))
					Face = CFrame.new(Result.Position+Result.Normal, Result.Position)

					Humanoid.AutoRotate=false
					Humanoid.PlatformStand=true

					BV.Parent=HRP
					BG.Parent=HRP

					repeat
						RunService.RenderStepped:Wait()
						BG.CFrame=Face or CFrame.new()

						--When you reach the top of the part
						if HRP.Position.Y > Result.Instance.Size.Y+3 then
							IsClimbing = false
							_G[Player.Name]:StopAnimation("Climb")
							BG.Parent=nil
							BV.Parent=nil
							Face=nil
						end

						local sideOrigin = HRP.CFrame*CFrame.new(0, 0, -1).Position
						local sideDirection = HRP.CFrame.RightVector*(IsKeyDown("D") and -2 or 2)

						local Hit = workspace:Raycast(sideOrigin, sideDirection, Params)
						if Hit and (IsKeyDown("D") or IsKeyDown("A")) then
							Face=CFrame.new(Hit.Position+Hit.Normal, Hit.Position)
						end
					until (not IsClimbing)

					Humanoid.AutoRotate=true
					Humanoid.PlatformStand=false
				end					
			end
		else
			--warn("STOP WALL CLIMB")
			if IsClimbing then --just make sure they all climbing
				Connections["Connection1"] = nil
				Connections["Connection2"] = nil
				Connections["Connection3"] = nil
				IsClimbing = false
				_G[Player.Name]:StopAnimation("Climb")
				HRP:FindFirstChild("WallClimbBG").Parent = nil
				HRP:FindFirstChild("WallClimbBV").Parent = nil
				Face = nil						
			end		
		end		
	end
end)

To get this to work though you need to make some collision groups and if you don’t know how to do that you can do it by going to the model section at the top in studio and at the very end you’ll see a button called collision groups and it will open up this panel
C__Desktop_Other_Programming Stuff_Roblox Studio Files_MurderGame.rbxl - Roblox Studio 6_7_2021 8_51_28 AM (2)
all you gotta do is where it says “+ Add Group” type “Climbable” and then “Unclimable”. Hopefully this made sense!

1 Like