Making a Terrain Climbing System

  1. What do you want to achieve?

    I want make a climbing system that can work on terrain

  2. What is the issue?

    So the problem now is make the player rotate correctly on the terrain curves, that isnt working, its rotating much more than necessary:

  3. What solutions have you tried so far?

    I tryed look on others posts and on youtube, but i found nothing

--services
local userInputService = game:GetService("UserInputService")
local players = game:GetService("Players")
local runService = game:GetService("RunService")
local workspace = game:GetService("Workspace")

--vars
local player = players.LocalPlayer
local humanoid = nil
local humanoidRootPart = nil
local head = nil
local bodyPosition = Instance.new("BodyPosition")
local bodyGyro = Instance.new("BodyGyro")
local rayCastParams = RaycastParams.new()
local climbInput = Enum.KeyCode.C
local climbing = false
local rayCastResult = nil
local position = nil
rayCastParams.FilterType = Enum.RaycastFilterType.Blacklist
local connection = nil
local delayClimb = os.clock()
local moved = false
local climbingDirection = ""

local ClimbController = {}

function ClimbController.fireRay(direction)
	if direction == "front" then
		position = humanoidRootPart.CFrame
		return workspace:Raycast(position.Position,position.lookVector * 10,rayCastParams)
	elseif direction == "up" then
		position = head.CFrame
		return workspace:Raycast(position.Position,position.upVector * 5,rayCastParams)
	end
end

function ClimbController.climb()
	if not ClimbController.canClimb("front") then return end
	climbing = true
	humanoid.PlatformStand = true
	local rayPosition = rayCastResult.Position
	bodyPosition.Position = rayPosition
	bodyGyro.CFrame = CFrame.new(humanoidRootPart.Position,rayPosition)
	bodyPosition.Parent = humanoidRootPart
	bodyGyro.Parent = humanoidRootPart
	ClimbController.climbing()
end

function ClimbController.climbing()
	connection = runService.Heartbeat:Connect(function()
		print(humanoid.MoveDirection)
		if climbing then
			if os.clock() - delayClimb > 0.15 then
				if humanoid.MoveDirection.X and humanoid.MoveDirection.Z > 0 then
					bodyPosition.Position = bodyPosition.Position + Vector3.new(0,2,0)
					climbingDirection = "up"
				end
				if ClimbController.canClimb(climbingDirection) then
					local rayPosition = rayCastResult.Position
					bodyGyro.CFrame = CFrame.new(humanoidRootPart.Position,rayPosition)
				end
				delayClimb = os.clock()
			end
		end
	end)
end

function ClimbController.canClimb(direction)
	rayCastResult = ClimbController.fireRay(direction)
	if not rayCastResult or not rayCastResult.Instance:IsA("Terrain") then return false end
	return true
end

function ClimbController.start()
	local character = player.Character or player.CharacterAdded:Wait()
	humanoid = character:WaitForChild("Humanoid")
	humanoidRootPart = character:WaitForChild("HumanoidRootPart")
	head = character:WaitForChild("Head")
	rayCastParams.FilterDescendantsInstances = {character}
	bodyPosition.MaxForce = Vector3.new(10000,10000,10000)
	bodyPosition.P = 10000
	bodyPosition.D = 600
	
	bodyGyro.D = 600
	bodyGyro.P = 1000
	bodyGyro.MaxTorque = Vector3.new(30000,30000,30000)
	
	userInputService.InputBegan:Connect(function(input,gameProcessed)
		if gameProcessed then return end
		
		if input.KeyCode == climbInput then
			if not climbing then
				print("lets try")
				ClimbController.climb()
			end
		end
	end)
end

return ClimbController
1 Like

The RaycastResult has a Normal property that you can use to figure out where to point the character.

It looks like there is a feedback loop caused by the rootpart turning to point to the raycast and then the raycast subsequently returns a new position. I would suggest using the normal returned by the raycast to get a desired orientation for the root part.

it seens dont work too, just rotate me like that

bassicaly i want it rotate to fit on curves.