Help with Ledge Grab Animation

I redid a ledge grab system but an error is happening, so I came to ask for help. The entire script part of holding the ledge is working normally, the problem is the animation that instead of the player holding the ledge normally, it only activates once and then stops, causing this bug shown in the video below. Can someone please help me?

Script
--varibles--
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local GuiService = game:GetService("GuiService")

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")

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"))

--check--
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

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

			--Delay--
			task.delay(.35, function()
				canMove = true
			end)
		end
	end
end
             --Movement-- 
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 Direction--
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)

             --Detecting 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

						--Movement--
						ledgePart = Instance.new("Part")
						ledgePart.Parent = workspace
						ledgePart.Anchored = true
						ledgePart.Size = Vector3.one
						ledgePart.CFrame = ledgeOffset + Vector3.new(0, -2, 0) + ledgeOffset.LookVector * -1
						ledgePart.CanQuery = false
						ledgePart.CanCollide = false
						ledgePart.CanTouch = false
						ledgePart.Transparency = 1

						--Animation--
						grabAnim:Play()

						--Connection--
						vaultConnection = RunService.RenderStepped:Connect(function(dt)
							rootPart.Anchored = true
							humanoid.AutoRotate = false -- so shift lock doesnt't rotate character
							rootPart.CFrame = rootPart.CFrame:Lerp(CFrame.lookAt(ledgePart.Position, (ledgePart.CFrame * CFrame.new(0, 0, -1)).Position), .25)
							humanoid:ChangeState(Enum.HumanoidStateType.Seated)
						end)
					end
				end
			end
		end
	elseif not canVault then
		canVault = true
		humanoid.AutoRotate = true
		rootPart.Anchored = false
		humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		grabAnim:Stop()

		--Disconnect--
		if vaultConnection then
			vaultConnection:Disconnect()
		end

		if ledgePart then
			ledgePart:Destroy()
		end
	end
end

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

--Mobile--
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
Video

The animation does not have the loop activated, because when it is activated, even when jumping back to the ground the animation continues.

You could probably try to pause the animation right before it ends. For example:

grabRightAnim:Play() -- Animation plays.
task.wait(grabRightAnim.Length - 0.05) -- Waits until right before the animation ends.
grabRightAnim:AdjustSpeed(0) -- Make the animation stop at the current frame.

And if you are experiencing issue when the player jumps down, I would recommend detecting when the player’s HumanoidStateType changes. For example, if the HumanoidStateType changes to Freefall, Jumping, or Landed, you can stop the animation.

I would personally just make the user jump, and have the entire animation having them hang off, with no point having the arms down. So like a single keyframe, or if you wanted to add a swinging animation, just keep the arms where you want them the whole time.
OR
you could loop it, and just make it so ends when they jump down. (With an event of some sort)

I got this, and thank you very much, I just wanted to change the animation but I can only rotate the body parts in the animator so it never comes out the way I want

1 Like