Currently my game has this method of swiping a mobile buttons while also moving a camera, This was a temporary solution to make firebutton easier to use and alternative to most inputs since roblox doesn’t offer a thumbstick or a way to make multi-touch, here most of the game i believe becomes even painful to move so while a player is holding the buttons for example and user moves the touch position out of the bound, function still runs even if you released the finger after swiped outside
ContextActionService/UserInputService when used in most games
as you can see, a fire function ends if they’re inside a button player was holding so i came up with a way of using RunService updating along with mouse location (since TouchStarted don’t exist on buttons GUI) so the only way was to use a GetMouseLocation() from UserInputService and there’s a problem again.
on Emulator
method worked out pretty fine on single touch but soon as player moves while reaching to another button, it flickers to another new input rather than staying on old ones sometimes which looked like this on phone
On actual phone
here’s also the script
local inputSV = game:GetService("UserInputService")
local runSV = game:GetService("RunService")
local gui = script.Parent
local butonz_pak = gui.butonz
local mouse_evt = nil
local mouse_process
local inputted = false
local touch_crv
butonz_pak.FireButton.InputBegan:Connect(function(inputOBJ, game_evt_proc)
inputted = true --boolean sorted out wrong but i'll change the way later
if inputOBJ.UserInputType == Enum.UserInputType.Touch or inputOBJ.UserInputType == Enum.UserInputType.MouseButton1 and inputOBJ.UserInputState == Enum.UserInputState.Begin then --mouse check exists for debugging purposes
if inputted then
for ind, butonz in pairs(butonz_pak:GetChildren()) do --hide all action buttons, preventing unexpected inputs
if butonz.Name ~= "FireButton" then
butonz.Visible = false
end
end
mouse_process = runSV.RenderStepped:Connect(function(delta) --connect while holding buttons
mouse_evt = inputSV:GetMouseLocation() --no such TouchStarted exists so this was used
butonz_pak.FireButton.Position = UDim2.fromOffset(mouse_evt.X, mouse_evt.Y - 30)
butonz_pak.FireButton.Size = UDim2.fromScale(0.172, 0.301)
end)
end
end
end)
butonz_pak.FireButton.InputEnded:Connect(function(inputOBJ, game_evt_proc)
inputted = false --boolean sorted out wrong but i'll change the way later
if inputOBJ.UserInputType == Enum.UserInputType.Touch or inputOBJ.UserInputType == Enum.UserInputType.MouseButton1 and inputOBJ.UserInputState ~= Enum.UserInputState.Begin then --mouse check exists for debugging purposes
if not inputted then
mouse_process:Disconnect()
mouse_evt = nil
butonz_pak.FireButton.Position = UDim2.fromScale(0.797, 0.665)
butonz_pak.FireButton.Size = UDim2.fromScale(0.094, 0.164)
for ind, butonz in pairs(butonz_pak:GetChildren()) do
butonz.Visible = true
end
end
end
end)
Turns out the InputObject gets created and UI follows by the mouse location, i had to reference the first touch from the InputObject and get a rid of GetMouseLocation() since that does not support multi touch and is meant for mouse (most games utilizes on mouse only on mobile supported games) This worked out pretty well
local runSV = game:GetService("RunService")
local gui = script.Parent
local butonz_pak = gui.butonz --folders
local mouse_process
local inputted = false
local touch_crv
butonz_pak.FireButton.InputBegan:Connect(function(inputOBJ, game_evt_proc)
if not (touch_crv) or (touch_crv == inputOBJ) then
touch_crv = inputOBJ --if first touch is pressed then assign it to touch_crv
if touch_crv.UserInputType == Enum.UserInputType.Touch and touch_crv.UserInputState == Enum.UserInputState.Begin and not inputted then
inputted = true
for ind, butonz in pairs(butonz_pak:GetChildren()) do --hide all action buttons, preventing unexpected inputs
if butonz.Name ~= "FireButton" then
butonz.Visible = false
end
end
mouse_process = runSV.RenderStepped:Connect(function(delta) --Because touch events for UI don't work on first tap, we have to use RunService
butonz_pak.FireButton.Position = UDim2.fromOffset(touch_crv.Position.X, touch_crv.Position.Y) --InputObject by default uses vector3 but you can use X and Y only
butonz_pak.FireButton.Size = UDim2.fromScale(0.172, 0.301) --scale image
end)
end
else --else meaning that it's for other touch, preventing flickers
if inputOBJ.UserInputType == Enum.UserInputType.Touch and inputOBJ.UserInputState == Enum.UserInputState.Begin and not inputted then
inputted = true --boolean sorted out wrong but i'll change the way later
for ind, butonz in pairs(butonz_pak:GetChildren()) do --hide all action buttons, preventing unexpected inputs
if butonz.Name ~= "FireButton" then
butonz.Visible = false
end
end
mouse_process = runSV.RenderStepped:Connect(function(delta) --connect while holding buttons
butonz_pak.FireButton.Position = UDim2.fromOffset(touch_crv.Position.X, touch_crv.Position.Y) --InputObject by default uses vector3 but you can use X and Y only
butonz_pak.FireButton.Size = UDim2.fromScale(0.172, 0.301)
end)
end
end
end)
butonz_pak.FireButton.InputEnded:Connect(function(inputOBJ, game_evt_proc)
inputted = false --boolean sorted out wrong but i'll change the way later
if touch_crv == inputOBJ then --if first touch is still inputobject (either first or second)
touch_crv = nil --then we set it to nil
mouse_process:Disconnect() --RunService still runs when we assign it so this is required to do
butonz_pak.FireButton.Position = UDim2.fromScale(0.797, 0.665)
butonz_pak.FireButton.Size = UDim2.fromScale(0.094, 0.164)
for ind, butonz in pairs(butonz_pak:GetChildren()) do --make all buttons in butonz_pak visible
butonz.Visible = true
end
else --same applies to below
inputted = false --boolean sorted out wrong but i'll change the way later
mouse_process:Disconnect()
touch_crv = nil
butonz_pak.FireButton.Position = UDim2.fromScale(0.797, 0.665)
butonz_pak.FireButton.Size = UDim2.fromScale(0.094, 0.164)
for ind, butonz in pairs(butonz_pak:GetChildren()) do
butonz.Visible = true
end
end
end)