Super-Nostalgia-Zone/Client/Camera/Main.lua

1042 lines
29 KiB
Lua

local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local StarterGui = game:GetService("StarterGui")
local GuiService = game:GetService("GuiService")
local ContextActionService = game:GetService("ContextActionService")
local TeleportService = game:GetService("TeleportService")
local Debris = game:GetService("Debris")
local LocalPlayer = Players.LocalPlayer
local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")
local GameSettings = UserSettings():GetService("UserGameSettings")
local math_abs = math.abs
local math_asin = math.asin
local math_atan2 = math.atan2
local math_floor = math.floor
local math_min = math.min
local math_max = math.max
local math_pi = math.pi
local math_rad = math.rad
local Vector2_new = Vector2.new
local Vector3_new = Vector3.new
local CFrame_Angles = CFrame.Angles
local CFrame_new = CFrame.new
local MIN_Y = math_rad(-80)
local MAX_Y = math_rad(80)
local ZERO_VECTOR2 = Vector2_new()
local ZERO_VECTOR3 = Vector3_new()
local UP_VECTOR = Vector3_new(0, 1, 0)
local XZ_VECTOR = Vector3_new(1, 0, 1)
local TOUCH_SENSITIVTY = Vector2_new(math_pi*2.25, math_pi*2)
local MOUSE_SENSITIVITY = Vector2_new(math_pi*4, math_pi*1.9)
local THUMBSTICK_DEADZONE = 0.2
local DEADZONE = 0.1
local ZOOM_FACTOR = 0.25
local humanoid
local function findPlayerHumanoid(player)
local character = player and player.Character
if character then
if not (humanoid and humanoid.Parent == character) then
humanoid = character:FindFirstChildOfClass("Humanoid")
end
end
return humanoid
end
local function clamp(low, high, num)
return (num > high and high or num < low and low or num)
end
local function findAngleBetweenXZVectors(vec2, vec1)
return math_atan2(vec1.X*vec2.Z-vec1.Z*vec2.X, vec1.X*vec2.X + vec1.Z*vec2.Z)
end
local function IsFinite(num)
return num == num and num ~= 1/0 and num ~= -1/0
end
local function SCurveTranform(t)
t = clamp(-1,1,t)
if t >= 0 then
return (.35*t) / (.35 - t + 1)
end
return -((.8*-t) / (.8 + t + 1))
end
local function toSCurveSpace(t)
return (1 + DEADZONE) * (2*math.abs(t) - 1) - DEADZONE
end
local function fromSCurveSpace(t)
return t/2 + 0.5
end
local function gamepadLinearToCurve(thumbstickPosition)
local function onAxis(axisValue)
local sign = 1
if axisValue < 0 then
sign = -1
end
local point = fromSCurveSpace(SCurveTranform(toSCurveSpace(math_abs(axisValue))))
point = point * sign
return clamp(-1, 1, point)
end
return Vector2_new(onAxis(thumbstickPosition.x), onAxis(thumbstickPosition.y))
end
-- Reset the camera look vector when the camera is enabled for the first time
local SetCameraOnSpawn = true
local this = {}
local isFirstPerson = false
local isRightMouseDown = false
local isMiddleMouseDown = false
this.Enabled = false
this.RotateInput = ZERO_VECTOR2
this.DefaultZoom = 10
this.activeGamepad = nil
this.PartSubjectHack = nil
function this:GetHumanoid()
local player = Players.LocalPlayer
return findPlayerHumanoid(player)
end
function this:GetHumanoidRootPart()
local humanoid = this:GetHumanoid()
return humanoid and humanoid.Torso
end
function this:GetSubjectPosition()
local camera = workspace.CurrentCamera
local result = camera.Focus.p
local cameraSubject = camera and camera.CameraSubject
if cameraSubject then
if cameraSubject:IsA("Humanoid") then
local char = cameraSubject.Parent
if char then
local head = char:FindFirstChild("Head")
if head and head:IsA("BasePart") then
result = head.Position
end
end
if this.PartSubjectHack then
this:ZoomCamera(this.PartSubjectHack)
this.PartSubjectHack = nil
this:UpdateMouseBehavior()
end
elseif cameraSubject:IsA("BasePart") then
result = cameraSubject.Position
if not this.PartSubjectHack then
this.PartSubjectHack = this:GetCameraZoom()
this:ZoomCamera(10)
this:UpdateMouseBehavior()
end
end
end
return result
end
function this:GetCameraLook()
return workspace.CurrentCamera and workspace.CurrentCamera.CFrame.lookVector or Vector3.new(0,0,1)
end
function this:GetCameraZoom()
if this.currentZoom == nil then
local player = Players.LocalPlayer
this.currentZoom = player and clamp(player.CameraMinZoomDistance, player.CameraMaxZoomDistance, this.DefaultZoom) or this.DefaultZoom
end
return this.currentZoom
end
function this:GetCameraActualZoom()
local camera = workspace.CurrentCamera
if camera then
return (camera.CFrame.p - camera.Focus.p).Magnitude
end
end
function this:ViewSizeX()
local result = 1024
local camera = workspace.CurrentCamera
if camera then
result = camera.ViewportSize.X
end
return result
end
function this:ViewSizeY()
local result = 768
local camera = workspace.CurrentCamera
if camera then
result = camera.ViewportSize.Y
end
return result
end
function this:ScreenTranslationToAngle(translationVector)
local screenX = this:ViewSizeX()
local screenY = this:ViewSizeY()
local xTheta = (translationVector.x / screenX)
local yTheta = (translationVector.y / screenY)
return Vector2_new(xTheta, yTheta)
end
function this:MouseTranslationToAngle(translationVector)
local xTheta = (translationVector.x / 1920)
local yTheta = (translationVector.y / 1200)
return Vector2_new(xTheta, yTheta)
end
function this:RotateVector(startVector, xyRotateVector)
local startCFrame = CFrame_new(ZERO_VECTOR3, startVector)
local resultLookVector = (CFrame_Angles(0, -xyRotateVector.x, 0) * startCFrame * CFrame_Angles(-xyRotateVector.y,0,0)).lookVector
return resultLookVector, Vector2_new(xyRotateVector.x, xyRotateVector.y)
end
function this:RotateCamera(startLook, xyRotateVector)
local startVertical = math_asin(startLook.y)
local yTheta = clamp(-MAX_Y + startVertical, -MIN_Y + startVertical, xyRotateVector.y)
return self:RotateVector(startLook, Vector2_new(xyRotateVector.x, yTheta))
end
function this:IsInFirstPerson()
return isFirstPerson
end
function this:UpdateMouseBehavior()
if isFirstPerson or this.PartSubjectHack then
GameSettings.RotationType = Enum.RotationType.CameraRelative
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
else
GameSettings.RotationType = Enum.RotationType.MovementRelative
if isRightMouseDown or isMiddleMouseDown then
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
else
UserInputService.MouseBehavior = Enum.MouseBehavior.Default
end
end
end
function this:PlayTick()
local now = tick()
local lastTickSound = this.LastTickSound
if not lastTickSound then
lastTickSound = 0
end
if (now - lastTickSound) > .03 then
local s = Instance.new("Sound")
s.SoundId = "rbxasset://sounds/switch3.wav"
s.Parent = script
s:Play()
Debris:AddItem(s,1)
this.LastTickSound = now
end
end
function this:ZoomCamera(desiredZoom)
this.currentZoom = clamp(0.25, 400, desiredZoom)
isFirstPerson = self:GetCameraZoom() < 1.5
-- set mouse behavior
self:UpdateMouseBehavior()
return self:GetCameraZoom()
end
function this:ZoomCameraBy(input)
if TeleportService:GetTeleportSetting("FPSCapTo30") then
input = input * 1.5
end
local zoom = this:GetCameraActualZoom()
if zoom then
if input > 0 then
zoom = math.max( 1, zoom / (1 + ZOOM_FACTOR*input))
elseif input < 0 then
zoom = math.min(5000, zoom * (1 - ZOOM_FACTOR*input))
end
self:ZoomCamera(zoom)
end
self:PlayTick()
return self:GetCameraZoom()
end
function this:ZoomCameraFixedBy(zoomIncrement)
return self:ZoomCamera(self:GetCameraZoom() + zoomIncrement)
end
------------------------
---- Input Events ----
------------------------
do
local startPos = nil
local lastPos = nil
local panBeginLook = nil
local lastTapTime = nil
local fingerTouches = {}
local NumUnsunkTouches = 0
local inputStartPositions = {}
local inputStartTimes = {}
local StartingDiff = nil
local pinchBeginZoom = nil
local dynamicThumbstickFrame = nil
local flaggedDynamic = {}
local function getDynamicThumbstickFrame()
if dynamicThumbstickFrame and dynamicThumbstickFrame:IsDescendantOf(game) then
return dynamicThumbstickFrame
else
local touchGui = PlayerGui:FindFirstChild("TouchGui")
if not touchGui then return nil end
local touchControlFrame = touchGui:FindFirstChild("TouchControlFrame")
if not touchControlFrame then return nil end
dynamicThumbstickFrame = touchControlFrame:FindFirstChild("DynamicThumbstickFrame")
return dynamicThumbstickFrame
end
end
this.ZoomEnabled = true
this.PanEnabled = true
this.KeyPanEnabled = true
local function inputIsDynamic(input)
if flaggedDynamic[input] ~= nil then
return flaggedDynamic[input]
end
if GameSettings.TouchMovementMode ~= Enum.TouchMovementMode.DynamicThumbstick then
return false
end
local df = getDynamicThumbstickFrame()
if not df then return end
local pos = input.Position
local p0 = df.AbsolutePosition
local p1 = p0 + df.AbsoluteSize
if p0.X <= pos.X and p0.Y <= pos.Y then
if pos.X <= p1.X and pos.Y <= p1.Y then
flaggedDynamic[input] = true
return true
end
end
flaggedDynamic[input] = false
return false
end
local function OnTouchBegan(input, processed)
if not inputIsDynamic(input) then
fingerTouches[input] = processed
if not processed then
inputStartPositions[input] = input.Position
inputStartTimes[input] = tick()
NumUnsunkTouches = NumUnsunkTouches + 1
end
end
end
local function OnTouchChanged(input, processed)
if inputIsDynamic(input) then
return
end
if fingerTouches[input] == nil then
fingerTouches[input] = processed
if not processed then
NumUnsunkTouches = NumUnsunkTouches + 1
end
end
if NumUnsunkTouches == 1 then
if fingerTouches[input] == false then
panBeginLook = panBeginLook or this:GetCameraLook()
startPos = startPos or input.Position
lastPos = lastPos or startPos
this.UserPanningTheCamera = true
local delta = input.Position - lastPos
delta = Vector2.new(delta.X, delta.Y * GameSettings:GetCameraYInvertValue())
if this.PanEnabled then
local desiredXYVector = this:ScreenTranslationToAngle(delta) * TOUCH_SENSITIVTY
this.RotateInput = this.RotateInput + desiredXYVector
end
lastPos = input.Position
end
else
panBeginLook = nil
startPos = nil
lastPos = nil
this.UserPanningTheCamera = false
end
if NumUnsunkTouches == 2 then
local unsunkTouches = {}
for touch, wasSunk in pairs(fingerTouches) do
if not wasSunk then
table.insert(unsunkTouches, touch)
end
end
if #unsunkTouches == 2 then
local difference = (unsunkTouches[1].Position - unsunkTouches[2].Position).magnitude
if StartingDiff and pinchBeginZoom then
local scale = difference / math_max(0.01, StartingDiff)
local clampedScale = clamp(0.1, 10, scale)
if this.ZoomEnabled then
this:ZoomCamera(pinchBeginZoom / clampedScale)
this:PlayTick()
end
else
StartingDiff = difference
pinchBeginZoom = this:GetCameraActualZoom()
end
end
else
StartingDiff = nil
pinchBeginZoom = nil
end
end
local function calcLookBehindRotateInput(torso)
if torso then
local newDesiredLook = (torso.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
local horizontalShift = findAngleBetweenXZVectors(newDesiredLook, this:GetCameraLook())
local vertShift = math_asin(this:GetCameraLook().y) - math_asin(newDesiredLook.y)
if not IsFinite(horizontalShift) then
horizontalShift = 0
end
if not IsFinite(vertShift) then
vertShift = 0
end
return Vector2.new(horizontalShift, vertShift)
end
return nil
end
local function OnTouchEnded(input, processed)
if fingerTouches[input] == false then
if NumUnsunkTouches == 1 then
panBeginLook = nil
startPos = nil
lastPos = nil
elseif NumUnsunkTouches == 2 then
StartingDiff = nil
pinchBeginZoom = nil
end
end
if fingerTouches[input] ~= nil and fingerTouches[input] == false then
NumUnsunkTouches = NumUnsunkTouches - 1
end
fingerTouches[input] = nil
inputStartPositions[input] = nil
inputStartTimes[input] = nil
flaggedDynamic[input] = nil
end
local function OnMousePanButtonPressed(input, processed)
if processed then return end
this:UpdateMouseBehavior()
panBeginLook = panBeginLook or this:GetCameraLook()
startPos = startPos or input.Position
lastPos = lastPos or startPos
this.UserPanningTheCamera = true
end
local function OnMousePanButtonReleased(input, processed)
this:UpdateMouseBehavior()
if not (isRightMouseDown or isMiddleMouseDown) then
panBeginLook = nil
startPos = nil
lastPos = nil
this.UserPanningTheCamera = false
end
end
local function OnMouse2Down(input, processed)
if processed then return end
isRightMouseDown = true
OnMousePanButtonPressed(input, processed)
end
local function OnMouse2Up(input, processed)
isRightMouseDown = false
OnMousePanButtonReleased(input, processed)
end
local function OnMouse3Down(input, processed)
if processed then return end
isMiddleMouseDown = true
OnMousePanButtonPressed(input, processed)
end
local function OnMouse3Up(input, processed)
isMiddleMouseDown = false
OnMousePanButtonReleased(input, processed)
end
local function OnMouseMoved(input, processed)
local inputDelta = input.Delta
inputDelta = Vector2.new(inputDelta.X, inputDelta.Y * GameSettings:GetCameraYInvertValue())
if startPos and lastPos and panBeginLook then
local currPos = lastPos + input.Delta
local totalTrans = currPos - startPos
if this.PanEnabled then
local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
this.RotateInput = this.RotateInput + desiredXYVector
end
lastPos = currPos
elseif (this:IsInFirstPerson() or this.PartSubjectHack) and this.PanEnabled then
local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
this.RotateInput = this.RotateInput + desiredXYVector
end
end
local function OnMouseWheel(input, processed)
if not processed then
if this.ZoomEnabled then
this:ZoomCameraBy(clamp(-1, 1, input.Position.Z))
end
end
end
local function round(num)
return math_floor(num + 0.5)
end
local eight2Pi = math_pi / 4
local function rotateVectorByAngleAndRound(camLook, rotateAngle, roundAmount)
if camLook ~= ZERO_VECTOR3 then
camLook = camLook.unit
local currAngle = math_atan2(camLook.z, camLook.x)
local newAngle = round((math_atan2(camLook.z, camLook.x) + rotateAngle) / roundAmount) * roundAmount
return newAngle - currAngle
end
return 0
end
local function OnKeyDown(input, processed)
if processed then return end
if this.ZoomEnabled then
if input.KeyCode == Enum.KeyCode.I then
this:ZoomCameraBy(1)
elseif input.KeyCode == Enum.KeyCode.O then
this:ZoomCameraBy(-1)
end
end
if panBeginLook == nil and this.KeyPanEnabled then
if input.KeyCode == Enum.KeyCode.Left then
this.TurningLeft = true
elseif input.KeyCode == Enum.KeyCode.Right then
this.TurningRight = true
elseif input.KeyCode == Enum.KeyCode.Comma then
local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), -eight2Pi * (3/4), eight2Pi)
if angle ~= 0 then
this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
this.LastUserPanCamera = tick()
this.LastCameraTransform = nil
end
this:PlayTick()
elseif input.KeyCode == Enum.KeyCode.Period then
local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), eight2Pi * (3/4), eight2Pi)
if angle ~= 0 then
this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
this.LastUserPanCamera = tick()
this.LastCameraTransform = nil
end
this:PlayTick()
elseif input.KeyCode == Enum.KeyCode.PageUp then
this.RotateInput = this.RotateInput + Vector2.new(0,math_pi/12)
this.LastCameraTransform = nil
this:PlayTick()
elseif input.KeyCode == Enum.KeyCode.PageDown then
this.RotateInput = this.RotateInput + Vector2.new(0,-math_pi/12)
this.LastCameraTransform = nil
this:PlayTick()
end
end
end
local function OnKeyUp(input, processed)
if input.KeyCode == Enum.KeyCode.Left then
this.TurningLeft = false
elseif input.KeyCode == Enum.KeyCode.Right then
this.TurningRight = false
end
end
local lastThumbstickRotate = nil
local numOfSeconds = 0.7
local currentSpeed = 0
local maxSpeed = 6
local lastThumbstickPos = Vector2.new(0,0)
local ySensitivity = 0.65
local lastVelocity = nil
function this:UpdateGamepad()
local gamepadPan = this.GamepadPanningCamera
if gamepadPan then
gamepadPan = gamepadLinearToCurve(gamepadPan)
local currentTime = tick()
if gamepadPan.X ~= 0 or gamepadPan.Y ~= 0 then
this.userPanningTheCamera = true
elseif gamepadPan == ZERO_VECTOR2 then
lastThumbstickRotate = nil
if lastThumbstickPos == ZERO_VECTOR2 then
currentSpeed = 0
end
end
local finalConstant = 0
if lastThumbstickRotate then
local elapsed = (currentTime - lastThumbstickRotate) * 10
currentSpeed = currentSpeed + (maxSpeed * ((elapsed*elapsed)/numOfSeconds))
if currentSpeed > maxSpeed then currentSpeed = maxSpeed end
if lastVelocity then
local velocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
local velocityDeltaMag = (velocity - lastVelocity).magnitude
if velocityDeltaMag > 12 then
currentSpeed = currentSpeed * (20/velocityDeltaMag)
if currentSpeed > maxSpeed then currentSpeed = maxSpeed end
end
end
local gamepadCameraSensitivity = GameSettings.GamepadCameraSensitivity
finalConstant = (gamepadCameraSensitivity * currentSpeed)
lastVelocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
end
lastThumbstickPos = gamepadPan
lastThumbstickRotate = currentTime
return Vector2_new( gamepadPan.X * finalConstant, gamepadPan.Y * finalConstant * ySensitivity * GameSettings:GetCameraYInvertValue())
end
return ZERO_VECTOR2
end
local InputEvents = {}
function this:DisconnectInputEvents()
-- Disconnect all input events.
while true do
local signalName = next(InputEvents)
if signalName then
InputEvents[signalName]:Disconnect()
InputEvents[signalName] = nil
else
break
end
end
this.TurningLeft = false
this.TurningRight = false
this.LastCameraTransform = nil
this.UserPanningTheCamera = false
this.RotateInput = ZERO_VECTOR2
this.GamepadPanningCamera = ZERO_VECTOR2
-- Reset input states
startPos = nil
lastPos = nil
panBeginLook = nil
isRightMouseDown = false
isMiddleMouseDown = false
fingerTouches = {}
NumUnsunkTouches = 0
StartingDiff = nil
pinchBeginZoom = nil
-- Unlock mouse for example if right mouse button was being held down
if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
UserInputService.MouseBehavior = Enum.MouseBehavior.Default
end
end
local function resetInputStates()
isRightMouseDown = false
isMiddleMouseDown = false
OnMousePanButtonReleased() -- this function doesn't seem to actually need parameters
if UserInputService.TouchEnabled then
--[[menu opening was causing serious touch issues
this should disable all active touch events if
they're active when menu opens.]]
for inputObject, value in pairs(fingerTouches) do
fingerTouches[inputObject] = nil
end
panBeginLook = nil
startPos = nil
lastPos = nil
this.UserPanningTheCamera = false
StartingDiff = nil
pinchBeginZoom = nil
NumUnsunkTouches = 0
end
end
local function getGamepadPan(name, state, input)
if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then
if state == Enum.UserInputState.Cancel then
this.GamepadPanningCamera = ZERO_VECTOR2
return
end
local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
if inputVector.magnitude > THUMBSTICK_DEADZONE then
this.GamepadPanningCamera = Vector2_new(input.Position.X, -input.Position.Y)
else
this.GamepadPanningCamera = ZERO_VECTOR2
end
end
end
local function doGamepadZoom(name, state, input)
if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.ButtonR3 and state == Enum.UserInputState.Begin then
if this.ZoomEnabled then
if this:GetCameraZoom() > 0.5 then
this:ZoomCamera(0)
else
this:ZoomCamera(10)
end
end
end
end
local function assignActivateGamepad()
local connectedGamepads = UserInputService:GetConnectedGamepads()
if #connectedGamepads > 0 then
for i = 1, #connectedGamepads do
if this.activeGamepad == nil then
this.activeGamepad = connectedGamepads[i]
elseif connectedGamepads[i].Value < this.activeGamepad.Value then
this.activeGamepad = connectedGamepads[i]
end
end
end
if this.activeGamepad == nil then -- nothing is connected, at least set up for gamepad1
this.activeGamepad = Enum.UserInputType.Gamepad1
end
end
local function onInputBegan(input, processed)
if input.UserInputType == Enum.UserInputType.Touch then
OnTouchBegan(input, processed)
elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
OnMouse2Down(input, processed)
elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
OnMouse3Down(input, processed)
end
-- Keyboard
if input.UserInputType == Enum.UserInputType.Keyboard then
OnKeyDown(input, processed)
end
end
local function onInputChanged(input, processed)
if input.UserInputType == Enum.UserInputType.Touch then
OnTouchChanged(input, processed)
elseif input.UserInputType == Enum.UserInputType.MouseMovement then
OnMouseMoved(input, processed)
elseif input.UserInputType == Enum.UserInputType.MouseWheel then
OnMouseWheel(input, processed)
end
end
local function onInputEnded(input, processed)
if input.UserInputType == Enum.UserInputType.Touch then
OnTouchEnded(input, processed)
elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
OnMouse2Up(input, processed)
elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
OnMouse3Up(input, processed)
end
-- Keyboard
if input.UserInputType == Enum.UserInputType.Keyboard then
OnKeyUp(input, processed)
end
end
local inputPassCmds =
{
ZoomIn = Enum.KeyCode.I;
ZoomOut = Enum.KeyCode.O;
RotateUp = Enum.KeyCode.PageUp;
RotateDown = Enum.KeyCode.PageDown;
}
local function onInputPassed(command)
local passKey = inputPassCmds[command]
if passKey then
OnKeyDown({KeyCode = passKey}, false)
end
end
local function onGamepadConnected(gamepadEnum)
if this.activeGamepad == nil then
assignActivateGamepad()
end
end
local function onGamepadDisconnected(gamepadEnum)
if this.activeGamepad ~= gamepadEnum then return end
this.activeGamepad = nil
assignActivateGamepad()
end
function this:ConnectInputEvents()
local player = Players.LocalPlayer
local playerScripts = player:WaitForChild("PlayerScripts")
local passCameraEvent = playerScripts:WaitForChild("PassCameraEvent")
this.RotateInput = ZERO_VECTOR2
this.activeGamepad = nil
InputEvents =
{
InputBegan = UserInputService.InputBegan:Connect(onInputBegan);
InputChanged = UserInputService.InputChanged:Connect(onInputChanged);
InputEnded = UserInputService.InputEnded:Connect(onInputEnded);
MenuOpened = GuiService.MenuOpened:Connect(resetInputStates);
MenuOpenedConn = GuiService.MenuOpened:Connect(resetInputStates);
GamepadConnected = UserInputService.GamepadConnected:Connect(onGamepadConnected);
GamepadDisconnected = UserInputService.GamepadDisconnected:Connect(onGamepadDisconnected);
InputPassed = passCameraEvent.Event:Connect(onInputPassed);
}
ContextActionService:BindAction("RootCamGamepadPan", getGamepadPan, false, Enum.KeyCode.Thumbstick2)
ContextActionService:BindAction("RootCamGamepadZoom", doGamepadZoom, false, Enum.KeyCode.ButtonR3)
assignActivateGamepad()
-- set mouse behavior
self:UpdateMouseBehavior()
end
function this:SetEnabled(newState)
if newState ~= self.Enabled then
self.Enabled = newState
if self.Enabled then
self:ConnectInputEvents()
else
self:DisconnectInputEvents()
end
end
end
local function OnPlayerAdded(player)
player.Changed:Connect(function (prop)
if this.Enabled then
if prop == "CameraMode" or prop == "CameraMaxZoomDistance" or prop == "CameraMinZoomDistance" then
this:ZoomCameraFixedBy(0)
end
end
end)
local function OnCharacterAdded(newCharacter)
local humanoid = findPlayerHumanoid(player)
local start = tick()
while tick() - start < 0.3 and (humanoid == nil or humanoid.Torso == nil) do
wait()
humanoid = findPlayerHumanoid(player)
end
if humanoid and humanoid.Torso and player.Character == newCharacter then
local newDesiredLook = (humanoid.Torso.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
local horizontalShift = findAngleBetweenXZVectors(newDesiredLook, this:GetCameraLook())
local vertShift = math_asin(this:GetCameraLook().y) - math_asin(newDesiredLook.y)
if not IsFinite(horizontalShift) then
horizontalShift = 0
end
if not IsFinite(vertShift) then
vertShift = 0
end
this.RotateInput = Vector2.new(horizontalShift, vertShift)
-- reset old camera info so follow cam doesn't rotate us
this.LastCameraTransform = nil
end
-- Need to wait for camera cframe to update before we zoom in
-- Not waiting will force camera to original cframe
wait()
this:ZoomCamera(this.DefaultZoom)
end
player.CharacterAdded:Connect(function (character)
if this.Enabled or SetCameraOnSpawn then
OnCharacterAdded(character)
SetCameraOnSpawn = false
end
end)
if player.Character then
spawn(function () OnCharacterAdded(player.Character) end)
end
end
if Players.LocalPlayer then
OnPlayerAdded(Players.LocalPlayer)
end
Players.ChildAdded:Connect(function (child)
if child and Players.LocalPlayer == child then
OnPlayerAdded(Players.LocalPlayer)
end
end)
end
------------------------
---- Main Updater ----
------------------------
do
local tweenAcceleration = math_rad(220)
local tweenSpeed = math_rad(0)
local tweenMaxSpeed = math_rad(250)
local timeBeforeAutoRotate = 2
local lastUpdate = tick()
this.LastUserPanCamera = lastUpdate
function this:Update()
local now = tick()
local timeDelta = (now - lastUpdate)
local userPanningTheCamera = (self.UserPanningTheCamera == true)
local camera = workspace.CurrentCamera
local humanoid = self:GetHumanoid()
local cameraSubject = camera and camera.CameraSubject
local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')
local isOnASkateboard = cameraSubject and cameraSubject:IsA('SkateboardPlatform')
if isInVehicle and cameraSubject.Occupant == humanoid then
cameraSubject = humanoid
camera.CameraSubject = humanoid
isInVehicle = false
end
if lastUpdate == nil or (now - lastUpdate) > 1 then
self.LastCameraTransform = nil
end
if lastUpdate then
local gamepadRotation = self:UpdateGamepad()
-- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
local delta = math_min(0.1, now - lastUpdate)
if gamepadRotation ~= ZERO_VECTOR2 then
userPanningTheCamera = true
self.RotateInput = self.RotateInput + (gamepadRotation * delta)
end
local angle = 0
if not (isInVehicle or isOnASkateboard) then
angle = angle + (self.TurningLeft and -120 or 0)
angle = angle + (self.TurningRight and 120 or 0)
end
if angle ~= 0 then
self.RotateInput = self.RotateInput + Vector2.new(math_rad(angle * delta), 0)
userPanningTheCamera = true
end
end
-- Reset tween speed if user is panning
if userPanningTheCamera then
tweenSpeed = 0
self.LastUserPanCamera = now
end
local userRecentlyPannedCamera = now - self.LastUserPanCamera < timeBeforeAutoRotate
local subjectPosition = self:GetSubjectPosition()
if subjectPosition and camera then
local zoom = self:GetCameraZoom()
if zoom < 0.25 then
zoom = 0.25
end
if TeleportService:GetTeleportSetting("FollowCamera") then
if self.LastCameraTransform and not self:IsInFirstPerson() then
local lastVec = -(self.LastCameraTransform.p - subjectPosition)
local y = findAngleBetweenXZVectors(lastVec, self:GetCameraLook())
-- Check for NaNs
if IsFinite(y) and math.abs(y) > 0.0001 then
self.RotateInput = self.RotateInput + Vector2.new(y, 0)
end
end
end
camera.Focus = CFrame_new(subjectPosition)
local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput)
self.RotateInput = ZERO_VECTOR2
if self.LastZoom ~= zoom then
self.LastZoom = zoom
if camera.CameraSubject and camera.CameraSubject:IsA("Humanoid") then
-- Flatten the lookVector
newLookVector = (newLookVector * XZ_VECTOR).Unit
-- Apply upwards tilt
local upY = -math_min(6, zoom/40)
newLookVector = (newLookVector + (UP_VECTOR * upY)).Unit
end
end
local newCF = CFrame_new(subjectPosition - (zoom * newLookVector), subjectPosition)
camera.CFrame = camera.CFrame:Lerp(newCF,.8)
self.LastCameraTransform = camera.CFrame
end
lastUpdate = now
end
GameSettings:SetCameraYInvertVisible()
GameSettings:SetGamepadCameraSensitivityVisible()
end
return this