SyntaxGameServer/RCCService2020/ExtraContent/scripts/PlayerScripts/StarterPlayerScripts_NewStr.../PlayerModule.module/CameraModule/CameraInput.lua

494 lines
13 KiB
Lua

local FFlagUserCameraInputRefactor do
local success, result = pcall(function()
return UserSettings():IsUserFeatureEnabled("UserCameraInputRefactor2")
end)
FFlagUserCameraInputRefactor = success and result
end
local ContextActionService = game:GetService("ContextActionService")
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local UserGameSettings = UserSettings():GetService("UserGameSettings")
local player = Players.LocalPlayer
local CAMERA_INPUT_PRIORITY = Enum.ContextActionPriority.Default.Value
local MB_TAP_LENGTH = 0.3 -- (s) length of time for a short mouse button tap to be registered
local ROTATION_SPEED_KEYS = math.rad(2) -- (rad/s)
local ROTATION_SPEED_MOUSE = Vector2.new(1, 0.77)*math.rad(0.5) -- (rad/s)
local ROTATION_SPEED_TOUCH = Vector2.new(1, 0.66)*math.rad(1) -- (rad/s)
local ROTATION_SPEED_GAMEPAD = Vector2.new(1, 0.77)*math.rad(4) -- (rad/s)
local ZOOM_SPEED_MOUSE = 1 -- (scaled studs/wheel click)
local ZOOM_SPEED_KEYS = 0.1 -- (studs/s)
local ZOOM_SPEED_TOUCH = 0.04 -- (scaled studs/DIP %)
local MIN_TOUCH_SENSITIVITY_FRACTION = 0.25 -- 25% sensitivity at 90°
-- right mouse button up & down events
local rmbDown, rmbUp do
local rmbDownBindable = Instance.new("BindableEvent")
local rmbUpBindable = Instance.new("BindableEvent")
rmbDown = rmbDownBindable.Event
rmbUp = rmbUpBindable.Event
UserInputService.InputBegan:Connect(function(input, gpe)
if not gpe and input.UserInputType == Enum.UserInputType.MouseButton2 then
rmbDownBindable:Fire()
end
end)
UserInputService.InputEnded:Connect(function(input, gpe)
if input.UserInputType == Enum.UserInputType.MouseButton2 then
rmbUpBindable:Fire()
end
end)
end
local thumbstickCurve do
local K_CURVATURE = 2 -- amount of upwards curvature (0 is flat)
local K_DEADZONE = 0.1 -- deadzone
function thumbstickCurve(x)
-- remove sign, apply linear deadzone
local fDeadzone = (math.abs(x) - K_DEADZONE)/(1 - K_DEADZONE)
-- apply exponential curve and scale to fit in [0, 1]
local fCurve = (math.exp(K_CURVATURE*fDeadzone) - 1)/(math.exp(K_CURVATURE) - 1)
-- reapply sign and clamp
return math.sign(x)*math.clamp(fCurve, 0, 1)
end
end
-- Adjust the touch sensitivity so that sensitivity is reduced when swiping up
-- or down, but stays the same when swiping towards the middle of the screen
local function adjustTouchPitchSensitivity(delta)
local camera = workspace.CurrentCamera
if not camera then
return delta
end
-- get the camera pitch in world space
local pitch = camera.CFrame:ToEulerAnglesYXZ()
if delta.Y*pitch >= 0 then
-- do not reduce sensitivity when pitching towards the horizon
return delta
end
-- set up a line to fit:
-- 1 = f(0)
-- 0 = f(±pi/2)
local curveY = 1 - (2*math.abs(pitch)/math.pi)^0.75
-- remap curveY from [0, 1] -> [MIN_TOUCH_SENSITIVITY_FRACTION, 1]
local sensitivity = curveY*(1 - MIN_TOUCH_SENSITIVITY_FRACTION) + MIN_TOUCH_SENSITIVITY_FRACTION
return Vector2.new(1, sensitivity)*delta
end
local function isInDynamicThumbstickArea(pos)
local playerGui = player:FindFirstChildOfClass("PlayerGui")
local touchGui = playerGui and playerGui:FindFirstChild("TouchGui")
local touchFrame = touchGui and touchGui:FindFirstChild("TouchControlFrame")
local thumbstickFrame = touchFrame and touchFrame:FindFirstChild("DynamicThumbstickFrame")
if not thumbstickFrame then
return false
end
if not touchGui.Enabled then
return false
end
local posTopLeft = thumbstickFrame.AbsolutePosition
local posBottomRight = posTopLeft + thumbstickFrame.AbsoluteSize
return
pos.X >= posTopLeft.X and
pos.Y >= posTopLeft.Y and
pos.X <= posBottomRight.X and
pos.Y <= posBottomRight.Y
end
local CameraInput = {}
do
local connectionList = {}
local touchPitchSensitivity = 1
local gamepadState = {
Thumbstick2 = Vector2.new(),
}
local keyboardState = {
Left = 0,
Right = 0,
I = 0,
O = 0
}
local mouseState = {
Movement = Vector2.new(),
Wheel = 0, -- PointerAction
Pan = Vector2.new(), -- PointerAction
Pinch = 0, -- PointerAction
MouseButton2 = 0,
}
local touchState = {
Move = Vector2.new(),
Pinch = 0,
}
local gamepadZoomPressBindable = Instance.new("BindableEvent")
CameraInput.gamepadZoomPress = gamepadZoomPressBindable.Event
function CameraInput.getPanning()
for _, input in pairs(UserInputService:GetMouseButtonsPressed()) do
if input.UserInputType == Enum.UserInputType.MouseButton2 then
return true
end
end
return false
end
function CameraInput.getRotation()
local kKeyboard = Vector2.new(keyboardState.Right - keyboardState.Left, 0)
local kGamepad = gamepadState.Thumbstick2
local kMouse = mouseState.Movement + mouseState.Pan
local kTouch = adjustTouchPitchSensitivity(touchState.Move)
local result =
kKeyboard*ROTATION_SPEED_KEYS +
kGamepad*ROTATION_SPEED_GAMEPAD +
kMouse*ROTATION_SPEED_MOUSE +
kTouch*ROTATION_SPEED_TOUCH
return result
end
function CameraInput.getZoomDelta()
local kKeyboard = keyboardState.O - keyboardState.I
local kMouse = -mouseState.Wheel + mouseState.Pinch
local kTouch = -touchState.Pinch
return kKeyboard*ZOOM_SPEED_KEYS + kMouse*ZOOM_SPEED_MOUSE + kTouch*ZOOM_SPEED_TOUCH
end
do
local function thumbstick(action, state, input)
local position = input.Position
gamepadState[input.KeyCode.Name] = Vector2.new(thumbstickCurve(position.X), -thumbstickCurve(position.Y))
end
local function mouseMove(action, state, input)
local delta = input.Delta
mouseState.Movement = Vector2.new(delta.X, delta.Y)
return Enum.ContextActionResult.Pass
end
local function mouseWheel(action, state, input)
mouseState.Wheel = input.Position.Z
return Enum.ContextActionResult.Pass
end
local function keypress(action, state, input)
keyboardState[input.KeyCode.Name] = state == Enum.UserInputState.Begin and 1 or 0
end
local function gamepadZoomPress(action, state, input)
if state == Enum.UserInputState.Begin then
gamepadZoomPressBindable:Fire()
end
end
local function resetInputDevices()
for _, device in pairs({
gamepadState,
keyboardState,
mouseState,
touchState,
}) do
for k, v in pairs(device) do
if type(v) == "boolean" then
device[k] = false
else
device[k] *= 0 -- Mul by zero to preserve vector types
end
end
end
end
local touchBegan, touchChanged, touchEnded do
-- Use TouchPan & TouchPinch when they work in the Studio emulator
local touches = {} -- {[InputObject] = sunk}
local dynamicThumbstickInput -- Special-cased
local lastPinchDiameter
function touchBegan(input, sunk)
assert(input.UserInputType == Enum.UserInputType.Touch)
assert(input.UserInputState == Enum.UserInputState.Begin)
if dynamicThumbstickInput == nil and isInDynamicThumbstickArea(input.Position) and not sunk then
-- any finger down starting in the dynamic thumbstick area should always be
-- ignored for camera purposes. these must be handled specially from all other
-- inputs, as the DT does not sink inputs by itself
dynamicThumbstickInput = input
return
end
-- register the finger
touches[input] = sunk
end
function touchEnded(input, sunk)
assert(input.UserInputType == Enum.UserInputType.Touch)
assert(input.UserInputState == Enum.UserInputState.End)
-- reset the DT input
if input == dynamicThumbstickInput then
dynamicThumbstickInput = nil
end
-- reset pinch state if one unsunk finger lifts
if touches[input] == false then
lastPinchDiameter = nil
end
-- unregister input
touches[input] = nil
end
function touchChanged(input, sunk)
assert(input.UserInputType == Enum.UserInputType.Touch)
assert(input.UserInputState == Enum.UserInputState.Change)
-- ignore movement from the DT finger
if input == dynamicThumbstickInput then
return
end
-- fixup unknown touches
if touches[input] == nil then
touches[input] = sunk
end
-- collect unsunk touches
local unsunkTouches = {}
for touch, sunk in pairs(touches) do
if not sunk then
table.insert(unsunkTouches, touch)
end
end
-- 1 finger: pan
if #unsunkTouches == 1 then
if touches[input] == false then
local delta = input.Delta
touchState.Move = Vector2.new(delta.X, delta.Y)
end
end
-- 2 fingers: pinch
if #unsunkTouches == 2 then
local pinchDiameter = (unsunkTouches[1].Position - unsunkTouches[2].Position).Magnitude
if lastPinchDiameter then
touchState.Pinch = pinchDiameter - lastPinchDiameter
end
lastPinchDiameter = pinchDiameter
else
lastPinchDiameter = nil
end
end
end
local function pointerAction(wheel, pan, pinch, gpe)
if not gpe then
local inversionVector = Vector2.new(1, UserGameSettings:GetCameraYInvertValue())
mouseState.Wheel = wheel
mouseState.Pan = pan*inversionVector
mouseState.Pinch = -pinch
end
end
local function inputBegan(input, sunk)
if input.UserInputType == Enum.UserInputType.Touch then
touchBegan(input, sunk)
end
end
local function inputChanged(input, sunk)
if input.UserInputType == Enum.UserInputType.Touch then
touchChanged(input, sunk)
end
end
local function inputEnded(input, sunk)
if input.UserInputType == Enum.UserInputType.Touch then
touchEnded(input, sunk)
end
end
local inputEnabled = false
function CameraInput.setInputEnabled(_inputEnabled)
assert(FFlagUserCameraInputRefactor)
if inputEnabled == _inputEnabled then
return
end
inputEnabled = _inputEnabled
if inputEnabled then -- enable
resetInputDevices()
ContextActionService:BindActionAtPriority(
"RbxCameraThumbstick",
thumbstick,
false,
CAMERA_INPUT_PRIORITY,
Enum.KeyCode.Thumbstick2
)
ContextActionService:BindActionAtPriority(
"RbxCameraMouseMove",
mouseMove,
false,
CAMERA_INPUT_PRIORITY,
Enum.UserInputType.MouseMovement
)
ContextActionService:BindActionAtPriority(
"RbxCameraKeypress",
keypress,
false,
CAMERA_INPUT_PRIORITY,
Enum.KeyCode.Left,
Enum.KeyCode.Right,
Enum.KeyCode.I,
Enum.KeyCode.O
)
ContextActionService:BindAction(
"RbxCameraGamepadZoom",
gamepadZoomPress,
false,
Enum.KeyCode.ButtonR3
)
table.insert(connectionList, UserInputService.InputBegan:Connect(inputBegan))
table.insert(connectionList, UserInputService.InputChanged:Connect(inputChanged))
table.insert(connectionList, UserInputService.InputEnded:Connect(inputEnded))
table.insert(connectionList, UserInputService.PointerAction:Connect(pointerAction))
else -- disable
ContextActionService:UnbindAction("RbxCameraThumbstick")
ContextActionService:UnbindAction("RbxCameraMouseMove")
ContextActionService:UnbindAction("RbxCameraMouseWheel")
ContextActionService:UnbindAction("RbxCameraKeypress")
resetInputDevices()
for _, conn in pairs(connectionList) do
conn:Disconnect()
end
connectionList = {}
end
end
function CameraInput.getInputEnabled()
return inputEnabled
end
function CameraInput.resetInputForFrameEnd()
mouseState.Movement = Vector2.new()
touchState.Move = Vector2.new()
mouseState.Wheel = 0
touchState.Pinch = 0
end
UserInputService.WindowFocused:Connect(resetInputDevices)
UserInputService.WindowFocusReleased:Connect(resetInputDevices)
end
end
-- Toggle pan
do
local holdPan = false
local togglePan = false
local lastRmbDown = 0 -- tick() timestamp of the last right mouse button down event
function CameraInput.getHoldPan()
return holdPan
end
function CameraInput.getTogglePan()
return togglePan
end
function CameraInput.getPanning()
return togglePan or holdPan
end
function CameraInput.setTogglePan(value)
togglePan = value
end
local cameraToggleInputEnabled = false
local rmbDownConnection
local rmbUpConnection
function CameraInput.enableCameraToggleInput()
if cameraToggleInputEnabled then
return
end
cameraToggleInputEnabled = true
holdPan = false
togglePan = false
if rmbDownConnection then
rmbDownConnection:Disconnect()
end
if rmbUpConnection then
rmbUpConnection:Disconnect()
end
rmbDownConnection = rmbDown:Connect(function()
holdPan = true
lastRmbDown = tick()
end)
rmbUpConnection = rmbUp:Connect(function()
holdPan = false
if tick() - lastRmbDown < MB_TAP_LENGTH and (togglePan or UserInputService:GetMouseDelta().Magnitude < 2) then
togglePan = not togglePan
end
end)
end
function CameraInput.disableCameraToggleInput()
if not cameraToggleInputEnabled then
return
end
cameraToggleInputEnabled = false
if rmbDownConnection then
rmbDownConnection:Disconnect()
rmbDownConnection = nil
end
if rmbUpConnection then
rmbUpConnection:Disconnect()
rmbUpConnection = nil
end
end
end
return CameraInput