Effortlessly reposition context action buttons!

Hello! Are you tired of using SetPosition everytime? Well fear not I have a script that reposition all of the touch buttons so you don’t have to call SetPosition every single time.

local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")

local TOUCH_BUTTON_POSITIONS: { UDim2 } = table.freeze({
	UDim2.fromScale(0.2, -0.8),
	UDim2.fromScale(-0.6, -0.6),
	UDim2.fromScale(-0.8, 0.2),
	UDim2.fromScale(0.4, -1.6),
	UDim2.fromScale(-0.4, -1.4),
	UDim2.fromScale(-1.2, -1.2),
	UDim2.fromScale(-1.4, -0.4),
})

local TOUCH_BUTTON_SIZE = UDim2.fromScale(0.8, 0.8)
local JUMP_BUTTON_SIZE = NumberRange.new(70, 120)

local playerGui = Players.LocalPlayer.PlayerGui
local touchButtons: { ImageButton } = {}
local camera = workspace.CurrentCamera

local function createUICorner(parent: GuiObject)
	local uiCorner = parent:FindFirstChildOfClass("UICorner") or Instance.new("UICorner")
	uiCorner.CornerRadius = UDim.new(1)
	uiCorner.Archivable = false
	uiCorner.Parent = parent
end

local function handleTouchButtons(object: Object)
	if not object:IsA("ImageButton") then
		return
	end

	if object.Name == "ContextActionButton" then
		local index = #touchButtons + 1
		local position = TOUCH_BUTTON_POSITIONS[index]

		if not position then
			object:Destroy()
			return
		end

		touchButtons[index] = object

		object.Position = position
		object.Size = TOUCH_BUTTON_SIZE
	elseif object.Name ~= "ThumbstickFrame" and object.Name ~= "JumpButton" then
		return
	end

	createUICorner(object)
end

local function isSmallScreen(): boolean
	local viewportSize = camera.ViewportSize
	local minAxis = math.min(viewportSize.X, viewportSize.Y)

	return minAxis <= 500
end

local function getJumpButtonPosition(jumpButtonSize: number): UDim2
	local xOffset = -(jumpButtonSize * 1.5 - 10)
	local yOffset = -(if isSmallScreen() then jumpButtonSize + 20 else jumpButtonSize * 1.75)

	return UDim2.new(1, xOffset, 1, yOffset)
end

local function handleContextButtonFrame(object: Object)
	if not object:IsA("Frame") or object.Name ~= "ContextButtonFrame" then
		return
	end

	local function onViewportSizeChanged()
		local jumpButtonSize = if isSmallScreen()
			then JUMP_BUTTON_SIZE.Min
			else JUMP_BUTTON_SIZE.Max

		object.Size = UDim2.fromOffset(jumpButtonSize, jumpButtonSize)
		object.Position = getJumpButtonPosition(jumpButtonSize)
	end

	onViewportSizeChanged()

	local viewportSizeChangedConnection =
		camera:GetPropertyChangedSignal("ViewportSize"):Connect(onViewportSizeChanged)

	object.Destroying:Once(function()
		viewportSizeChangedConnection:Disconnect()
	end)
end

local function handleContextActionGui(object: Object)
	if not object:IsA("ScreenGui") or object.Name ~= "ContextActionGui" then
		return
	end

	local function onPreferredInputChanged()
		local preferredInput = UserInputService.PreferredInput
		object.Enabled = preferredInput == Enum.PreferredInput.Touch
	end

	onPreferredInputChanged()

	local preferredInputChangedConnection =
		UserInputService:GetPropertyChangedSignal("PreferredInput"):Connect(onPreferredInputChanged)

	object.Destroying:Once(function()
		preferredInputChangedConnection:Disconnect()
	end)
end

local function onDescendantAdded(descendant: Instance)
	handleContextButtonFrame(descendant)
	handleContextActionGui(descendant)
	handleTouchButtons(descendant)
end

local function repositionTouchButtons()
	for index, touchButton in touchButtons do
		touchButton.Position = TOUCH_BUTTON_POSITIONS[index]
	end
end

local function onDescendantRemoving(descendant: Instance)
	local index = if descendant:IsA("ImageButton")
		then table.find(touchButtons, descendant)
		else nil
	if not index then
		return
	end

	if not table.remove(touchButtons, index) then
		return
	end

	repositionTouchButtons()
end

for _, descendant in playerGui:GetDescendants() do
	onDescendantAdded(descendant)
end

playerGui.DescendantAdded:Connect(onDescendantAdded)
playerGui.DescendantRemoving:Connect(onDescendantRemoving)

Preview:

9 Likes