It’s under PlayerModule.CameraModule.BaseCamera. The PlayerModule moduleScript is NOT made by me, it is thrown in by Roblox automatically on player spawn.
Here is the full script that is printing the error:
--!nonstrict
--!nolint DeprecatedApi
--[[
BaseCamera - Abstract base class for camera control modules
2018 Camera Update - AllYourBlox
--]]
--[[ Local Constants ]]--
local CommonUtils = script.Parent.Parent:WaitForChild("CommonUtils")
local FlagUtil = require(CommonUtils:WaitForChild("FlagUtil"))
local FFlagUserFixGamepadMaxZoom
do
local success, result = pcall(function()
return UserSettings():IsUserFeatureEnabled("UserFixGamepadMaxZoom")
end)
FFlagUserFixGamepadMaxZoom = success and result
end
local FFlagUserFixCameraOffsetJitter = FlagUtil.getUserFlag("UserFixCameraOffsetJitter2")
local UNIT_Z = Vector3.new(0,0,1)
local X1_Y0_Z1 = Vector3.new(1,0,1) --Note: not a unit vector, used for projecting onto XZ plane
local DEFAULT_DISTANCE = 12.5 -- Studs
local PORTRAIT_DEFAULT_DISTANCE = 25 -- Studs
local FIRST_PERSON_DISTANCE_THRESHOLD = 1.0 -- Below this value, snap into first person
-- Note: DotProduct check in CoordinateFrame::lookAt() prevents using values within about
-- 8.11 degrees of the +/- Y axis, that's why these limits are currently 80 degrees
local MIN_Y = math.rad(-80)
local MAX_Y = math.rad(80)
local VR_ANGLE = math.rad(15)
local VR_LOW_INTENSITY_ROTATION = Vector2.new(math.rad(15), 0)
local VR_HIGH_INTENSITY_ROTATION = Vector2.new(math.rad(45), 0)
local VR_LOW_INTENSITY_REPEAT = 0.1
local VR_HIGH_INTENSITY_REPEAT = 0.4
local ZERO_VECTOR2 = Vector2.new(0,0)
local ZERO_VECTOR3 = Vector3.new(0,0,0)
local SEAT_OFFSET = Vector3.new(0,5,0)
local VR_SEAT_OFFSET = Vector3.new(0,4,0)
local HEAD_OFFSET = Vector3.new(0,1.5,0)
local R15_HEAD_OFFSET = Vector3.new(0, 1.5, 0)
local R15_HEAD_OFFSET_NO_SCALING = Vector3.new(0, 2, 0)
local HUMANOID_ROOT_PART_SIZE = Vector3.new(2, 2, 1)
local ZOOM_SENSITIVITY_CURVATURE = 0.5
local FIRST_PERSON_DISTANCE_MIN = 0.5
local CameraUtils = require(script.Parent:WaitForChild("CameraUtils"))
local ZoomController = require(script.Parent:WaitForChild("ZoomController"))
local CameraToggleStateController = require(script.Parent:WaitForChild("CameraToggleStateController"))
local CameraInput = require(script.Parent:WaitForChild("CameraInput"))
local CameraUI = require(script.Parent:WaitForChild("CameraUI"))
--[[ Roblox Services ]]--
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local StarterGui = game:GetService("StarterGui")
local VRService = game:GetService("VRService")
local UserGameSettings = UserSettings():GetService("UserGameSettings")
local player = Players.LocalPlayer
--[[ The Module ]]--
local BaseCamera = {}
BaseCamera.__index = BaseCamera
function BaseCamera.new()
local self = setmetatable({}, BaseCamera)
self.gamepadZoomLevels = {0, 10, 20} -- zoom levels that are cycled through on a gamepad R3 press
-- So that derived classes have access to this
self.FIRST_PERSON_DISTANCE_THRESHOLD = FIRST_PERSON_DISTANCE_THRESHOLD
self.cameraType = nil
self.cameraMovementMode = nil
self.lastCameraTransform = nil
self.lastUserPanCamera = tick()
self.humanoidRootPart = nil
self.humanoidCache = {}
-- Subject and position on last update call
self.lastSubject = nil
self.lastSubjectPosition = Vector3.new(0, 5, 0)
self.lastSubjectCFrame = CFrame.new(self.lastSubjectPosition)
self.currentSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
self.inFirstPerson = false
self.inMouseLockedMode = false
self.portraitMode = false
self.isSmallTouchScreen = false
-- Used by modules which want to reset the camera angle on respawn.
self.resetCameraAngle = true
self.enabled = false
-- Input Event Connections
self.PlayerGui = nil
self.cameraChangedConn = nil
self.viewportSizeChangedConn = nil
-- VR Support
self.shouldUseVRRotation = false
self.VRRotationIntensityAvailable = false
self.lastVRRotationIntensityCheckTime = 0
self.lastVRRotationTime = 0
self.vrRotateKeyCooldown = {}
self.cameraTranslationConstraints = Vector3.new(1, 1, 1)
self.humanoidJumpOrigin = nil
self.trackingHumanoid = nil
self.cameraFrozen = false
self.subjectStateChangedConn = nil
self.gamepadZoomPressConnection = nil
-- Mouse locked formerly known as shift lock mode
self.mouseLockOffset = ZERO_VECTOR3
-- Initialization things used to always execute at game load time, but now these camera modules are instantiated
-- when needed, so the code here may run well after the start of the game
if player.Character then
self:OnCharacterAdded(player.Character)
end
player.CharacterAdded:Connect(function(char)
self:OnCharacterAdded(char)
end)
if self.playerCameraModeChangeConn then self.playerCameraModeChangeConn:Disconnect() end
self.playerCameraModeChangeConn = player:GetPropertyChangedSignal("CameraMode"):Connect(function()
self:OnPlayerCameraPropertyChange()
end)
if self.minDistanceChangeConn then self.minDistanceChangeConn:Disconnect() end
self.minDistanceChangeConn = player:GetPropertyChangedSignal("CameraMinZoomDistance"):Connect(function()
self:OnPlayerCameraPropertyChange()
end)
if self.maxDistanceChangeConn then self.maxDistanceChangeConn:Disconnect() end
self.maxDistanceChangeConn = player:GetPropertyChangedSignal("CameraMaxZoomDistance"):Connect(function()
self:OnPlayerCameraPropertyChange()
end)
if self.playerDevTouchMoveModeChangeConn then self.playerDevTouchMoveModeChangeConn:Disconnect() end
self.playerDevTouchMoveModeChangeConn = player:GetPropertyChangedSignal("DevTouchMovementMode"):Connect(function()
self:OnDevTouchMovementModeChanged()
end)
self:OnDevTouchMovementModeChanged() -- Init
if self.gameSettingsTouchMoveMoveChangeConn then self.gameSettingsTouchMoveMoveChangeConn:Disconnect() end
self.gameSettingsTouchMoveMoveChangeConn = UserGameSettings:GetPropertyChangedSignal("TouchMovementMode"):Connect(function()
self:OnGameSettingsTouchMovementModeChanged()
end)
self:OnGameSettingsTouchMovementModeChanged() -- Init
UserGameSettings:SetCameraYInvertVisible()
UserGameSettings:SetGamepadCameraSensitivityVisible()
self.hasGameLoaded = game:IsLoaded()
if not self.hasGameLoaded then
self.gameLoadedConn = game.Loaded:Connect(function()
self.hasGameLoaded = true
self.gameLoadedConn:Disconnect()
self.gameLoadedConn = nil
end)
end
self:OnPlayerCameraPropertyChange()
return self
end
function BaseCamera:GetModuleName()
return "BaseCamera"
end
function BaseCamera:OnCharacterAdded(char)
self.resetCameraAngle = self.resetCameraAngle or self:GetEnabled()
self.humanoidRootPart = nil
if UserInputService.TouchEnabled then
self.PlayerGui = player:WaitForChild("PlayerGui")
for _, child in ipairs(char:GetChildren()) do
if child:IsA("Tool") then
self.isAToolEquipped = true
end
end
char.ChildAdded:Connect(function(child)
if child:IsA("Tool") then
self.isAToolEquipped = true
end
end)
char.ChildRemoved:Connect(function(child)
if child:IsA("Tool") then
self.isAToolEquipped = false
end
end)
end
end
function BaseCamera:GetHumanoidRootPart(): BasePart
if not self.humanoidRootPart then
if player.Character then
local humanoid = player.Character:FindFirstChildOfClass("Humanoid")
if humanoid then
self.humanoidRootPart = humanoid.RootPart
end
end
end
return self.humanoidRootPart
end
function BaseCamera:GetBodyPartToFollow(humanoid: Humanoid, isDead: boolean) -- BasePart
-- If the humanoid is dead, prefer the head part if one still exists as a sibling of the humanoid
if humanoid:GetState() == Enum.HumanoidStateType.Dead then
local character = humanoid.Parent
if character and character:IsA("Model") then
return character:FindFirstChild("Head") or humanoid.RootPart
end
end
return humanoid.RootPart
end
function BaseCamera:GetSubjectCFrame(): CFrame
local result = self.lastSubjectCFrame
local camera = workspace.CurrentCamera
local cameraSubject = camera and camera.CameraSubject
if not cameraSubject then
return result
end
if cameraSubject:IsA("Humanoid") then
local humanoid = cameraSubject
local humanoidIsDead = humanoid:GetState() == Enum.HumanoidStateType.Dead
local cameraOffset = humanoid.CameraOffset
-- when in mouse lock mode, the character's rotation follows the camera instead of vice versa.
-- Allow the mouse lock calculation to be camera based instead of subject based to prevent jitter
if FFlagUserFixCameraOffsetJitter and self:GetIsMouseLocked() then
cameraOffset = Vector3.new()
end
local bodyPartToFollow = humanoid.RootPart
-- If the humanoid is dead, prefer their head part as a follow target, if it exists
if humanoidIsDead then
if humanoid.Parent and humanoid.Parent:IsA("Model") then
bodyPartToFollow = humanoid.Parent:FindFirstChild("Head") or bodyPartToFollow
end
end
if bodyPartToFollow and bodyPartToFollow:IsA("BasePart") then
local heightOffset
if humanoid.RigType == Enum.HumanoidRigType.R15 then
if humanoid.AutomaticScalingEnabled then
heightOffset = R15_HEAD_OFFSET
local rootPart = humanoid.RootPart
if bodyPartToFollow == rootPart then
local rootPartSizeOffset = (rootPart.Size.Y - HUMANOID_ROOT_PART_SIZE.Y)/2
heightOffset = heightOffset + Vector3.new(0, rootPartSizeOffset, 0)
end
else
heightOffset = R15_HEAD_OFFSET_NO_SCALING
end
else
heightOffset = HEAD_OFFSET
end
if humanoidIsDead then
heightOffset = ZERO_VECTOR3
end
result = bodyPartToFollow.CFrame*CFrame.new(heightOffset + cameraOffset)
end
elseif cameraSubject:IsA("BasePart") then
result = cameraSubject.CFrame
elseif cameraSubject:IsA("Model") then
-- Model subjects are expected to have a PrimaryPart to determine orientation
if cameraSubject.PrimaryPart then
result = cameraSubject:GetPrimaryPartCFrame()
else
result = CFrame.new()
end
end
if result then
self.lastSubjectCFrame = result
end
return result
end
function BaseCamera:GetSubjectVelocity(): Vector3
local camera = workspace.CurrentCamera
local cameraSubject = camera and camera.CameraSubject
if not cameraSubject then
return ZERO_VECTOR3
end
if cameraSubject:IsA("BasePart") then
return cameraSubject.Velocity
elseif cameraSubject:IsA("Humanoid") then
local rootPart = cameraSubject.RootPart
if rootPart then
return rootPart.Velocity
end
elseif cameraSubject:IsA("Model") then
local primaryPart = cameraSubject.PrimaryPart
if primaryPart then
return primaryPart.Velocity
end
end
return ZERO_VECTOR3
end
function BaseCamera:GetSubjectRotVelocity(): Vector3
local camera = workspace.CurrentCamera
local cameraSubject = camera and camera.CameraSubject
if not cameraSubject then
return ZERO_VECTOR3
end
if cameraSubject:IsA("BasePart") then
return cameraSubject.RotVelocity
elseif cameraSubject:IsA("Humanoid") then
local rootPart = cameraSubject.RootPart
if rootPart then
return rootPart.RotVelocity
end
elseif cameraSubject:IsA("Model") then
local primaryPart = cameraSubject.PrimaryPart
if primaryPart then
return primaryPart.RotVelocity
end
end
return ZERO_VECTOR3
end
function BaseCamera:StepZoom()
local zoom: number = self.currentSubjectDistance
local zoomDelta: number = CameraInput.getZoomDelta()
if math.abs(zoomDelta) > 0 then
local newZoom
if zoomDelta > 0 then
newZoom = zoom + zoomDelta*(1 + zoom*ZOOM_SENSITIVITY_CURVATURE)
newZoom = math.max(newZoom, self.FIRST_PERSON_DISTANCE_THRESHOLD)
else
newZoom = (zoom + zoomDelta)/(1 - zoomDelta*ZOOM_SENSITIVITY_CURVATURE)
newZoom = math.max(newZoom, FIRST_PERSON_DISTANCE_MIN)
end
if newZoom < self.FIRST_PERSON_DISTANCE_THRESHOLD then
newZoom = FIRST_PERSON_DISTANCE_MIN
end
self:SetCameraToSubjectDistance(newZoom)
end
return ZoomController.GetZoomRadius()
end
function BaseCamera:GetSubjectPosition(): Vector3?
local result = self.lastSubjectPosition
local camera = game.Workspace.CurrentCamera
local cameraSubject = camera and camera.CameraSubject
if cameraSubject then
if cameraSubject:IsA("Humanoid") then
local humanoid = cameraSubject
local humanoidIsDead = humanoid:GetState() == Enum.HumanoidStateType.Dead
local cameraOffset = humanoid.CameraOffset
-- when in mouse lock mode, the character's rotation follows the camera instead of vice versa.
-- Allow the mouse lock calculation to be camera based instead of subject based to prevent jitter
if FFlagUserFixCameraOffsetJitter and self:GetIsMouseLocked() then
cameraOffset = Vector3.new()
end
local bodyPartToFollow = humanoid.RootPart
-- If the humanoid is dead, prefer their head part as a follow target, if it exists
if humanoidIsDead then
if humanoid.Parent and humanoid.Parent:IsA("Model") then
bodyPartToFollow = humanoid.Parent:FindFirstChild("Head") or bodyPartToFollow
end
end
if bodyPartToFollow and bodyPartToFollow:IsA("BasePart") then
local heightOffset
if humanoid.RigType == Enum.HumanoidRigType.R15 then
if humanoid.AutomaticScalingEnabled then
heightOffset = R15_HEAD_OFFSET
if bodyPartToFollow == humanoid.RootPart then
local rootPartSizeOffset = (humanoid.RootPart.Size.Y/2) - (HUMANOID_ROOT_PART_SIZE.Y/2)
heightOffset = heightOffset + Vector3.new(0, rootPartSizeOffset, 0)
end
else
heightOffset = R15_HEAD_OFFSET_NO_SCALING
end
else
heightOffset = HEAD_OFFSET
end
if humanoidIsDead then
heightOffset = ZERO_VECTOR3
end
result = bodyPartToFollow.CFrame.p + bodyPartToFollow.CFrame:vectorToWorldSpace(heightOffset + cameraOffset)
end
elseif cameraSubject:IsA("VehicleSeat") then
local offset = SEAT_OFFSET
result = cameraSubject.CFrame.p + cameraSubject.CFrame:vectorToWorldSpace(offset)
elseif cameraSubject:IsA("SkateboardPlatform") then
result = cameraSubject.CFrame.p + SEAT_OFFSET
elseif cameraSubject:IsA("BasePart") then
result = cameraSubject.CFrame.p
elseif cameraSubject:IsA("Model") then
if cameraSubject.PrimaryPart then
result = cameraSubject:GetPrimaryPartCFrame().p
else
result = cameraSubject:GetModelCFrame().p
end
end
else
-- cameraSubject is nil
-- Note: Previous RootCamera did not have this else case and let self.lastSubject and self.lastSubjectPosition
-- both get set to nil in the case of cameraSubject being nil. This function now exits here to preserve the
-- last set valid values for these, as nil values are not handled cases
return nil
end
self.lastSubject = cameraSubject
self.lastSubjectPosition = result
return result
end
function BaseCamera:OnViewportSizeChanged()
local camera = game.Workspace.CurrentCamera
local size = camera.ViewportSize
self.portraitMode = size.X < size.Y
self.isSmallTouchScreen = UserInputService.TouchEnabled and (size.Y < 500 or size.X < 700)
end
-- Listener for changes to workspace.CurrentCamera
function BaseCamera:OnCurrentCameraChanged()
if UserInputService.TouchEnabled then
if self.viewportSizeChangedConn then
self.viewportSizeChangedConn:Disconnect()
self.viewportSizeChangedConn = nil
end
local newCamera = game.Workspace.CurrentCamera
if newCamera then
self:OnViewportSizeChanged()
self.viewportSizeChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
self:OnViewportSizeChanged()
end)
end
end
-- VR support additions
if self.cameraSubjectChangedConn then
self.cameraSubjectChangedConn:Disconnect()
self.cameraSubjectChangedConn = nil
end
local camera = game.Workspace.CurrentCamera
if camera then
self.cameraSubjectChangedConn = camera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
self:OnNewCameraSubject()
end)
self:OnNewCameraSubject()
end
end
function BaseCamera:OnDynamicThumbstickEnabled()
if UserInputService.TouchEnabled then
self.isDynamicThumbstickEnabled = true
end
end
function BaseCamera:OnDynamicThumbstickDisabled()
self.isDynamicThumbstickEnabled = false
end
function BaseCamera:OnGameSettingsTouchMovementModeChanged()
if player.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice then
if (UserGameSettings.TouchMovementMode == Enum.TouchMovementMode.DynamicThumbstick
or UserGameSettings.TouchMovementMode == Enum.TouchMovementMode.Default) then
self:OnDynamicThumbstickEnabled()
else
self:OnDynamicThumbstickDisabled()
end
end
end
function BaseCamera:OnDevTouchMovementModeChanged()
if player.DevTouchMovementMode == Enum.DevTouchMovementMode.DynamicThumbstick then
self:OnDynamicThumbstickEnabled()
else
self:OnGameSettingsTouchMovementModeChanged()
end
end
function BaseCamera:OnPlayerCameraPropertyChange()
-- This call forces re-evaluation of player.CameraMode and clamping to min/max distance which may have changed
self:SetCameraToSubjectDistance(self.currentSubjectDistance)
end
function BaseCamera:InputTranslationToCameraAngleChange(translationVector, sensitivity)
return translationVector * sensitivity
end
-- cycles between zoom levels in self.gamepadZoomLevels, setting CameraToSubjectDistance. gamepadZoomLevels may
-- be out of range of Min/Max camera zoom
function BaseCamera:GamepadZoomPress()
-- this code relies on the fact that SetCameraToSubjectDistance will clamp the min and max
local dist = self:GetCameraToSubjectDistance()
local max = player.CameraMaxZoomDistance
-- check from largest to smallest, set the first zoom level which is
-- below the threshold
for i = #self.gamepadZoomLevels, 1, -1 do
local zoom = self.gamepadZoomLevels[i]
if max < zoom then
continue
end
if zoom < player.CameraMinZoomDistance then
zoom = player.CameraMinZoomDistance
if FFlagUserFixGamepadMaxZoom then
-- no more zoom levels to check, all the remaining ones
-- are < min
if max == zoom then
break
end
end
end
if not FFlagUserFixGamepadMaxZoom then
if max == zoom then
break
end
end
-- theshold is set at halfway between zoom levels
if dist > zoom + (max - zoom) / 2 then
self:SetCameraToSubjectDistance(zoom)
return
end
max = zoom
end
-- cycle back to the largest, relies on the fact that SetCameraToSubjectDistance will clamp max and min
self:SetCameraToSubjectDistance(self.gamepadZoomLevels[#self.gamepadZoomLevels])
end
function BaseCamera:Enable(enable: boolean)
if self.enabled ~= enable then
self.enabled = enable
self:OnEnabledChanged()
end
end
function BaseCamera:OnEnabledChanged()
if self.enabled then
CameraInput.setInputEnabled(true)
self.gamepadZoomPressConnection = CameraInput.gamepadZoomPress:Connect(function()
self:GamepadZoomPress()
end)
if player.CameraMode == Enum.CameraMode.LockFirstPerson then
self.currentSubjectDistance = 0.5
if not self.inFirstPerson then
self:EnterFirstPerson()
end
end
if self.cameraChangedConn then self.cameraChangedConn:Disconnect(); self.cameraChangedConn = nil end
self.cameraChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
self:OnCurrentCameraChanged()
end)
self:OnCurrentCameraChanged()
else
CameraInput.setInputEnabled(false)
if self.gamepadZoomPressConnection then
self.gamepadZoomPressConnection:Disconnect()
self.gamepadZoomPressConnection = nil
end
-- Clean up additional event listeners and reset a bunch of properties
self:Cleanup()
end
end
function BaseCamera:GetEnabled(): boolean
return self.enabled
end
function BaseCamera:Cleanup()
if self.subjectStateChangedConn then
self.subjectStateChangedConn:Disconnect()
self.subjectStateChangedConn = nil
end
if self.viewportSizeChangedConn then
self.viewportSizeChangedConn:Disconnect()
self.viewportSizeChangedConn = nil
end
if self.cameraChangedConn then
self.cameraChangedConn:Disconnect()
self.cameraChangedConn = nil
end
self.lastCameraTransform = nil
self.lastSubjectCFrame = nil
-- Unlock mouse for example if right mouse button was being held down
CameraUtils.restoreMouseBehavior()
end
function BaseCamera:UpdateMouseBehavior()
local blockToggleDueToClickToMove = UserGameSettings.ComputerMovementMode == Enum.ComputerMovementMode.ClickToMove
if self.isCameraToggle and blockToggleDueToClickToMove == false then
CameraUI.setCameraModeToastEnabled(true)
CameraInput.enableCameraToggleInput()
CameraToggleStateController(self.inFirstPerson)
else
CameraUI.setCameraModeToastEnabled(false)
CameraInput.disableCameraToggleInput()
-- first time transition to first person mode or mouse-locked third person
if self.inFirstPerson or self.inMouseLockedMode then
CameraUtils.setRotationTypeOverride(Enum.RotationType.CameraRelative)
CameraUtils.setMouseBehaviorOverride(Enum.MouseBehavior.LockCenter)
else
CameraUtils.restoreRotationType()
local rotationActivated = CameraInput.getRotationActivated()
if rotationActivated then
CameraUtils.setMouseBehaviorOverride(Enum.MouseBehavior.LockCurrentPosition)
else
CameraUtils.restoreMouseBehavior()
end
end
end
end
function BaseCamera:UpdateForDistancePropertyChange()
-- Calling this setter with the current value will force checking that it is still
-- in range after a change to the min/max distance limits
self:SetCameraToSubjectDistance(self.currentSubjectDistance)
end
function BaseCamera:SetCameraToSubjectDistance(desiredSubjectDistance: number): number
local lastSubjectDistance = self.currentSubjectDistance
-- By default, camera modules will respect LockFirstPerson and override the currentSubjectDistance with 0
-- regardless of what Player.CameraMinZoomDistance is set to, so that first person can be made
-- available by the developer without needing to allow players to mousewheel dolly into first person.
-- Some modules will override this function to remove or change first-person capability.
if player.CameraMode == Enum.CameraMode.LockFirstPerson then
self.currentSubjectDistance = 0.5
if not self.inFirstPerson then
self:EnterFirstPerson()
end
else
local newSubjectDistance = math.clamp(desiredSubjectDistance, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
if newSubjectDistance < FIRST_PERSON_DISTANCE_THRESHOLD then
self.currentSubjectDistance = 0.5
if not self.inFirstPerson then
self:EnterFirstPerson()
end
else
self.currentSubjectDistance = newSubjectDistance
if self.inFirstPerson then
self:LeaveFirstPerson()
end
end
end
-- Pass target distance and zoom direction to the zoom controller
ZoomController.SetZoomParameters(self.currentSubjectDistance, math.sign(desiredSubjectDistance - lastSubjectDistance))
-- Returned only for convenience to the caller to know the outcome
return self.currentSubjectDistance
end
function BaseCamera:SetCameraType( cameraType )
--Used by derived classes
self.cameraType = cameraType
end
function BaseCamera:GetCameraType()
return self.cameraType
end
-- Movement mode standardized to Enum.ComputerCameraMovementMode values
function BaseCamera:SetCameraMovementMode( cameraMovementMode )
self.cameraMovementMode = cameraMovementMode
end
function BaseCamera:GetCameraMovementMode()
return self.cameraMovementMode
end
function BaseCamera:SetIsMouseLocked(mouseLocked: boolean)
self.inMouseLockedMode = mouseLocked
end
function BaseCamera:GetIsMouseLocked(): boolean
return self.inMouseLockedMode
end
function BaseCamera:SetMouseLockOffset(offsetVector)
self.mouseLockOffset = offsetVector
end
function BaseCamera:GetMouseLockOffset()
return self.mouseLockOffset
end
function BaseCamera:InFirstPerson(): boolean
return self.inFirstPerson
end
function BaseCamera:EnterFirstPerson()
self.inFirstPerson = true
self:UpdateMouseBehavior()
end
function BaseCamera:LeaveFirstPerson()
self.inFirstPerson = false
self:UpdateMouseBehavior()
end
-- Nominal distance, set by dollying in and out with the mouse wheel or equivalent, not measured distance
function BaseCamera:GetCameraToSubjectDistance(): number
return self.currentSubjectDistance
end
-- Actual measured distance to the camera Focus point, which may be needed in special circumstances, but should
-- never be used as the starting point for updating the nominal camera-to-subject distance (self.currentSubjectDistance)
-- since that is a desired target value set only by mouse wheel (or equivalent) input, PopperCam, and clamped to min max camera distance
function BaseCamera:GetMeasuredDistanceToFocus(): number?
local camera = game.Workspace.CurrentCamera
if camera then
return (camera.CoordinateFrame.p - camera.Focus.p).magnitude
end
return nil
end
function BaseCamera:GetCameraLookVector(): Vector3
return game.Workspace.CurrentCamera and game.Workspace.CurrentCamera.CFrame.LookVector or UNIT_Z
end
function BaseCamera:CalculateNewLookCFrameFromArg(suppliedLookVector: Vector3?, rotateInput: Vector2): CFrame
local currLookVector: Vector3 = suppliedLookVector or self:GetCameraLookVector()
local currPitchAngle = math.asin(currLookVector.Y)
local yTheta = math.clamp(rotateInput.Y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle)
local constrainedRotateInput = Vector2.new(rotateInput.X, yTheta)
local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.X, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.Y,0,0)
return newLookCFrame
end
function BaseCamera:CalculateNewLookVectorFromArg(suppliedLookVector: Vector3?, rotateInput: Vector2): Vector3
local newLookCFrame = self:CalculateNewLookCFrameFromArg(suppliedLookVector, rotateInput)
return newLookCFrame.LookVector
end
function BaseCamera:CalculateNewLookVectorVRFromArg(rotateInput: Vector2): Vector3
local subjectPosition: Vector3 = self:GetSubjectPosition()
local vecToSubject: Vector3 = (subjectPosition - (game.Workspace.CurrentCamera :: Camera).CFrame.p)
local currLookVector: Vector3 = (vecToSubject * X1_Y0_Z1).unit
local vrRotateInput: Vector2 = Vector2.new(rotateInput.X, 0)
local startCFrame: CFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
local yawRotatedVector: Vector3 = (CFrame.Angles(0, -vrRotateInput.X, 0) * startCFrame * CFrame.Angles(-vrRotateInput.Y,0,0)).LookVector
return (yawRotatedVector * X1_Y0_Z1).unit
end
function BaseCamera:GetHumanoid(): Humanoid?
local character = player and player.Character
if character then
local resultHumanoid = self.humanoidCache[player]
if resultHumanoid and resultHumanoid.Parent == character then
return resultHumanoid
else
self.humanoidCache[player] = nil -- Bust Old Cache
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
self.humanoidCache[player] = humanoid
end
return humanoid
end
end
return nil
end
function BaseCamera:GetHumanoidPartToFollow(humanoid: Humanoid, humanoidStateType: Enum.HumanoidStateType) -- BasePart
if humanoidStateType == Enum.HumanoidStateType.Dead then
local character = humanoid.Parent
if character then
return character:FindFirstChild("Head") or humanoid.Torso
else
return humanoid.Torso
end
else
return humanoid.Torso
end
end
function BaseCamera:OnNewCameraSubject()
if self.subjectStateChangedConn then
self.subjectStateChangedConn:Disconnect()
self.subjectStateChangedConn = nil
end
end
function BaseCamera:IsInFirstPerson()
return self.inFirstPerson
end
function BaseCamera:Update(dt)
error("BaseCamera:Update() This is a virtual function that should never be getting called.", 2)
end
function BaseCamera:GetCameraHeight()
if VRService.VREnabled and not self.inFirstPerson then
return math.sin(VR_ANGLE) * self.currentSubjectDistance
end
return 0
end
return BaseCamera
EDIT: THIS IS THE FUNCTION THAT CONTAINS LINE 799 SPECIFICALLY
function BaseCamera:CalculateNewLookCFrameFromArg(suppliedLookVector: Vector3?, rotateInput: Vector2): CFrame
local currLookVector: Vector3 = suppliedLookVector or self:GetCameraLookVector()
local currPitchAngle = math.asin(currLookVector.Y)
local yTheta = math.clamp(rotateInput.Y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle) -- This is line 799
local constrainedRotateInput = Vector2.new(rotateInput.X, yTheta)
local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.X, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.Y,0,0)
return newLookCFrame
end