Animated edge climb system

I want to make climb system like in “Refinery Caves 2” game.

Here is the example:

I don’t know how to code this and animate it correctly. If you can, give me a similar topic, complete tutorial or script, or just tell me any ways to do this. Thank you!

I’ve found this un-copy-locked Experience that may be just what you’re looking for (play it first to check it out; Do note that if you download it, the animation most likely won’t work as it’s of the ownership of the player who published the Experience): Ledge Climbing - Roblox

1 Like

This is similar to what I need, but in the version I need, the player immediately climbs to the top.

In that case, I have made some edits to the LocalScript in StarterPlayer → StarterCharacterScripts → Vaulting . Erase everything and paste this code and let me know how it goes (afterwards, if it goes well, I may explain what I changed):

--services
local UserInputService = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local Debris = game:GetService("Debris")
local GuiService = game:GetService("GuiService")

--varibles
local player = game.Players.LocalPlayer
local character = player.Character
local camera = workspace.CurrentCamera
local humanoid = character:WaitForChild("Humanoid")
local rootPart = character:WaitForChild("HumanoidRootPart")
local head = character:WaitForChild("Head")

--modules
local cameraShaker = require(game.ReplicatedStorage:WaitForChild("Modules").CameraShaker)

--shake camera
local function ShakeCamera(shakeCf)
	camera.CFrame = camera.CFrame * shakeCf
end

-- Create CameraShaker instance:
local renderPriority = Enum.RenderPriority.Camera.Value + 1
local camShake = cameraShaker.new(renderPriority, ShakeCamera)

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

local vaultMoveNumber = 10
local canVault = true
local canMove = true
local vaultConnection = nil
local ledgePart = nil

--[[local grabAnim = humanoid:LoadAnimation(script:WaitForChild("Grab"))
local grabRightAnim = humanoid:LoadAnimation(script:WaitForChild("GrabRight"))
local grabLeftAnim = humanoid:LoadAnimation(script:WaitForChild("GrabLeft"))
local grabSounds = script:WaitForChild("Sounds"):GetChildren()]]

--play vault sounds
local function playSound()
	--[[local sound = grabSounds[math.random(1, #grabSounds)]:Clone()
	sound.Parent = rootPart
	sound.Volume = .15
	sound:Play()
	
	sound.Ended:Connect(function()
		sound:Destroy()
	end)]]
end

--check if part is above when tryin to vault or move
local function partCheck(ledge)
	local vaultPartCheck = workspace:Raycast(ledge.Position + Vector3.new(0, -1, 0) + ledge.LookVector * 1, ledge.UpVector * 3, raycastParams)
	if vaultPartCheck == nil then
		return true
	else
		return false
	end
end

local function vaultMoveCheck(ray, anim)
	local localPos = ray.Instance.CFrame:PointToObjectSpace(ray.Position)
	local localLedgePos = Vector3.new(localPos.X, ray.Instance.Size.Y/2, localPos.Z)
	local ledgePos = ray.Instance.CFrame:PointToWorldSpace(localLedgePos)
	local ledgeOffset = CFrame.lookAt(ledgePos, ledgePos - ray.Normal)
	
	if partCheck(ledgeOffset) then
		local magnitude = (ledgePos - head.Position).Magnitude
		if magnitude < 3 then
			local info = TweenInfo.new(.15, Enum.EasingStyle.Sine, Enum.EasingDirection.Out, 0, false, 0)
			local goal = {CFrame = ledgeOffset + Vector3.new(0, -2, 0) + ledgeOffset.LookVector * -1}
			--[[local tween = TweenService:Create(ledgePart, info, goal)
			tween:Play()]]
			canMove = false
			--playSound()

			--screen shake
			camShake:Start()
			local dashShake = camShake:ShakeOnce(.2, 13, 0, .5)
			dashShake:StartFadeOut(.5)

			--play anim
			--[[if anim == "Right" then
				grabRightAnim:Play()
			elseif anim == "Left" then
				grabLeftAnim:Play()
			end]]

			--vault move delay
			task.delay(.35, function()
				canMove = true
			end)
		end
	end
end


--moving from left to right function(so my code isn't messy)
local function vaultMove(direction, anim)
	local moveRay = workspace:Raycast(head.CFrame.Position, head.CFrame.RightVector * direction + head.CFrame.LookVector * 8, raycastParams)
	if moveRay then
		if moveRay.Instance then
			vaultMoveCheck(moveRay, anim)
		end
	else
		local turnRay = workspace:Raycast(head.CFrame.Position + Vector3.new(0, -1, 0) + head.CFrame.RightVector * direction, head.CFrame.RightVector * -direction + head.CFrame.LookVector * 2, raycastParams)
		if turnRay then
			if turnRay.Instance then
				vaultMoveCheck(turnRay, anim)
			end
		end
	end
end

--detect if moving left or right
humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
	if (humanoid.MoveDirection:Dot(camera.CFrame.RightVector) > .7) and not canVault and canMove then
		vaultMove(vaultMoveNumber, "Right")
	end
	
	if (humanoid.MoveDirection:Dot(-camera.CFrame.RightVector) > .7) and not canVault and canMove then
		vaultMove(-vaultMoveNumber, "Left")
	end
end)

--detect ledges
local function detectLedge()
	if canVault and (humanoid:GetState() == Enum.HumanoidStateType.Freefall or humanoid:GetState() == Enum.HumanoidStateType.Jumping) then
		local vaultCheck = workspace:Raycast(rootPart.CFrame.Position, rootPart.CFrame.LookVector * 5, raycastParams)
		if vaultCheck then
			if vaultCheck.Instance then			
				local localPos = vaultCheck.Instance.CFrame:PointToObjectSpace(vaultCheck.Position)
				local localLedgePos = Vector3.new(localPos.X, vaultCheck.Instance.Size.Y/2, localPos.Z)
				local ledgePos = vaultCheck.Instance.CFrame:PointToWorldSpace(localLedgePos)
				local ledgeOffset = CFrame.lookAt(ledgePos, ledgePos - vaultCheck.Normal)

				local magnitude = (ledgePos - head.Position).Magnitude
				if magnitude < 4 then
					if partCheck(ledgeOffset) then
						canVault = false

						--screen shake
						camShake:Start()
						local dashShake = camShake:ShakeOnce(.36, 12, 0, .5)
						dashShake:StartFadeOut(.5)
					end
				end
			end
		end
	elseif not canVault then
		canVault = true
		--humanoid.AutoRotate = true
		--rootPart.Anchored = false
		humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		--grabAnim:Stop()
		
		--check if it exists and then disconnect
		if vaultConnection then
			vaultConnection:Disconnect()
		end

		if ledgePart then
			ledgePart:Destroy()
		end
	end
end

--pc and console support
UserInputService.InputBegan:Connect(function(input, gp)
	if (input.KeyCode == Enum.KeyCode.ButtonA or input.KeyCode == Enum.KeyCode.Space) then
		detectLedge()
	end
end)

--mobile support
if UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled and not UserInputService.MouseEnabled and not UserInputService.GamepadEnabled and not GuiService:IsTenFootInterface() then
	local jumpButton = player.PlayerGui:WaitForChild("TouchGui"):WaitForChild("TouchControlFrame"):WaitForChild("JumpButton")
	jumpButton.Activated:Connect(function()
		detectLedge()
	end)
end

(Note that I have commented-out the parts that call for the animations and sounds to suppress the load errors and warnings).

1 Like

Works pretty good, but now i need climb animation.

Glad to hear!

I’m afraid that escapes my area. However, it’s a common practice for un-copy-locked Experiences to leave an ‘Anims’ rig that’s in the place that has the animations for you to upload them to your account (you may retrieve its animations by going to the ‘Avatar’ tab in Roblox Studio, and pressing over ‘Animation Editor’, or, at least, something like that).

Hope that helps. Keep me updated if you’d like.

1 Like

I made a few adjustments. Now I like everything, thanks for your help!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.