2013/yue/153556783.yue

580 lines
19 KiB
Plaintext

import "macros" as { $ }
$load $FILE
-- 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, Roblox 2013
-- Heliodex's basic New function (basically a simplified version of melt)
New = (className, name, props) ->
if not props? -- no name was provided
props = name
name = nil
obj = Instance.new className
obj.Name = name if name
local parent
for k, v in pairs props
if type(k) == "string"
if k == "Parent"
parent = v
else
obj[k] = v
elseif type(k) == "number" and type(v) == "userdata"
v.Parent = obj
obj.Parent = parent
obj
--
-- obligatory stuff to make sure we don't access nil data
wait! until Game
wait! until Game\FindFirstChild "Players"
wait! until Game.Players.LocalPlayer
wait! until Game\FindFirstChild "CoreGui"
wait! until Game.CoreGui\FindFirstChild "RobloxGui"
userInputService = Game\GetService "UserInputService"
success = try
userInputService\IsLuaTouchControls!
if not success
script\Destroy!
----------------------------------------------------------------------------
----------------------------------------------------------------------------
-- Variables
screenResolution = Game\GetService"GuiService"\GetScreenResolution!
isSmallScreenDevice = ->
return screenResolution.y <= 320
localPlayer = Game.Players.LocalPlayer
thumbstickSize = 120
if isSmallScreenDevice!
thumbstickSize = 70
touchControlsSheet = "rbxasset://textures/ui/TouchControlsSheet.png"
ThumbstickDeadZone = 5
ThumbstickMaxPercentGive = 0.92
thumbstickTouches = {}
jumpButtonSize = 90
if isSmallScreenDevice!
jumpButtonSize = 70
oldJumpTouches = {}
local currentJumpTouch
CameraRotateSensitivity = 0.007
CameraRotateDeadZone = CameraRotateSensitivity * 16
CameraZoomSensitivity = 0.03
PinchZoomDelay = 0.2
local cameraTouch
-- make sure all of our images are good to go
Game\GetService"ContentProvider"\Preload touchControlsSheet
----------------------------------------------------------------------------
----------------------------------------------------------------------------
-- Functions
DistanceBetweenTwoPoints = (point1, point2) ->
dx = point2.x - point1.x
dy = point2.y - point1.y
math.sqrt dx * dx + dy * dy
transformFromCenterToTopLeft = (pointToTranslate, guiObject) ->
UDim2.new(
0,
pointToTranslate.x - guiObject.AbsoluteSize.x / 2,
0,
pointToTranslate.y - guiObject.AbsoluteSize.y / 2
)
rotatePointAboutLocation = (pointToRotate, pointToRotateAbout, radians) ->
sinAnglePercent = math.sin radians
cosAnglePercent = math.cos radians
transformedPoint = pointToRotate
-- translate point back to origin:
transformedPoint = Vector2.new transformedPoint.x - pointToRotateAbout.x, transformedPoint.y - pointToRotateAbout.y
-- rotate point
xNew = transformedPoint.x * cosAnglePercent - transformedPoint.y * sinAnglePercent
yNew = transformedPoint.x * sinAnglePercent + transformedPoint.y * cosAnglePercent
-- translate point back:
transformedPoint = Vector2.new xNew + pointToRotateAbout.x, yNew + pointToRotateAbout.y
transformedPoint
dotProduct = (v1, v2) -> v1.x * v2.x + v1.y * v2.y
stationaryThumbstickTouchMove = (thumbstickFrame, thumbstickOuter, touchLocation) ->
thumbstickOuterCenterPosition = Vector2.new(
thumbstickOuter.Position.X.Offset + thumbstickOuter.AbsoluteSize.x / 2,
thumbstickOuter.Position.Y.Offset + thumbstickOuter.AbsoluteSize.y / 2
)
centerDiff = DistanceBetweenTwoPoints touchLocation, thumbstickOuterCenterPosition
-- thumbstick is moving outside our region, need to cap its distance
if centerDiff > (thumbstickSize / 2)
thumbVector = Vector2.new(
touchLocation.x - thumbstickOuterCenterPosition.x,
touchLocation.y - thumbstickOuterCenterPosition.y
)
normal = thumbVector.unit
if normal.x == math.nan or normal.x == math.inf
normal = Vector2.new 0, normal.y
if normal.y == math.nan or normal.y == math.inf
normal = Vector2.new normal.x, 0
newThumbstickInnerPosition = thumbstickOuterCenterPosition + (normal * (thumbstickSize / 2))
thumbstickFrame.Position = transformFromCenterToTopLeft newThumbstickInnerPosition, thumbstickFrame
else
thumbstickFrame.Position = transformFromCenterToTopLeft touchLocation, thumbstickFrame
Vector2.new(
thumbstickFrame.Position.X.Offset - thumbstickOuter.Position.X.Offset,
thumbstickFrame.Position.Y.Offset - thumbstickOuter.Position.Y.Offset
)
followThumbstickTouchMove = (thumbstickFrame, thumbstickOuter, touchLocation) ->
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
thumbstickInnerCenter = Vector2.new(
thumbstickFrame.Position.X.Offset + thumbstickFrame.AbsoluteSize.x / 2,
thumbstickFrame.Position.Y.Offset + thumbstickFrame.AbsoluteSize.y / 2
)
movementVectorUnit = Vector2.new(touchLocation.x - thumbstickInnerCenter.x, touchLocation.y - thumbstickInnerCenter.y).unit
outerToInnerVectorCurrent = Vector2.new(
thumbstickInnerCenter.x - thumbstickOuterCenter.x,
thumbstickInnerCenter.y - thumbstickOuterCenter.y
)
outerToInnerVectorCurrentUnit = outerToInnerVectorCurrent.unit
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
crossOuterToInnerWithMovement = (outerToInnerVectorCurrentUnit.x * movementVectorUnit.y) -
(outerToInnerVectorCurrentUnit.y * movementVectorUnit.x)
angle = math.atan2 crossOuterToInnerWithMovement, dotProduct outerToInnerVectorCurrentUnit, movementVectorUnit
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
outerThumbCenter = rotatePointAboutLocation thumbstickOuterCenter, thumbstickInnerCenter, anglePercent
thumbstickOuter.Position = transformFromCenterToTopLeft Vector2.new(outerThumbCenter.x, outerThumbCenter.y), thumbstickOuter
-- 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
)
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
vectorWithLength = (thumbstickOuterPosition - thumbstickFramePosition).unit * thumbstickSize / 2
thumbstickOuter.Position = UDim2.new(
0,
thumbstickFramePosition.x + vectorWithLength.x,
0,
thumbstickFramePosition.y + vectorWithLength.y
)
Vector2.new(
thumbstickFrame.Position.X.Offset - thumbstickOuter.Position.X.Offset,
thumbstickFrame.Position.Y.Offset - thumbstickOuter.Position.Y.Offset
)
movementOutsideDeadZone = (movementVector) ->
(math.abs(movementVector.x) > ThumbstickDeadZone) or (math.abs(movementVector.y) > ThumbstickDeadZone)
constructThumbstick = (defaultThumbstickPos, updateFunction, stationaryThumbstick) ->
thumbstickFrame = New "Frame", "ThumbstickFrame"
Active: true
Size: UDim2.new 0, thumbstickSize, 0, thumbstickSize
Position: defaultThumbstickPos
BackgroundTransparency: 1
New "ImageLabel", "InnerThumbstick"
Image: touchControlsSheet
ImageRectOffset: Vector2.new 220, 0
ImageRectSize: Vector2.new 111, 111
BackgroundTransparency: 1
Size: UDim2.new 0, thumbstickSize / 2, 0, thumbstickSize / 2
Position: UDim2.new(
0,
thumbstickFrame.Size.X.Offset / 2 - thumbstickSize / 4,
0,
thumbstickFrame.Size.Y.Offset / 2 - thumbstickSize / 4
)
ZIndex: 2
Parent: thumbstickFrame
outerThumbstick = New "ImageLabel", "OuterThumbstick"
Image: touchControlsSheet
ImageRectOffset: Vector2.new 0, 0
ImageRectSize: Vector2.new 220, 220
BackgroundTransparency: 1
Size: UDim2.new 0, thumbstickSize, 0, thumbstickSize
Position: defaultThumbstickPos
Parent: Game.CoreGui.RobloxGui
local thumbstickTouch
local userInputServiceTouchMovedCon
local userInputSeviceTouchEndedCon
startInputTracking = (inputObject) ->
return if thumbstickTouch
return if inputObject == cameraTouch
return if inputObject == currentJumpTouch
return if inputObject.UserInputType ~= Enum.UserInputType.Touch
thumbstickTouch = inputObject
table.insert thumbstickTouches, thumbstickTouch
thumbstickFrame.Position = transformFromCenterToTopLeft(thumbstickTouch.Position, thumbstickFrame)
outerThumbstick.Position = thumbstickFrame.Position
userInputServiceTouchMovedCon = userInputService.TouchMoved\connect (movedInput) ->
if movedInput == thumbstickTouch
local movementVector
if stationaryThumbstick
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
)
if updateFunction
updateFunction movementVector, outerThumbstick.Size.X.Offset / 2
userInputSeviceTouchEndedCon = userInputService.TouchEnded\connect (endedInput) ->
if endedInput == thumbstickTouch
if updateFunction
updateFunction Vector2.new(0, 0), 1
userInputSeviceTouchEndedCon\disconnect!
userInputServiceTouchMovedCon\disconnect!
thumbstickFrame.Position = defaultThumbstickPos
outerThumbstick.Position = defaultThumbstickPos
for i, object in pairs thumbstickTouches
if object == thumbstickTouch
table.remove thumbstickTouches, i
break
thumbstickTouch = nil
userInputService.Changed\connect (prop) ->
if prop == "ModalEnabled"
thumbstickFrame.Visible = outerThumbstick.Visible = not userInputService.ModalEnabled
thumbstickFrame.InputBegan\connect startInputTracking
thumbstickFrame
setupCharacterMovement = (parentFrame) ->
local lastMovementVector, lastMaxMovement
moveCharacterFunc = localPlayer.MoveCharacter
moveCharacterFunction = (movementVector, maxMovement) ->
if localPlayer
if movementOutsideDeadZone movementVector
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
maxMovement = movementVector.magnitude - 1
moveCharacterFunc localPlayer, movementVector, maxMovement
else
lastMovementVector = Vector2.new 0, 0
lastMaxMovement = 1
moveCharacterFunc localPlayer, lastMovementVector, lastMaxMovement
thumbstickPos = UDim2.new 0, thumbstickSize / 2, 1, -thumbstickSize * 1.75
if isSmallScreenDevice!
thumbstickPos = UDim2.new 0, (thumbstickSize / 2) - 10, 1, -thumbstickSize - 20
characterThumbstick = constructThumbstick thumbstickPos, moveCharacterFunction, false
characterThumbstick.Name = "CharacterThumbstick"
characterThumbstick.Parent = parentFrame
refreshCharacterMovement = ->
if localPlayer and moveCharacterFunc and lastMovementVector and lastMaxMovement
moveCharacterFunc localPlayer, lastMovementVector, lastMaxMovement
refreshCharacterMovement
setupJumpButton = (parentFrame) ->
jumpButton = New "ImageButton", "JumpButton"
BackgroundTransparency: 1
Image: touchControlsSheet
ImageRectOffset: Vector2.new 176, 222
ImageRectSize: Vector2.new 174, 174
Size: UDim2.new 0, jumpButtonSize, 0, jumpButtonSize
Position: UDim2.new 1, if isSmallScreenDevice!
-(jumpButtonSize * 2.25), 1, -jumpButtonSize - 20
else
-(jumpButtonSize * 2.75), 1, -jumpButtonSize - 120
playerJumpFunc = localPlayer.JumpCharacter
doJumpLoop = ->
while currentJumpTouch
if localPlayer
playerJumpFunc localPlayer
wait 1 / 60
jumpButton.InputBegan\connect (inputObject) ->
return if inputObject.UserInputType ~= Enum.UserInputType.Touch
return if currentJumpTouch
return if inputObject == cameraTouch
for _, touch in pairs oldJumpTouches
return if touch == inputObject
currentJumpTouch = inputObject
jumpButton.ImageRectOffset = Vector2.new 0, 222
jumpButton.ImageRectSize = Vector2.new 174, 174
doJumpLoop!
jumpButton.InputEnded\connect (inputObject) ->
return if inputObject.UserInputType ~= Enum.UserInputType.Touch
jumpButton.ImageRectOffset = Vector2.new 176, 222
jumpButton.ImageRectSize = Vector2.new 174, 174
if inputObject == currentJumpTouch
table.insert oldJumpTouches, currentJumpTouch
currentJumpTouch = nil
userInputService.InputEnded\connect (globalInputObject) ->
for i, touch in pairs oldJumpTouches
if touch == globalInputObject
table.remove oldJumpTouches, i
break
userInputService.Changed\connect (prop) ->
if prop == "ModalEnabled"
jumpButton.Visible = not userInputService.ModalEnabled
jumpButton.Parent = parentFrame
isTouchUsedByJumpButton = (touch) ->
if touch == currentJumpTouch
return true
for _, touchToCompare in pairs oldJumpTouches
if touch == touchToCompare
return true
false
isTouchUsedByThumbstick = (touch) ->
for _, touchToCompare in pairs thumbstickTouches
if touch == touchToCompare
return true
false
setupCameraControl = (parentFrame, refreshCharacterMoveFunc) ->
local lastPos
hasRotatedCamera = false
rotateCameraFunc = userInputService.RotateCamera
pinchTime = -1
shouldPinch = false
local lastPinchScale
zoomCameraFunc = userInputService.ZoomCamera
pinchTouches = {}
local pinchFrame
resetCameraRotateState = ->
cameraTouch = nil
hasRotatedCamera = false
lastPos = nil
resetPinchState = ->
pinchTouches = {}
lastPinchScale = nil
shouldPinch = false
pinchFrame\Destroy!
pinchFrame = nil
startPinch = (firstTouch, secondTouch) ->
-- track pinching in new frame
pinchFrame?\Destroy!
-- make sure we didn't track in any mud
pinchFrame = New "Frame"
Name: "PinchFrame"
BackgroundTransparency: 1
Size: UDim2.new 1, 0, 1, 0
Parent: parentFrame
pinchFrame.InputChanged\connect (inputObject) ->
if not shouldPinch
resetPinchState!
return
resetCameraRotateState!
if not lastPinchScale? then -- first pinch move, just set up scale
if inputObject == firstTouch
lastPinchScale = (inputObject.Position - secondTouch.Position).magnitude
firstTouch = inputObject
elseif inputObject == secondTouch
lastPinchScale = (inputObject.Position - firstTouch.Position).magnitude
secondTouch = inputObject
else -- we are now actually pinching, do comparison to last pinch size
newPinchDistance = 0
if inputObject == firstTouch
newPinchDistance = (inputObject.Position - secondTouch.Position).magnitude
firstTouch = inputObject
elseif inputObject == secondTouch
newPinchDistance = (inputObject.Position - firstTouch.Position).magnitude
secondTouch = inputObject
if newPinchDistance ~= 0
pinchDiff = newPinchDistance - lastPinchScale
if pinchDiff ~= 0
zoomCameraFunc userInputService, (pinchDiff * CameraZoomSensitivity)
lastPinchScale = newPinchDistance
pinchFrame.InputEnded\connect (inputObject) -> -- pinch is over, destroy all
if inputObject == firstTouch or inputObject == secondTouch
resetPinchState!
pinchGestureReceivedTouch = (inputObject) ->
if #pinchTouches < 1
table.insert pinchTouches, inputObject
pinchTime = tick!
shouldPinch = false
elseif #pinchTouches == 1
shouldPinch = ((tick! - pinchTime) <= PinchZoomDelay)
if shouldPinch
table.insert pinchTouches, inputObject
startPinch pinchTouches[1], pinchTouches[2]
else -- shouldn't ever get here, but just in case
pinchTouches = {}
parentFrame.InputBegan\connect (inputObject) ->
return if inputObject.UserInputType ~= Enum.UserInputType.Touch
return if isTouchUsedByJumpButton inputObject
usedByThumbstick = isTouchUsedByThumbstick inputObject
if not usedByThumbstick
pinchGestureReceivedTouch inputObject
if not cameraTouch? and not usedByThumbstick
cameraTouch = inputObject
lastPos = Vector2.new cameraTouch.Position.x, cameraTouch.Position.y
-- lastTick = tick!
userInputService.InputChanged\connect (inputObject) ->
return if inputObject.UserInputType ~= Enum.UserInputType.Touch
return if cameraTouch ~= inputObject
newPos = Vector2.new cameraTouch.Position.x, cameraTouch.Position.y
touchDiff = (lastPos - newPos) * CameraRotateSensitivity
-- first time rotating outside deadzone, just setup for next changed event
if not hasRotatedCamera and (touchDiff.magnitude > CameraRotateDeadZone)
hasRotatedCamera = true
lastPos = newPos
-- fire everytime after we have rotated out of deadzone
if hasRotatedCamera and (lastPos ~= newPos)
rotateCameraFunc userInputService, touchDiff
refreshCharacterMoveFunc!
lastPos = newPos
userInputService.InputEnded\connect (inputObject) ->
if cameraTouch == inputObject or not cameraTouch?
resetCameraRotateState!
for i, touch in pairs pinchTouches
if touch == inputObject
table.remove pinchTouches, i
setupTouchControls = ->
touchControlFrame = New "Frame", "TouchControlFrame"
Size: UDim2.new 1, 0, 1, 0
BackgroundTransparency: 1
Parent: Game.CoreGui.RobloxGui
refreshCharacterMoveFunc = setupCharacterMovement touchControlFrame
setupJumpButton touchControlFrame
setupCameraControl touchControlFrame, refreshCharacterMoveFunc
userInputService.ProcessedEvent\connect (inputObject, processed) ->
return if not processed
-- kill camera pan if the touch is used by some user controls
if inputObject == cameraTouch and inputObject.UserInputState == Enum.UserInputState.Begin
cameraTouch = nil
----------------------------------------------------------------------------
----------------------------------------------------------------------------
-- Start of Script
-- if true then --userInputService\IsLuaTouchControls!
setupTouchControls!
-- else
-- script\Destroy!