853 lines
30 KiB
Plaintext
853 lines
30 KiB
Plaintext
--rbxassetid%859%
|
|
-- This is responsible for all touch controls we show (as of this writing, only on iOS)
|
|
-- this includes character move thumbsticks, and buttons for jump, use of items, camera, etc.
|
|
-- Written by Ben Tkacheff, Copyright Roblox 2013
|
|
|
|
-- obligatory stuff to make sure we don't access nil data
|
|
while not Game do
|
|
wait()
|
|
end
|
|
while not Game:FindFirstChild("Players") do
|
|
wait()
|
|
end
|
|
while not Game.Players.LocalPlayer do
|
|
wait()
|
|
end
|
|
while not Game:FindFirstChild("CoreGui") do
|
|
wait()
|
|
end
|
|
while not Game.CoreGui:FindFirstChild("RobloxGui") do
|
|
wait()
|
|
end
|
|
|
|
local userInputService = Game:GetService("UserInputService")
|
|
local success = pcall(function() userInputService:IsLuaTouchControls() end)
|
|
if not success then
|
|
script:Destroy()
|
|
end
|
|
|
|
----------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------
|
|
-- Variables
|
|
local screenResolution = Game:GetService("GuiService"):GetScreenResolution()
|
|
function isSmallScreenDevice()
|
|
return screenResolution.y <= 500
|
|
end
|
|
|
|
local GameSettings = UserSettings().GameSettings
|
|
|
|
local localPlayer = Game.Players.LocalPlayer
|
|
|
|
local isInThumbstickMode = false
|
|
|
|
local thumbstickInactiveAlpha = 0.3
|
|
local thumbstickSize = 120
|
|
if isSmallScreenDevice() then
|
|
thumbstickSize = 70
|
|
end
|
|
|
|
local touchControlsSheet = "rbxasset://textures/ui/TouchControlsSheet.png"
|
|
local DPadSheet = "rbxasset://textures/ui/DPadSheet.png"
|
|
local ThumbstickDeadZone = 5
|
|
local ThumbstickMaxPercentGive = 0.92
|
|
local thumbstickTouches = {}
|
|
|
|
local jumpButtonSize = 90
|
|
if isSmallScreenDevice() then
|
|
jumpButtonSize = 70
|
|
end
|
|
local oldJumpTouches = {}
|
|
local currentJumpTouch = nil
|
|
|
|
local CameraRotateSensitivity = 0.007
|
|
local CameraRotateDeadZone = CameraRotateSensitivity * 16
|
|
local CameraZoomSensitivity = 0.03
|
|
local PinchZoomDelay = 0.2
|
|
local cameraTouch = nil
|
|
local dpadTouch = nil
|
|
|
|
|
|
-- make sure all of our images are good to go
|
|
Game:GetService("ContentProvider"):Preload(touchControlsSheet)
|
|
Game:GetService("ContentProvider"):Preload(DPadSheet)
|
|
|
|
----------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------
|
|
-- Functions
|
|
|
|
local isPointInRect = function(point, rectPos, rectSize)
|
|
if point.x >= rectPos.x and point.x <= (rectPos.x + rectSize.x) then
|
|
if point.y >= rectPos.y and point.y <= (rectPos.y + rectSize.y) then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
|
|
--
|
|
-- Thumbstick Control
|
|
--
|
|
|
|
function setCameraTouch(newTouch)
|
|
cameraTouch = newTouch
|
|
if newTouch == nil then
|
|
pcall(function() userInputService.InCameraGesture = false end)
|
|
else
|
|
pcall(function() userInputService.InCameraGesture = true end)
|
|
end
|
|
end
|
|
|
|
function DistanceBetweenTwoPoints(point1, point2)
|
|
local dx = point2.x - point1.x
|
|
local dy = point2.y - point1.y
|
|
return math.sqrt( (dx*dx) + (dy*dy) )
|
|
end
|
|
|
|
function transformFromCenterToTopLeft(pointToTranslate, guiObject)
|
|
return UDim2.new(0,pointToTranslate.x - guiObject.AbsoluteSize.x/2,0,pointToTranslate.y - guiObject.AbsoluteSize.y/2)
|
|
end
|
|
|
|
function rotatePointAboutLocation(pointToRotate, pointToRotateAbout, radians)
|
|
local sinAnglePercent = math.sin(radians)
|
|
local cosAnglePercent = math.cos(radians)
|
|
|
|
local transformedPoint = pointToRotate
|
|
|
|
-- translate point back to origin:
|
|
transformedPoint = Vector2.new(transformedPoint.x - pointToRotateAbout.x, transformedPoint.y - pointToRotateAbout.y)
|
|
|
|
-- rotate point
|
|
local xNew = transformedPoint.x * cosAnglePercent - transformedPoint.y * sinAnglePercent
|
|
local yNew = transformedPoint.x * sinAnglePercent + transformedPoint.y * cosAnglePercent
|
|
|
|
-- translate point back:
|
|
transformedPoint = Vector2.new(xNew + pointToRotateAbout.x, yNew + pointToRotateAbout.y)
|
|
|
|
return transformedPoint
|
|
end
|
|
|
|
function dotProduct(v1,v2)
|
|
return ((v1.x*v2.x) + (v1.y*v2.y))
|
|
end
|
|
|
|
function stationaryThumbstickTouchMove(thumbstickFrame, thumbstickOuter, touchLocation)
|
|
local thumbstickOuterCenterPosition = Vector2.new(thumbstickOuter.Position.X.Offset + thumbstickOuter.AbsoluteSize.x/2, thumbstickOuter.Position.Y.Offset + thumbstickOuter.AbsoluteSize.y/2)
|
|
local centerDiff = DistanceBetweenTwoPoints(touchLocation, thumbstickOuterCenterPosition)
|
|
|
|
-- thumbstick is moving outside our region, need to cap its distance
|
|
if centerDiff > (thumbstickSize/2) then
|
|
local thumbVector = Vector2.new(touchLocation.x - thumbstickOuterCenterPosition.x,touchLocation.y - thumbstickOuterCenterPosition.y);
|
|
local normal = thumbVector.unit
|
|
if normal.x == math.nan or normal.x == math.inf then
|
|
normal = Vector2.new(0,normal.y)
|
|
end
|
|
if normal.y == math.nan or normal.y == math.inf then
|
|
normal = Vector2.new(normal.x,0)
|
|
end
|
|
|
|
local newThumbstickInnerPosition = thumbstickOuterCenterPosition + (normal * (thumbstickSize/2))
|
|
thumbstickFrame.Position = transformFromCenterToTopLeft(newThumbstickInnerPosition, thumbstickFrame)
|
|
else
|
|
thumbstickFrame.Position = transformFromCenterToTopLeft(touchLocation,thumbstickFrame)
|
|
end
|
|
|
|
return Vector2.new(thumbstickFrame.Position.X.Offset - thumbstickOuter.Position.X.Offset,thumbstickFrame.Position.Y.Offset - thumbstickOuter.Position.Y.Offset)
|
|
end
|
|
|
|
function followThumbstickTouchMove(thumbstickFrame, thumbstickOuter, touchLocation)
|
|
local thumbstickOuterCenter = Vector2.new(thumbstickOuter.Position.X.Offset + thumbstickOuter.AbsoluteSize.x/2, thumbstickOuter.Position.Y.Offset + thumbstickOuter.AbsoluteSize.y/2)
|
|
|
|
-- thumbstick is moving outside our region, need to position outer thumbstick texture carefully (to make look and feel like actual joystick controller)
|
|
if DistanceBetweenTwoPoints(touchLocation, thumbstickOuterCenter) > thumbstickSize/2 then
|
|
local thumbstickInnerCenter = Vector2.new(thumbstickFrame.Position.X.Offset + thumbstickFrame.AbsoluteSize.x/2, thumbstickFrame.Position.Y.Offset + thumbstickFrame.AbsoluteSize.y/2)
|
|
local movementVectorUnit = Vector2.new(touchLocation.x - thumbstickInnerCenter.x, touchLocation.y - thumbstickInnerCenter.y).unit
|
|
|
|
local outerToInnerVectorCurrent = Vector2.new(thumbstickInnerCenter.x - thumbstickOuterCenter.x, thumbstickInnerCenter.y - thumbstickOuterCenter.y)
|
|
local outerToInnerVectorCurrentUnit = outerToInnerVectorCurrent.unit
|
|
local movementVector = Vector2.new(touchLocation.x - thumbstickInnerCenter.x, touchLocation.y - thumbstickInnerCenter.y)
|
|
|
|
-- First, find the angle between the new thumbstick movement vector,
|
|
-- and the vector between thumbstick inner and thumbstick outer.
|
|
-- We will use this to pivot thumbstick outer around thumbstick inner, gives a nice joystick feel
|
|
local crossOuterToInnerWithMovement = (outerToInnerVectorCurrentUnit.x * movementVectorUnit.y) - (outerToInnerVectorCurrentUnit.y * movementVectorUnit.x)
|
|
local angle = math.atan2(crossOuterToInnerWithMovement, dotProduct(outerToInnerVectorCurrentUnit, movementVectorUnit))
|
|
local anglePercent = angle * math.min( (movementVector.magnitude)/(outerToInnerVectorCurrent.magnitude), 1.0);
|
|
|
|
-- If angle is significant, rotate about the inner thumbsticks current center
|
|
if math.abs(anglePercent) > 0.00001 then
|
|
local outerThumbCenter = rotatePointAboutLocation(thumbstickOuterCenter, thumbstickInnerCenter, anglePercent)
|
|
thumbstickOuter.Position = transformFromCenterToTopLeft(Vector2.new(outerThumbCenter.x,outerThumbCenter.y), thumbstickOuter)
|
|
end
|
|
|
|
-- now just translate outer thumbstick to make sure it stays nears inner thumbstick
|
|
thumbstickOuter.Position = UDim2.new(0,thumbstickOuter.Position.X.Offset+movementVector.x,0,thumbstickOuter.Position.Y.Offset+movementVector.y)
|
|
end
|
|
|
|
thumbstickFrame.Position = transformFromCenterToTopLeft(touchLocation,thumbstickFrame)
|
|
|
|
-- a bit of error checking to make sure thumbsticks stay close to eachother
|
|
thumbstickFramePosition = Vector2.new(thumbstickFrame.Position.X.Offset,thumbstickFrame.Position.Y.Offset)
|
|
thumbstickOuterPosition = Vector2.new(thumbstickOuter.Position.X.Offset,thumbstickOuter.Position.Y.Offset)
|
|
if DistanceBetweenTwoPoints(thumbstickFramePosition, thumbstickOuterPosition) > thumbstickSize/2 then
|
|
local vectorWithLength = (thumbstickOuterPosition - thumbstickFramePosition).unit * thumbstickSize/2
|
|
thumbstickOuter.Position = UDim2.new(0,thumbstickFramePosition.x + vectorWithLength.x,0,thumbstickFramePosition.y + vectorWithLength.y)
|
|
end
|
|
|
|
return Vector2.new(thumbstickFrame.Position.X.Offset - thumbstickOuter.Position.X.Offset,thumbstickFrame.Position.Y.Offset - thumbstickOuter.Position.Y.Offset)
|
|
end
|
|
|
|
function movementOutsideDeadZone(movementVector)
|
|
return ( (math.abs(movementVector.x) > ThumbstickDeadZone) or (math.abs(movementVector.y) > ThumbstickDeadZone) )
|
|
end
|
|
|
|
function constructThumbstick(defaultThumbstickPos, updateFunction, stationaryThumbstick)
|
|
local thumbstickFrame = Instance.new("Frame")
|
|
thumbstickFrame.Name = "ThumbstickFrame"
|
|
thumbstickFrame.Active = true
|
|
thumbstickFrame.Size = UDim2.new(0,thumbstickSize,0,thumbstickSize)
|
|
thumbstickFrame.Position = defaultThumbstickPos
|
|
thumbstickFrame.BackgroundTransparency = 1
|
|
|
|
local outerThumbstick = Instance.new("ImageLabel")
|
|
outerThumbstick.Name = "OuterThumbstick"
|
|
outerThumbstick.Image = touchControlsSheet
|
|
outerThumbstick.ImageRectOffset = Vector2.new(0,0)
|
|
outerThumbstick.ImageRectSize = Vector2.new(220,220)
|
|
outerThumbstick.BackgroundTransparency = 1
|
|
outerThumbstick.Size = UDim2.new(0,thumbstickSize,0,thumbstickSize)
|
|
outerThumbstick.Position = defaultThumbstickPos
|
|
outerThumbstick.Parent = Game.CoreGui.RobloxGui
|
|
|
|
local innerThumbstick = Instance.new("ImageLabel")
|
|
innerThumbstick.Name = "InnerThumbstick"
|
|
innerThumbstick.Image = touchControlsSheet
|
|
innerThumbstick.ImageRectOffset = Vector2.new(220,0)
|
|
innerThumbstick.ImageRectSize = Vector2.new(111,111)
|
|
innerThumbstick.BackgroundTransparency = 1
|
|
innerThumbstick.Size = UDim2.new(0,thumbstickSize/2,0,thumbstickSize/2)
|
|
innerThumbstick.Position = UDim2.new(0, thumbstickFrame.Size.X.Offset/2 - thumbstickSize/4, 0, thumbstickFrame.Size.Y.Offset/2 - thumbstickSize/4)
|
|
innerThumbstick.Parent = thumbstickFrame
|
|
innerThumbstick.ZIndex = 2
|
|
|
|
local thumbstickTouch = nil
|
|
local userInputServiceTouchMovedCon = nil
|
|
local userInputSeviceTouchEndedCon = nil
|
|
|
|
local startInputTracking = function(inputObject)
|
|
if thumbstickTouch then return end
|
|
if inputObject == cameraTouch then return end
|
|
if inputObject == currentJumpTouch then return end
|
|
if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
|
|
|
|
thumbstickTouch = inputObject
|
|
table.insert(thumbstickTouches,thumbstickTouch)
|
|
|
|
thumbstickFrame.Position = transformFromCenterToTopLeft(thumbstickTouch.Position,thumbstickFrame)
|
|
outerThumbstick.Position = thumbstickFrame.Position
|
|
|
|
userInputServiceTouchMovedCon = userInputService.TouchMoved:connect(function(movedInput)
|
|
if movedInput == thumbstickTouch then
|
|
local movementVector = nil
|
|
if stationaryThumbstick then
|
|
movementVector = stationaryThumbstickTouchMove(thumbstickFrame,outerThumbstick,Vector2.new(movedInput.Position.x,movedInput.Position.y))
|
|
else
|
|
movementVector = followThumbstickTouchMove(thumbstickFrame,outerThumbstick,Vector2.new(movedInput.Position.x,movedInput.Position.y))
|
|
end
|
|
|
|
if updateFunction then
|
|
updateFunction(movementVector,outerThumbstick.Size.X.Offset/2)
|
|
end
|
|
end
|
|
end)
|
|
userInputSeviceTouchEndedCon = userInputService.TouchEnded:connect(function(endedInput)
|
|
if endedInput == thumbstickTouch then
|
|
if updateFunction then
|
|
updateFunction(Vector2.new(0,0),1)
|
|
end
|
|
|
|
userInputSeviceTouchEndedCon:disconnect()
|
|
userInputServiceTouchMovedCon:disconnect()
|
|
|
|
thumbstickFrame.Position = defaultThumbstickPos
|
|
outerThumbstick.Position = defaultThumbstickPos
|
|
|
|
for i, object in pairs(thumbstickTouches) do
|
|
if object == thumbstickTouch then
|
|
table.remove(thumbstickTouches,i)
|
|
break
|
|
end
|
|
end
|
|
thumbstickTouch = nil
|
|
end
|
|
end)
|
|
end
|
|
|
|
thumbstickFrame.Visible = not userInputService.ModalEnabled
|
|
outerThumbstick.Visible = not userInputService.ModalEnabled
|
|
|
|
thumbstickFrame.InputBegan:connect(startInputTracking)
|
|
return thumbstickFrame, outerThumbstick
|
|
end
|
|
|
|
----------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------
|
|
--
|
|
-- DPad Control
|
|
--
|
|
|
|
function createDPadArrowButton(name, parent, position, size, image, spriteOffset, spriteSize)
|
|
local DPadArrow = Instance.new("ImageButton")
|
|
DPadArrow.Name = name
|
|
DPadArrow.Image = image
|
|
DPadArrow.ImageRectOffset = spriteOffset
|
|
DPadArrow.ImageRectSize = spriteSize
|
|
DPadArrow.BackgroundTransparency = 1
|
|
DPadArrow.Size = size
|
|
DPadArrow.Position = position
|
|
DPadArrow.Parent = parent
|
|
return DPadArrow
|
|
end
|
|
|
|
function createDPad()
|
|
local DPadFrame = Instance.new("Frame")
|
|
DPadFrame.Name = "DPadFrame"
|
|
DPadFrame.Active = true
|
|
DPadFrame.Size = UDim2.new(0,192,0,192)
|
|
DPadFrame.Position = UDim2.new(0,10,1,-230)
|
|
DPadFrame.BackgroundTransparency = 1
|
|
|
|
-- local image = "rbxassetid://133293265"
|
|
local image = DPadSheet
|
|
local bigSize = UDim2.new(0,64,0,64)
|
|
local smallSize = UDim2.new(0,23,0,23)
|
|
local spriteSizeLarge = Vector2.new(128, 128)
|
|
local spriteSizeSmall = Vector2.new(46, 46)
|
|
|
|
createDPadArrowButton("BackButton", DPadFrame, UDim2.new(0.5, -32, 1, -64), bigSize, image, Vector2.new(0, 0), spriteSizeLarge)
|
|
createDPadArrowButton("ForwardButton", DPadFrame, UDim2.new(0.5, -32, 0, 0), bigSize, image, Vector2.new(0, 258), spriteSizeLarge)
|
|
createDPadArrowButton("JumpButton", DPadFrame, UDim2.new(0.5, -32, 0.5, -32), bigSize, image, Vector2.new(129, 0), spriteSizeLarge)
|
|
createDPadArrowButton("LeftButton", DPadFrame, UDim2.new(0, 0, 0.5, -32), bigSize, image, Vector2.new(129,129), spriteSizeLarge)
|
|
createDPadArrowButton("RightButton", DPadFrame, UDim2.new(1, -64, 0.5, -32), bigSize, image, Vector2.new(0, 129), spriteSizeLarge)
|
|
createDPadArrowButton("forwardLeftButton", DPadFrame, UDim2.new(0, 35, 0, 35), smallSize, image, Vector2.new(129,258), spriteSizeSmall)
|
|
createDPadArrowButton("forwardRightButton", DPadFrame, UDim2.new(1, -55, 0, 35), smallSize, image, Vector2.new(176,258), spriteSizeSmall)
|
|
|
|
return DPadFrame
|
|
end
|
|
|
|
|
|
|
|
|
|
function setupDPadControls(DPadFrame)
|
|
|
|
local moveCharacterFunc = localPlayer.MoveCharacter
|
|
DPadFrame.JumpButton.InputBegan:connect(function(inputObject)
|
|
localPlayer:JumpCharacter()
|
|
end)
|
|
local movementVector = Vector2.new(0,0)
|
|
|
|
function setupButton(button,funcToCallBegin,funcToCallEnd)
|
|
button.InputBegan:connect(function(inputObject)
|
|
if not dpadTouch and inputObject.UserInputType == Enum.UserInputType.Touch and inputObject.UserInputState == Enum.UserInputState.Begin then
|
|
dpadTouch = inputObject
|
|
funcToCallBegin()
|
|
end
|
|
end)
|
|
button.InputEnded:connect(function(inputObject)
|
|
if dpadTouch == inputObject then
|
|
if funcToCallEnd then
|
|
funcToCallEnd()
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
local forwardButtonBegin = function()
|
|
movementVector = Vector2.new(0,-1)
|
|
moveCharacterFunc(localPlayer, Vector2.new(0,-1), 1)
|
|
|
|
DPadFrame.forwardLeftButton.Visible = true
|
|
DPadFrame.forwardRightButton.Visible = true
|
|
end
|
|
|
|
local backwardButtonBegin = function()
|
|
movementVector = Vector2.new(0,1)
|
|
moveCharacterFunc(localPlayer, Vector2.new(0,1),1)
|
|
end
|
|
|
|
local leftButtonBegin = function()
|
|
movementVector = Vector2.new(-1,0)
|
|
moveCharacterFunc(localPlayer, Vector2.new(-1,0),1)
|
|
end
|
|
|
|
local rightButtonBegin = function()
|
|
movementVector = Vector2.new(1,0)
|
|
moveCharacterFunc(localPlayer, Vector2.new(1,0),1)
|
|
end
|
|
|
|
DPadFrame.InputEnded:connect(function()
|
|
DPadFrame.forwardLeftButton.Visible = false
|
|
DPadFrame.forwardRightButton.Visible = false
|
|
end)
|
|
|
|
local endStep = 0.08
|
|
local endMovementFunc = function()
|
|
DPadFrame.forwardLeftButton.Visible = false
|
|
DPadFrame.forwardRightButton.Visible = false
|
|
|
|
Spawn(function()
|
|
while not dpadTouch and movementVector ~= Vector2.new(0,0) do
|
|
local newX = movementVector.x
|
|
local newY = movementVector.y
|
|
|
|
if movementVector.x > 0 then
|
|
newX = movementVector.x - endStep
|
|
if newX < 0 then newX = 0 end
|
|
elseif movementVector.x < 0 then
|
|
newX = movementVector.x + endStep
|
|
if newX > 0 then newX = 0 end
|
|
end
|
|
|
|
if movementVector.y > 0 then
|
|
newY = movementVector.y - endStep
|
|
if newY < 0 then newY = 0 end
|
|
elseif movementVector.y < 0 then
|
|
newY = movementVector.y + endStep
|
|
if newY > 0 then newY = 0 end
|
|
end
|
|
|
|
movementVector = Vector2.new(newX,newY)
|
|
moveCharacterFunc(localPlayer, movementVector,1)
|
|
wait(1/60)
|
|
end
|
|
|
|
if movementVector ~= Vector2.new(0,0) then
|
|
movementVector = Vector2.new(0,0)
|
|
moveCharacterFunc(localPlayer,Vector2.new(0,0) ,0)
|
|
end
|
|
end)
|
|
end
|
|
|
|
local removeDiagonalButtons = function()
|
|
if isPointInRect(dpadTouch.Position,DPadFrame.ForwardButton.AbsolutePosition,DPadFrame.ForwardButton.AbsoluteSize) then
|
|
DPadFrame.forwardLeftButton.Visible = false
|
|
DPadFrame.forwardRightButton.Visible = false
|
|
end
|
|
end
|
|
|
|
setupButton(DPadFrame.ForwardButton,forwardButtonBegin,removeDiagonalButtons)
|
|
setupButton(DPadFrame.BackButton,backwardButtonBegin,nil)
|
|
setupButton(DPadFrame.LeftButton,leftButtonBegin,nil)
|
|
setupButton(DPadFrame.RightButton,rightButtonBegin,nil)
|
|
|
|
local getMovementVector = function(touchPosition)
|
|
local xDiff = touchPosition.x - (DPadFrame.AbsolutePosition.x + DPadFrame.AbsoluteSize.x/2)
|
|
local yDiff = touchPosition.y - (DPadFrame.AbsolutePosition.y + DPadFrame.AbsoluteSize.y/2)
|
|
local vectorNew = Vector2.new(xDiff,yDiff)
|
|
|
|
movementVector = vectorNew.unit
|
|
return vectorNew.unit
|
|
end
|
|
|
|
Game:GetService("UserInputService").TouchMoved:connect(function(touchObject)
|
|
if touchObject == dpadTouch then
|
|
if isPointInRect(dpadTouch.Position,DPadFrame.AbsolutePosition,DPadFrame.AbsoluteSize) then
|
|
moveCharacterFunc(localPlayer, getMovementVector(dpadTouch.Position),1)
|
|
else
|
|
endMovementFunc()
|
|
end
|
|
end
|
|
end)
|
|
Game:GetService("UserInputService").TouchEnded:connect(function(touchObject)
|
|
if touchObject == dpadTouch then
|
|
dpadTouch = nil
|
|
endMovementFunc()
|
|
end
|
|
end)
|
|
|
|
|
|
DPadFrame.Visible = not userInputService.ModalEnabled
|
|
|
|
end
|
|
|
|
----------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------
|
|
--
|
|
-- Character Movement
|
|
--
|
|
local characterThumbstick = nil
|
|
local characterOuterThumbstick = nil
|
|
|
|
function setupCharacterMovement( parentFrame )
|
|
local lastMovementVector, lastMaxMovement = nil
|
|
local moveCharacterFunc = localPlayer.MoveCharacter
|
|
local moveCharacterFunction = function ( movementVector, maxMovement )
|
|
if localPlayer then
|
|
if movementOutsideDeadZone(movementVector) then
|
|
lastMovementVector = movementVector
|
|
lastMaxMovement = maxMovement
|
|
-- sometimes rounding error will not allow us to go max speed at some
|
|
-- thumbstick angles, fix this with a bit of fudging near 100% throttle
|
|
if movementVector.magnitude/maxMovement > ThumbstickMaxPercentGive then
|
|
maxMovement = movementVector.magnitude - 1
|
|
end
|
|
moveCharacterFunc(localPlayer, movementVector, maxMovement)
|
|
else
|
|
lastMovementVector = Vector2.new(0,0)
|
|
lastMaxMovement = 1
|
|
moveCharacterFunc(localPlayer, lastMovementVector, lastMaxMovement)
|
|
end
|
|
end
|
|
end
|
|
|
|
local thumbstickPos = UDim2.new(0,thumbstickSize/2,1,-thumbstickSize*1.75)
|
|
if isSmallScreenDevice() then
|
|
thumbstickPos = UDim2.new(0,(thumbstickSize/2) - 10,1,-thumbstickSize - 20)
|
|
end
|
|
|
|
characterThumbstick, characterOuterThumbstick = constructThumbstick(thumbstickPos, moveCharacterFunction, false)
|
|
characterThumbstick.Name = "CharacterThumbstick"
|
|
characterThumbstick.Parent = parentFrame
|
|
|
|
local refreshCharacterMovement = function()
|
|
if localPlayer and isInThumbstickMode and moveCharacterFunc and lastMovementVector and lastMaxMovement then
|
|
moveCharacterFunc(localPlayer, lastMovementVector, lastMaxMovement)
|
|
end
|
|
end
|
|
return refreshCharacterMovement
|
|
end
|
|
|
|
----------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------
|
|
--
|
|
-- Jump Button
|
|
--
|
|
local jumpButton = nil
|
|
|
|
function setupJumpButton( parentFrame )
|
|
if (jumpButton == nil) then
|
|
jumpButton = Instance.new("ImageButton")
|
|
jumpButton.Name = "JumpButton"
|
|
jumpButton.BackgroundTransparency = 1
|
|
jumpButton.Image = touchControlsSheet
|
|
jumpButton.ImageRectOffset = Vector2.new(176,222)
|
|
jumpButton.ImageRectSize = Vector2.new(174,174)
|
|
jumpButton.Size = UDim2.new(0,jumpButtonSize,0,jumpButtonSize)
|
|
if isSmallScreenDevice() then
|
|
jumpButton.Position = UDim2.new(1, -(jumpButtonSize*2.25), 1, -jumpButtonSize - 20)
|
|
else
|
|
jumpButton.Position = UDim2.new(1, -(jumpButtonSize*2.75), 1, -jumpButtonSize - 120)
|
|
end
|
|
|
|
local playerJumpFunc = localPlayer.JumpCharacter
|
|
|
|
local doJumpLoop = function ()
|
|
while currentJumpTouch do
|
|
if localPlayer then
|
|
playerJumpFunc(localPlayer)
|
|
end
|
|
wait(1/60)
|
|
end
|
|
end
|
|
|
|
jumpButton.InputBegan:connect(function(inputObject)
|
|
if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
|
|
if currentJumpTouch then return end
|
|
if inputObject == cameraTouch then return end
|
|
for i, touch in pairs(oldJumpTouches) do
|
|
if touch == inputObject then
|
|
return
|
|
end
|
|
end
|
|
|
|
currentJumpTouch = inputObject
|
|
jumpButton.ImageRectOffset = Vector2.new(0,222)
|
|
jumpButton.ImageRectSize = Vector2.new(174,174)
|
|
doJumpLoop()
|
|
end)
|
|
jumpButton.InputEnded:connect(function (inputObject)
|
|
if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
|
|
|
|
jumpButton.ImageRectOffset = Vector2.new(176,222)
|
|
jumpButton.ImageRectSize = Vector2.new(174,174)
|
|
|
|
if inputObject == currentJumpTouch then
|
|
table.insert(oldJumpTouches,currentJumpTouch)
|
|
currentJumpTouch = nil
|
|
end
|
|
end)
|
|
userInputService.InputEnded:connect(function ( globalInputObject )
|
|
for i, touch in pairs(oldJumpTouches) do
|
|
if touch == globalInputObject then
|
|
table.remove(oldJumpTouches,i)
|
|
break
|
|
end
|
|
end
|
|
end)
|
|
jumpButton.Parent = parentFrame
|
|
end
|
|
jumpButton.Visible = not userInputService.ModalEnabled
|
|
end
|
|
|
|
function isTouchUsedByJumpButton( touch )
|
|
if touch == currentJumpTouch then return true end
|
|
for i, touchToCompare in pairs(oldJumpTouches) do
|
|
if touch == touchToCompare then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function isTouchUsedByThumbstick(touch)
|
|
for i, touchToCompare in pairs(thumbstickTouches) do
|
|
if touch == touchToCompare then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
----------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------
|
|
--
|
|
-- Camera Control
|
|
--
|
|
|
|
function setupCameraControl(parentFrame, refreshCharacterMoveFunc)
|
|
local lastPos = nil
|
|
local hasRotatedCamera = false
|
|
local rotateCameraFunc = userInputService.RotateCamera
|
|
|
|
local pinchTime = -1
|
|
local shouldPinch = false
|
|
local lastPinchScale = nil
|
|
local zoomCameraFunc = userInputService.ZoomCamera
|
|
local pinchTouches = {}
|
|
local pinchFrame = nil
|
|
local pinchInputChangedCon = nil
|
|
local pinchInputEndedCon = nil
|
|
|
|
local resetCameraRotateState = function()
|
|
setCameraTouch(nil)
|
|
hasRotatedCamera = false
|
|
lastPos = nil
|
|
end
|
|
|
|
local resetPinchState = function ()
|
|
pinchTouches = {}
|
|
|
|
pinchTime = -1
|
|
lastPinchScale = nil
|
|
shouldPinch = false
|
|
pinchFrame:Destroy()
|
|
pinchFrame = nil
|
|
end
|
|
|
|
local startPinch = function(firstTouch, secondTouch)
|
|
-- track pinching in new frame
|
|
if pinchFrame then pinchFrame:Destroy() end -- make sure we didn't track in any mud
|
|
pinchFrame = Instance.new("Frame")
|
|
pinchFrame.Name = "PinchFrame"
|
|
pinchFrame.BackgroundTransparency = 1
|
|
pinchFrame.Parent = parentFrame
|
|
pinchFrame.Size = UDim2.new(1,0,1,0)
|
|
|
|
pinchFrame.InputChanged:connect(function(inputObject)
|
|
if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
|
|
|
|
if not shouldPinch then
|
|
resetPinchState()
|
|
return
|
|
end
|
|
|
|
if lastPinchScale == nil then -- first pinch move, just set up scale
|
|
if inputObject == firstTouch then
|
|
lastPinchScale = (inputObject.Position - secondTouch.Position).magnitude
|
|
firstTouch = inputObject
|
|
elseif inputObject == secondTouch then
|
|
lastPinchScale = (inputObject.Position - firstTouch.Position).magnitude
|
|
secondTouch = inputObject
|
|
end
|
|
else -- we are now actually pinching, do comparison to last pinch size
|
|
local newPinchDistance = 0
|
|
if inputObject == firstTouch then
|
|
newPinchDistance = (inputObject.Position - secondTouch.Position).magnitude
|
|
firstTouch = inputObject
|
|
elseif inputObject == secondTouch then
|
|
newPinchDistance = (inputObject.Position - firstTouch.Position).magnitude
|
|
secondTouch = inputObject
|
|
end
|
|
if newPinchDistance ~= 0 then
|
|
local pinchDiff = newPinchDistance - lastPinchScale
|
|
if pinchDiff ~= 0 then
|
|
zoomCameraFunc(userInputService, (pinchDiff * CameraZoomSensitivity))
|
|
end
|
|
lastPinchScale = newPinchDistance
|
|
end
|
|
end
|
|
end)
|
|
pinchFrame.InputEnded:connect(function(inputObject) -- pinch is over, destroy all
|
|
if inputObject == firstTouch or inputObject == secondTouch then
|
|
resetPinchState()
|
|
end
|
|
end)
|
|
end
|
|
|
|
local pinchGestureReceivedTouch = function(inputObject)
|
|
if #pinchTouches < 1 then
|
|
table.insert(pinchTouches,inputObject)
|
|
pinchTime = tick()
|
|
shouldPinch = false
|
|
elseif #pinchTouches == 1 then
|
|
shouldPinch = ( (tick() - pinchTime) <= PinchZoomDelay )
|
|
|
|
if shouldPinch then
|
|
table.insert(pinchTouches,inputObject)
|
|
startPinch(pinchTouches[1], pinchTouches[2])
|
|
resetCameraRotateState()
|
|
return true
|
|
else -- shouldn't ever get here, but just in case
|
|
pinchTouches = {}
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
parentFrame.InputBegan:connect(function (inputObject)
|
|
if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
|
|
if isTouchUsedByJumpButton(inputObject) then return end
|
|
|
|
local usedByThumbstick = isTouchUsedByThumbstick(inputObject)
|
|
local isPinching = false
|
|
if not usedByThumbstick then
|
|
isPinching = pinchGestureReceivedTouch(inputObject)
|
|
end
|
|
|
|
if cameraTouch == nil and not usedByThumbstick and not isPinching then
|
|
setCameraTouch(inputObject)
|
|
lastPos = Vector2.new(cameraTouch.Position.x,cameraTouch.Position.y)
|
|
lastTick = tick()
|
|
end
|
|
end)
|
|
|
|
userInputService.InputChanged:connect(function (inputObject)
|
|
if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
|
|
if cameraTouch ~= inputObject then return end
|
|
|
|
local newPos = Vector2.new(cameraTouch.Position.x,cameraTouch.Position.y)
|
|
local touchDiff = Vector2.new(0,0)
|
|
if lastPos then
|
|
touchDiff = (lastPos - newPos) * CameraRotateSensitivity
|
|
end
|
|
|
|
-- first time rotating outside deadzone, just setup for next changed event
|
|
if not hasRotatedCamera and (touchDiff.magnitude > CameraRotateDeadZone) then
|
|
hasRotatedCamera = true
|
|
lastPos = newPos
|
|
end
|
|
|
|
-- fire everytime after we have rotated out of deadzone
|
|
if hasRotatedCamera and (lastPos ~= newPos) then
|
|
rotateCameraFunc(userInputService, touchDiff)
|
|
refreshCharacterMoveFunc()
|
|
lastPos = newPos
|
|
end
|
|
end)
|
|
userInputService.InputEnded:connect(function (inputObject)
|
|
if cameraTouch == inputObject or cameraTouch == nil then
|
|
resetCameraRotateState()
|
|
end
|
|
|
|
for i, touch in pairs(pinchTouches) do
|
|
if touch == inputObject then
|
|
table.remove(pinchTouches,i)
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
----------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------
|
|
--
|
|
-- Touch Control
|
|
--
|
|
|
|
local touchControlFrame = nil
|
|
local characterDPad = nil
|
|
|
|
function setupTouchControls()
|
|
touchControlFrame = Instance.new("Frame")
|
|
touchControlFrame.Name = "TouchControlFrame"
|
|
touchControlFrame.Size = UDim2.new(1,0,1,0)
|
|
touchControlFrame.BackgroundTransparency = 1
|
|
touchControlFrame.Parent = Game.CoreGui.RobloxGui
|
|
|
|
userInputService.ProcessedEvent:connect(function(inputObject, processed)
|
|
if not processed then return end
|
|
|
|
-- kill camera pan if the touch is used by some user controls
|
|
if inputObject == cameraTouch and inputObject.UserInputState == Enum.UserInputState.Begin then
|
|
setCameraTouch(nil)
|
|
end
|
|
end)
|
|
|
|
setupJumpButton(touchControlFrame)
|
|
local refreshCharacterMoveFunc = setupCharacterMovement(touchControlFrame)
|
|
setupCameraControl(touchControlFrame, refreshCharacterMoveFunc)
|
|
|
|
characterDPad = createDPad()
|
|
characterDPad.Name = "CharacterDPad"
|
|
characterDPad.Parent = touchControlFrame
|
|
setupDPadControls(characterDPad)
|
|
|
|
userInputService.Changed:connect(function(prop)
|
|
if prop == "ModalEnabled" then
|
|
activateTouchControls()
|
|
end
|
|
end)
|
|
|
|
activateTouchControls()
|
|
end
|
|
|
|
function activateTouchControls()
|
|
-- set user controlled visibility
|
|
if userInputService.ModalEnabled then
|
|
characterThumbstick.Visible = false
|
|
characterOuterThumbstick.Visible = false
|
|
jumpButton.Visible = false
|
|
characterDPad.Visible = false
|
|
else
|
|
if (GameSettings.TouchMovementMode.Name == "Thumbstick" or GameSettings.TouchMovementMode.Name == "Default") then
|
|
isInThumbstickMode = true
|
|
else
|
|
isInThumbstickMode = false
|
|
end
|
|
|
|
characterThumbstick.Visible = isInThumbstickMode
|
|
characterOuterThumbstick.Visible = isInThumbstickMode
|
|
jumpButton.Visible = isInThumbstickMode
|
|
characterDPad.Visible = not isInThumbstickMode
|
|
end
|
|
end
|
|
|
|
----------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------
|
|
-- Start of Script
|
|
|
|
if userInputService:IsLuaTouchControls() then
|
|
setupTouchControls()
|
|
else
|
|
script:Destroy()
|
|
end
|
|
|
|
GameSettings.Changed:connect(function(property)
|
|
if (property == "TouchMovementMode") then
|
|
activateTouchControls()
|
|
end
|
|
end)
|