Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- 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 <= 320
- end
- local localPlayer = Game.Players.LocalPlayer
- local thumbstickInactiveAlpha = 0.3
- local thumbstickSize = 120
- if isSmallScreenDevice() then
- thumbstickSize = 70
- end
- local thumbstickInnerImage = "rbxasset://textures/ui/thumbstickInner.png"
- local thumbstickOuterImage = "rbxasset://textures/ui/thumbstickOuter.png"
- local ThumbstickDeadZone = 5
- local ThumbstickMaxPercentGive = 0.97
- local thumbstickTouches = {}
- local jumpButtonUpImage = "rbxasset://textures/ui/jumpButton.png"
- local jumpButtonDownImage = "rbxasset://textures/ui/jumpButtonPress.png"
- local jumpButtonSize = 90
- if isSmallScreenDevice() then
- jumpButtonSize = 70
- end
- local oldJumpTouches = {}
- local currentJumpTouch = nil
- local CameraRotateSensitivity = 0.01
- local CameraRotateDeadZone = CameraRotateSensitivity * 16
- local CameraZoomSensitivity = 0.05
- local PinchZoomDelay = 0.2
- local cameraTouch = nil
- -- make sure all of our images are good to go
- Game:GetService("ContentProvider"):Preload(thumbstickInnerImage)
- Game:GetService("ContentProvider"):Preload(thumbstickOuterImage)
- Game:GetService("ContentProvider"):Preload(jumpButtonUpImage)
- Game:GetService("ContentProvider"):Preload(jumpButtonDownImage)
- ----------------------------------------------------------------------------
- ----------------------------------------------------------------------------
- -- Functions
- 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 = thumbstickOuterImage
- 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 = thumbstickInnerImage
- 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
- userInputService.Changed:connect(function(prop)
- if prop == "ModalEnabled" then
- thumbstickFrame.Visible = not userInputService.ModalEnabled
- outerThumbstick.Visible = not userInputService.ModalEnabled
- end
- end)
- thumbstickFrame.InputBegan:connect(startInputTracking)
- return thumbstickFrame
- end
- 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
- local characterThumbstick = constructThumbstick(thumbstickPos, moveCharacterFunction, false)
- characterThumbstick.Name = "CharacterThumbstick"
- characterThumbstick.Parent = parentFrame
- local refreshCharacterMovement = function()
- if localPlayer and moveCharacterFunc and lastMovementVector and lastMaxMovement then
- moveCharacterFunc(localPlayer, lastMovementVector, lastMaxMovement)
- end
- end
- return refreshCharacterMovement
- end
- function setupJumpButton( parentFrame )
- local jumpButton = Instance.new("ImageButton")
- jumpButton.Name = "JumpButton"
- jumpButton.BackgroundTransparency = 1
- jumpButton.Image = jumpButtonUpImage
- 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.Image = jumpButtonDownImage
- doJumpLoop()
- end)
- jumpButton.InputEnded:connect(function (inputObject)
- if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
- jumpButton.Image = jumpButtonUpImage
- 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)
- userInputService.Changed:connect(function(prop)
- if prop == "ModalEnabled" then
- jumpButton.Visible = not userInputService.ModalEnabled
- end
- end)
- jumpButton.Parent = parentFrame
- 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
- 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 resetCameraRotateState = function()
- cameraTouch = nil
- hasRotatedCamera = false
- lastPos = nil
- end
- local resetPinchState = function ()
- pinchTouches = {}
- 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 not shouldPinch then
- resetPinchState()
- return
- end
- resetCameraRotateState()
- 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])
- else -- shouldn't ever get here, but just in case
- pinchTouches = {}
- end
- end
- 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)
- if not usedByThumbstick then
- pinchGestureReceivedTouch(inputObject)
- end
- if cameraTouch == nil and not usedByThumbstick then
- cameraTouch = 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 = (lastPos - newPos) * CameraRotateSensitivity
- -- 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
- function setupTouchControls()
- local touchControlFrame = Instance.new("Frame")
- touchControlFrame.Name = "TouchControlFrame"
- touchControlFrame.Size = UDim2.new(1,0,1,0)
- touchControlFrame.BackgroundTransparency = 1
- touchControlFrame.Parent = Game.CoreGui.RobloxGui
- local refreshCharacterMoveFunc = setupCharacterMovement(touchControlFrame)
- setupJumpButton(touchControlFrame)
- setupCameraControl(touchControlFrame, refreshCharacterMoveFunc)
- 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
- cameraTouch = nil
- end
- end)
- end
- ----------------------------------------------------------------------------
- ----------------------------------------------------------------------------
- -- Start of Script
- if userInputService:IsLuaTouchControls() then
- setupTouchControls()
- else
- script:Destroy()
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement