Hello developers! I’ve worked on a custom “Over-The-Shoulder” camera for my game. However, I have an issue. I can’t seem to climb down a ladder or truss when I’m in this scriptable camera mode. How do I fix this?
I’ve tried switching back to the default Roblox-powered camera, and it works perfectly. However, I want to achieve the player climbing down a ladder with my custom camera.
VIDEO:
External Media
CODE:
function CameraHandler.OverTheShoulderCamera()
-- Basic player assets.
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:FindFirstChildOfClass("Humanoid")
local CurrentCamera = game.Workspace.CurrentCamera
-- Reset look angles
local y, x, _ : CFrame = workspace.CurrentCamera.CFrame:ToOrientation()
CameraHandler.CameraSettings.cameraAngles.AngleX = math.deg(x)
CameraHandler.CameraSettings.cameraAngles.AngleY = math.deg(y)
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
CurrentCamera.CameraType = Enum.CameraType.Scriptable
Humanoid.AutoRotate = false
local cameraConnection = nil
cameraConnection = RunService.Stepped:Connect(function(t: number, dT: number)
if Humanoid.Health == 0 then
CameraHandler.CameraSettings.CameraStates.OverTheShoulder = false
CameraHandler.CameraSettings.CameraStates.Default = true
CameraHandler.Reset(true)
cameraConnection:Disconnect()
end
-- OTS camera system.
local startCFrame : CFrame = CFrame.new(Character.HumanoidRootPart.Position) * CFrame.Angles(0, math.rad(CameraHandler.CameraSettings.cameraAngles.AngleX), 0) * CFrame.Angles(math.rad(CameraHandler.CameraSettings.cameraAngles.AngleY), 0, 0)
-- If the camera state is default, then we'll use the default camera offset.
if CameraHandler.CameraSettings.CameraStates["Default"] then
CameraHandler.CameraSettings.cameraOffset = CameraHandler.CameraSettings.defaultCameraPosition
CameraHandler.CameraSettings.cameraTransitionNumber += dT
if CameraHandler.CameraSettings.cameraTransitionNumber > 0.2 then
CameraHandler.Reset(true)
cameraConnection:Disconnect()
end
end
-- OTS camera CFrame values.
local cameraCFrame : Vector3 = startCFrame:PointToWorldSpace(CameraHandler.CameraSettings.cameraOffset)
local cameraFocus : Vector3 = startCFrame:PointToWorldSpace(Vector3.new(CameraHandler.CameraSettings.cameraOffset.X, CameraHandler.CameraSettings.cameraOffset.Y, -10000))
local resultingCameraCFrame : CFrame = CFrame.new(cameraCFrame, cameraFocus)
-- Side-To-Side camera system.
if CameraHandler.CameraSettings.isFirearmCameraRight then
CameraHandler.CameraSettings.cameraOffset = CameraHandler.CameraSettings.cameraOffsetRight
if Movement.GetState()["Crouching"] then
CameraHandler.CameraSettings.cameraOffset = CameraHandler.CameraSettings.cameraOffsetRightCrouching
end
else
CameraHandler.CameraSettings.cameraOffset = CameraHandler.CameraSettings.cameraOffsetLeft
if Movement.GetState()["Crouching"] then
CameraHandler.CameraSettings.cameraOffset = CameraHandler.CameraSettings.cameraOffsetLeftCrouching
end
end
-- Create raycast parameters for the camera.
local cameraRaycastParameters = RaycastParams.new()
cameraRaycastParameters.FilterType = Enum.RaycastFilterType.Exclude
cameraRaycastParameters.IgnoreWater = true
cameraRaycastParameters.FilterDescendantsInstances = {Character}
-- Cast a ray behind the player's camera to avoid seeing through parts.
local cameraRaycastResult = game.Workspace:Raycast(Character:GetPivot().Position, -resultingCameraCFrame.LookVector * 6, cameraRaycastParameters)
-- Cast a ray above the player's camera to avoid seeing through parts.
local cameraUpRaycastResult = game.Workspace:Raycast(Character:GetPivot().Position, resultingCameraCFrame.UpVector * 10, cameraRaycastParameters)
-- Cast a ray going left to right the player's camera to avoid seeing through parts.
local sideViewCameraCFrame = resultingCameraCFrame.RightVector * 6
if not CameraHandler.CameraSettings.isFirearmCameraRight then sideViewCameraCFrame = -resultingCameraCFrame.RightVector * 6 end
local cameraSideRaycastResult = game.Workspace:Raycast(Character:GetPivot().Position, sideViewCameraCFrame, cameraRaycastParameters)
-- Determine if the ray touches any parts.
if not CameraHandler.CameraSettings.CameraStates.Default then
if cameraRaycastResult then
local backDistance = (Character:GetPivot().Position - cameraRaycastResult.Position).Magnitude
if backDistance < 5.5 then
CameraHandler.CameraSettings.cameraOffset = Vector3.new(0, CameraHandler.CameraSettings.cameraOffset.Y, 0)
end
cameraCFrame = startCFrame:PointToWorldSpace(Vector3.new(CameraHandler.CameraSettings.cameraOffset.X, CameraHandler.CameraSettings.cameraOffset.Y, backDistance))
resultingCameraCFrame = CFrame.new(cameraCFrame, cameraFocus)
elseif cameraSideRaycastResult then
local sideDistance = (Character:GetPivot().Position - cameraSideRaycastResult.Position).Magnitude
if sideDistance < 2.5 then
CameraHandler.CameraSettings.cameraOffset = Vector3.new(0, CameraHandler.CameraSettings.cameraOffset.Y, 0)
end
cameraCFrame = startCFrame:PointToWorldSpace(Vector3.new(CameraHandler.CameraSettings.cameraOffset.X, CameraHandler.CameraSettings.cameraOffset.Y, sideDistance))
resultingCameraCFrame = CFrame.new(cameraCFrame, cameraFocus)
elseif cameraUpRaycastResult then
local upDistance = (Character:GetPivot().Position - cameraUpRaycastResult.Position).Magnitude
cameraCFrame = startCFrame:PointToWorldSpace(Vector3.new(CameraHandler.CameraSettings.cameraOffset.X, CameraHandler.CameraSettings.cameraOffset.Y, upDistance))
resultingCameraCFrame = CFrame.new(cameraCFrame, cameraFocus)
end
end
-- Root Part movement.
local RootPart : BasePart = Character.HumanoidRootPart
local lookingCFrame : Vector3 = CurrentCamera.CFrame:PointToWorldSpace(Vector3.new(0, 0, -10000))
local rootLookDirection : Vector3 = (lookingCFrame - RootPart.CFrame.Position)
local rootYaw : number = math.atan2(-rootLookDirection.X, -rootLookDirection.Z)
if CameraHandler.CameraSettings.CameraStates["OverTheShoulder"] then
RootPart.CFrame = CFrame.new(RootPart.CFrame.Position) * CFrame.Angles(0, rootYaw, 0)
end
-- Setup camera movement.
local cameraAlpha = Interpolate:Exponential(dT, 2)
CurrentCamera.CFrame = CurrentCamera.CFrame:Lerp(resultingCameraCFrame, cameraAlpha)
end)
end
I’ve tried messing with the camera modes and some code, but nothing seemed to work.