local function i(i,c,K)i:TweenSizeAndPosition(c,K,Enum.EasingDirection.InOut,Enum.EasingStyle.Linear,.15,true)end local function c(i,c,K,J,Q,o)local D=Instance.new("\073\109\097\103\101\076\097\098\101\108")D.Name=i D.Image=DPAD_SHEET D.ImageRectOffset=J D.ImageRectSize=Q D.BackgroundTransparency=1 D.ImageColor3=Color3.fromRGB(190,190,190)D.Size=K D.Position=c D.Parent=o return D end function TouchThumbpad.Enable(K,i,c)if i==nil then return false end i=i and true or false if K.enabled==i then return true end K.moveVector=ZERO_VECTOR3 K.isJumping=false if i then if not K.thumbpadFrame then K:Create(c)end K.thumbpadFrame.Visible=true else K.thumbpadFrame.Visible=false K:OnInputEnded()end K.enabled=i end function TouchThumbpad.OnInputEnded(c)c.moveVector=ZERO_VECTOR3 c.isJumping=false c.thumbpadFrame.Position=c.screenPos c.touchObject=nil c.isUp,c.isDown,c.isLeft,c.isRight=false,false,false,false i(c.dArrow,c.smArrowSize,UDim2.new(.5,-0.5*c.smArrowSize.X.Offset,1,c.lgImgOffset))i(c.uArrow,c.smArrowSize,UDim2.new(.5,-0.5*c.smArrowSize.X.Offset,0,c.smImgOffset))i(c.lArrow,c.smArrowSize,UDim2.new(0,c.smImgOffset,.5,-0.5*c.smArrowSize.Y.Offset))i(c.rArrow,c.smArrowSize,UDim2.new(1,c.lgImgOffset,.5,-0.5*c.smArrowSize.Y.Offset))end function TouchThumbpad.Create(K,i)if K.thumbpadFrame then K.thumbpadFrame:Destroy()K.thumbpadFrame=nil end if K.touchChangedConn then K.touchChangedConn:Disconnect()K.touchChangedConn=nil end if K.touchEndedConn then K.touchEndedConn:Disconnect()K.touchEndedConn=nil end if K.menuOpenedConn then K.menuOpenedConn:Disconnect()K.menuOpenedConn=nil end local J=math.min(i.AbsoluteSize.x,i.AbsoluteSize.y)local Q=J<=500 local o=Q and 70 or 120 K.screenPos=Q and UDim2.new(0,o*1.25,1,-o-20)or UDim2.new(0,o*.5-10,1,-o*1.75-10)K.thumbpadFrame=Instance.new("\070\114\097\109\101")K.thumbpadFrame.Name="\084\104\117\109\098\112\097\100\070\114\097\109\101"K.thumbpadFrame.Visible=false K.thumbpadFrame.Active=true K.thumbpadFrame.Size=UDim2.new(0,o+20,0,o+20)K.thumbpadFrame.Position=K.screenPos K.thumbpadFrame.BackgroundTransparency=1 local D=Instance.new("\073\109\097\103\101\076\097\098\101\108")D.Name="\079\117\116\101\114\073\109\097\103\101"D.Image=TOUCH_CONTROL_SHEET D.ImageRectOffset=Vector2.new(0,0)D.ImageRectSize=Vector2.new(220,220)D.BackgroundTransparency=1 D.Size=UDim2.new(0,o,0,o)D.Position=UDim2.new(0,10,0,10)D.Parent=K.thumbpadFrame K.smArrowSize=Q and UDim2.new(0,32,0,32)or UDim2.new(0,64,0,64)K.lgArrowSize=UDim2.new(0,K.smArrowSize.X.Offset*2,0,K.smArrowSize.Y.Offset*2)local g=Vector2.new(110,110)K.smImgOffset=Q and-4 or-9 K.lgImgOffset=Q and-28 or-55 K.dArrow=c("\068\111\119\110\065\114\114\111\119",UDim2.new(.5,-0.5*K.smArrowSize.X.Offset,1,K.lgImgOffset),K.smArrowSize,Vector2.new(8,8),g,D)K.uArrow=c("\085\112\065\114\114\111\119",UDim2.new(.5,-0.5*K.smArrowSize.X.Offset,0,K.smImgOffset),K.smArrowSize,Vector2.new(8,266),g,D)K.lArrow=c("\076\101\102\116\065\114\114\111\119",UDim2.new(0,K.smImgOffset,.5,-0.5*K.smArrowSize.Y.Offset),K.smArrowSize,Vector2.new(137,137),g,D)K.rArrow=c("\082\105\103\104\116\065\114\114\111\119",UDim2.new(1,K.lgImgOffset,.5,-0.5*K.smArrowSize.Y.Offset),K.smArrowSize,Vector2.new(8,137),g,D)local function B(i,c,K)i:TweenSizeAndPosition(c,K,Enum.EasingDirection.InOut,Enum.EasingStyle.Linear,.15,true)end local X=nil local V=.1 K.isRight,K.isLeft,K.isUp,K.isDown=false,false,false,false local function n(i)local c=i-X local J=(2*c)/o if J.Magnitude<V then K.moveVector=ZERO_VECTOR3 else J=J.unit*((J.Magnitude-V)/(1-V))if J.Magnitude==0 then K.moveVector=ZERO_VECTOR3 else K.moveVector=(Vector3.new(J.x,0,J.y)).Unit end end local Q=K.moveVector:Dot(-UNIT_Z)local D=K.moveVector:Dot(UNIT_X)if Q>.5 then if not K.isUp then K.isUp,K.isDown=true,false B(K.uArrow,K.lgArrowSize,UDim2.new(.5,-K.smArrowSize.X.Offset,0,K.smImgOffset-1.5*K.smArrowSize.Y.Offset))B(K.dArrow,K.smArrowSize,UDim2.new(.5,-0.5*K.smArrowSize.X.Offset,1,K.lgImgOffset))end elseif Q<-0.5 then if not K.isDown then K.isDown,K.isUp=true,false B(K.dArrow,K.lgArrowSize,UDim2.new(.5,-K.smArrowSize.X.Offset,1,K.lgImgOffset+.5*K.smArrowSize.Y.Offset))B(K.uArrow,K.smArrowSize,UDim2.new(.5,-0.5*K.smArrowSize.X.Offset,0,K.smImgOffset))end else K.isUp,K.isDown=false,false B(K.dArrow,K.smArrowSize,UDim2.new(.5,-0.5*K.smArrowSize.X.Offset,1,K.lgImgOffset))B(K.uArrow,K.smArrowSize,UDim2.new(.5,-0.5*K.smArrowSize.X.Offset,0,K.smImgOffset))end if D>.5 then if not K.isRight then K.isRight,K.isLeft=true,false B(K.rArrow,K.lgArrowSize,UDim2.new(1,K.lgImgOffset+.5*K.smArrowSize.X.Offset,.5,-K.smArrowSize.Y.Offset))B(K.lArrow,K.smArrowSize,UDim2.new(0,K.smImgOffset,.5,-0.5*K.smArrowSize.Y.Offset))end elseif D<-0.5 then if not K.isLeft then K.isLeft,K.isRight=true,false B(K.lArrow,K.lgArrowSize,UDim2.new(0,K.smImgOffset-1.5*K.smArrowSize.X.Offset,.5,-K.smArrowSize.Y.Offset))B(K.rArrow,K.smArrowSize,UDim2.new(1,K.lgImgOffset,.5,-0.5*K.smArrowSize.Y.Offset))end else K.isRight,K.isLeft=false,false B(K.lArrow,K.smArrowSize,UDim2.new(0,K.smImgOffset,.5,-0.5*K.smArrowSize.Y.Offset))B(K.rArrow,K.smArrowSize,UDim2.new(1,K.lgImgOffset,.5,-0.5*K.smArrowSize.Y.Offset))end end K.thumbpadFrame.InputBegan:connect(function(i)if K.touchObject or i.UserInputType~=Enum.UserInputType.Touch or i.UserInputState~=Enum.UserInputState.Begin then return end K.thumbpadFrame.Position=UDim2.new(0,i.Position.x-.5*K.thumbpadFrame.AbsoluteSize.x,0,i.Position.y-.5*K.thumbpadFrame.Size.Y.Offset)X=Vector3.new(K.thumbpadFrame.AbsolutePosition.x+.5*K.thumbpadFrame.AbsoluteSize.x,K.thumbpadFrame.AbsolutePosition.y+.5*K.thumbpadFrame.AbsoluteSize.y,0)n(i.Position)K.touchObject=i end)K.touchChangedConn=UserInputService.TouchMoved:connect(function(i,c)if i==K.touchObject then n(K.touchObject.Position)end end)K.touchEndedConn=UserInputService.TouchEnded:Connect(function(i)if i==K.touchObject then K:OnInputEnded()end end)K.menuOpenedConn=GuiService.MenuOpened:Connect(function()if K.touchObject then K:OnInputEnded()end end)K.thumbpadFrame.Parent=i end return TouchThumbpad
--
local function doTween(guiObject, endSize, endPosition)
	guiObject:TweenSizeAndPosition(endSize, endPosition, Enum.EasingDirection.InOut, Enum.EasingStyle.Linear, 0.15, true)
end

local function CreateArrowLabel(name, position, size, rectOffset, rectSize, parent)
	local image = Instance.new("ImageLabel")
	image.Name = name
	image.Image = DPAD_SHEET
	image.ImageRectOffset = rectOffset
	image.ImageRectSize = rectSize
	image.BackgroundTransparency = 1
	image.ImageColor3 = Color3.fromRGB(190, 190, 190)
	image.Size = size
	image.Position = position
	image.Parent = parent
	return image
end

function TouchThumbpad:Enable(enable, uiParentFrame)
	if enable == nil then return false end			-- If nil, return false (invalid argument)
	enable = enable and true or false				-- Force anything non-nil to boolean before comparison
	if self.enabled == enable then return true end	-- If no state change, return true indicating already in requested state
	
	self.moveVector = ZERO_VECTOR3
	self.isJumping = false
	
	if enable then
		-- Enable
		if not self.thumbpadFrame then
			self:Create(uiParentFrame)
		end
		self.thumbpadFrame.Visible = true
	else 
		-- Disable
		self.thumbpadFrame.Visible = false
		self:OnInputEnded()
	end
	self.enabled = enable
end
	
function TouchThumbpad:OnInputEnded()
	self.moveVector = ZERO_VECTOR3
	self.isJumping = false

	self.thumbpadFrame.Position = self.screenPos
	self.touchObject = nil
	self.isUp, self.isDown, self.isLeft, self.isRight = false, false, false, false
	doTween(self.dArrow, self.smArrowSize, UDim2.new(0.5, -0.5*self.smArrowSize.X.Offset, 1, self.lgImgOffset))
	doTween(self.uArrow, self.smArrowSize, UDim2.new(0.5, -0.5*self.smArrowSize.X.Offset, 0, self.smImgOffset))
	doTween(self.lArrow, self.smArrowSize, UDim2.new(0, self.smImgOffset, 0.5, -0.5*self.smArrowSize.Y.Offset))
	doTween(self.rArrow, self.smArrowSize, UDim2.new(1, self.lgImgOffset, 0.5, -0.5*self.smArrowSize.Y.Offset))
end

function TouchThumbpad:Create(parentFrame)
	if self.thumbpadFrame then
		self.thumbpadFrame:Destroy()
		self.thumbpadFrame = nil
	end
	if self.touchChangedConn then
		self.touchChangedConn:Disconnect()
		self.touchChangedConn = nil
	end
	if self.touchEndedConn then
		self.touchEndedConn:Disconnect()
		self.touchEndedConn = nil
	end
	if self.menuOpenedConn then
		self.menuOpenedConn:Disconnect()
		self.menuOpenedConn = nil
	end
	
	local minAxis = math.min(parentFrame.AbsoluteSize.x, parentFrame.AbsoluteSize.y)
	local isSmallScreen = minAxis <= 500
	local thumbpadSize = isSmallScreen and 70 or 120
	self.screenPos = isSmallScreen and UDim2.new(0, thumbpadSize * 1.25, 1, -thumbpadSize - 20) or
		UDim2.new(0, thumbpadSize * 0.5 - 10, 1, -thumbpadSize * 1.75 - 10)
	
	self.thumbpadFrame = Instance.new("Frame")
	self.thumbpadFrame.Name = "ThumbpadFrame"
	self.thumbpadFrame.Visible = false
	self.thumbpadFrame.Active = true
	self.thumbpadFrame.Size = UDim2.new(0, thumbpadSize + 20, 0, thumbpadSize + 20)
	self.thumbpadFrame.Position = self.screenPos
	self.thumbpadFrame.BackgroundTransparency = 1
	
	local outerImage = Instance.new("ImageLabel")
	outerImage.Name = "OuterImage"
	outerImage.Image = TOUCH_CONTROL_SHEET
	outerImage.ImageRectOffset = Vector2.new(0, 0)
	outerImage.ImageRectSize = Vector2.new(220, 220)
	outerImage.BackgroundTransparency = 1
	outerImage.Size = UDim2.new(0, thumbpadSize, 0, thumbpadSize)
	outerImage.Position = UDim2.new(0, 10, 0, 10)
	outerImage.Parent = self.thumbpadFrame
	
	self.smArrowSize = isSmallScreen and UDim2.new(0, 32, 0, 32) or UDim2.new(0, 64, 0, 64)
	self.lgArrowSize = UDim2.new(0, self.smArrowSize.X.Offset * 2, 0, self.smArrowSize.Y.Offset * 2)
	local imgRectSize = Vector2.new(110, 110)
	self.smImgOffset = isSmallScreen and -4 or -9
	self.lgImgOffset = isSmallScreen and -28 or -55
	
	self.dArrow = CreateArrowLabel("DownArrow", UDim2.new(0.5, -0.5*self.smArrowSize.X.Offset, 1, self.lgImgOffset), self.smArrowSize, Vector2.new(8, 8), imgRectSize, outerImage)
	self.uArrow = CreateArrowLabel("UpArrow", UDim2.new(0.5, -0.5*self.smArrowSize.X.Offset, 0, self.smImgOffset), self.smArrowSize, Vector2.new(8, 266), imgRectSize, outerImage)
	self.lArrow = CreateArrowLabel("LeftArrow", UDim2.new(0, self.smImgOffset, 0.5, -0.5*self.smArrowSize.Y.Offset), self.smArrowSize, Vector2.new(137, 137), imgRectSize, outerImage)
	self.rArrow = CreateArrowLabel("RightArrow", UDim2.new(1, self.lgImgOffset, 0.5, -0.5*self.smArrowSize.Y.Offset), self.smArrowSize, Vector2.new(8, 137), imgRectSize, outerImage)
	
	local function doTween(guiObject, endSize, endPosition)
		guiObject:TweenSizeAndPosition(endSize, endPosition, Enum.EasingDirection.InOut, Enum.EasingStyle.Linear, 0.15, true)
	end
	
	local padOrigin = nil
	local deadZone = 0.1
	self.isRight, self.isLeft, self.isUp, self.isDown = false, false, false, false
	
	local function doMove(pos)
		
		local moveDelta = pos - padOrigin
		local moveVector2 = 2 * moveDelta / thumbpadSize
		
		-- Scaled Radial Dead Zone
		if moveVector2.Magnitude < deadZone then
			self.moveVector = ZERO_VECTOR3
		else
			moveVector2 = moveVector2.unit * ((moveVector2.Magnitude - deadZone) / (1 - deadZone))
			
			-- prevent NAN Vector from trying to do zerovector.Unit
			if moveVector2.Magnitude == 0 then
				self.moveVector = ZERO_VECTOR3
			else
				self.moveVector = Vector3.new(moveVector2.x, 0, moveVector2.y).Unit
			end
		end
		
		local forwardDot = self.moveVector:Dot(-UNIT_Z)
		local rightDot = self.moveVector:Dot(UNIT_X)
		
		if forwardDot > 0.5 then		-- UP
			if not self.isUp then
				self.isUp, self.isDown = true, false
				doTween(self.uArrow, self.lgArrowSize, UDim2.new(0.5, -self.smArrowSize.X.Offset, 0, self.smImgOffset - 1.5*self.smArrowSize.Y.Offset))
				doTween(self.dArrow, self.smArrowSize, UDim2.new(0.5, -0.5*self.smArrowSize.X.Offset, 1, self.lgImgOffset))
			end
		elseif forwardDot < -0.5 then	-- DOWN
			if not self.isDown then
				self.isDown, self.isUp = true, false
				doTween(self.dArrow, self.lgArrowSize, UDim2.new(0.5, -self.smArrowSize.X.Offset, 1, self.lgImgOffset + 0.5*self.smArrowSize.Y.Offset))
				doTween(self.uArrow, self.smArrowSize, UDim2.new(0.5, -0.5*self.smArrowSize.X.Offset, 0, self.smImgOffset))
			end
		else
			self.isUp, self.isDown = false, false
			doTween(self.dArrow, self.smArrowSize, UDim2.new(0.5, -0.5*self.smArrowSize.X.Offset, 1, self.lgImgOffset))
			doTween(self.uArrow, self.smArrowSize, UDim2.new(0.5, -0.5*self.smArrowSize.X.Offset, 0, self.smImgOffset))
		end
		
		if rightDot > 0.5 then
			if not self.isRight then
				self.isRight, self.isLeft = true, false
				doTween(self.rArrow, self.lgArrowSize, UDim2.new(1, self.lgImgOffset + 0.5*self.smArrowSize.X.Offset, 0.5, -self.smArrowSize.Y.Offset))
				doTween(self.lArrow, self.smArrowSize, UDim2.new(0, self.smImgOffset, 0.5, -0.5*self.smArrowSize.Y.Offset))
			end
		elseif rightDot < -0.5 then
			if not self.isLeft then
				self.isLeft, self.isRight = true, false
				doTween(self.lArrow, self.lgArrowSize, UDim2.new(0, self.smImgOffset - 1.5*self.smArrowSize.X.Offset, 0.5, -self.smArrowSize.Y.Offset))
				doTween(self.rArrow, self.smArrowSize, UDim2.new(1, self.lgImgOffset, 0.5, -0.5*self.smArrowSize.Y.Offset))
			end
		else
			self.isRight, self.isLeft = false, false
			doTween(self.lArrow, self.smArrowSize, UDim2.new(0, self.smImgOffset, 0.5, -0.5*self.smArrowSize.Y.Offset))
			doTween(self.rArrow, self.smArrowSize, UDim2.new(1, self.lgImgOffset, 0.5, -0.5*self.smArrowSize.Y.Offset))
		end
	end
	
	--input connections
	self.thumbpadFrame.InputBegan:connect(function(inputObject)
		--A touch that starts elsewhere on the screen will be sent to a frame's InputBegan event
		--if it moves over the frame. So we check that this is actually a new touch (inputObject.UserInputState ~= Enum.UserInputState.Begin)
		if self.touchObject or inputObject.UserInputType ~= Enum.UserInputType.Touch
			or inputObject.UserInputState ~= Enum.UserInputState.Begin then
			return
		end
		
		self.thumbpadFrame.Position = UDim2.new(0, inputObject.Position.x - 0.5*self.thumbpadFrame.AbsoluteSize.x, 0, inputObject.Position.y - 0.5*self.thumbpadFrame.Size.Y.Offset)
		padOrigin = Vector3.new(self.thumbpadFrame.AbsolutePosition.x +0.5* self.thumbpadFrame.AbsoluteSize.x,
			self.thumbpadFrame.AbsolutePosition.y + 0.5*self.thumbpadFrame.AbsoluteSize.y, 0)
		doMove(inputObject.Position)
		self.touchObject = inputObject
	end)
	
	self.touchChangedConn = UserInputService.TouchMoved:connect(function(inputObject, isProcessed)
		if inputObject == self.touchObject then
			doMove(self.touchObject.Position)
		end
	end)
	
	self.touchEndedConn = UserInputService.TouchEnded:Connect(function(inputObject)
		if inputObject == self.touchObject then
			self:OnInputEnded()
		end
	end)
	
	self.menuOpenedConn = GuiService.MenuOpened:Connect(function()
		if self.touchObject then
			self:OnInputEnded()
		end
	end)	
	
	self.thumbpadFrame.Parent = parentFrame
end

return TouchThumbpad
