Updating GuiObject.Position in mouse input event causes delayed update

When you modify a GUI Position in a mouse input event (e.g., MouseButton1Down), it doesn’t take effect until the next render cycle. However, all other properties (e.g., BackgroundColor3), do get updated in the immediate frame. This can cause flickering in GUIs.

My expectation is that if I update two properties, they take effect simultaneously (or in the same sequence I assigned them).

Short of that, it’d be nice if this were documented. I did waste ~3+ hours on this today trying to get to root cause, only to learn that the issue was in the engine.

Steps to Reproduce:

I’ve attached a simple(ish) repro. You can see there’s a “Rotate” function which moves & recolors GUIs, but the overall screen remains the same: there are three text buttons going down the screen in red, green, and blue.

This Rotate function is called every second. When running in this manner, there is no flicker. However, I also made it so clicking any one of these buttons calls the Rotate function. In this case, there is a flicker.

Code to Reproduce

This code is meant to run as a plugin, or through HotSwap (it puts a GUI in the CoreGui).


--[[

Attempt to reproduce an issue where elements aren't moved into position at the right times.

--]]

local sgui = Instance.new("ScreenGui", game.CoreGui);
local f = Instance.new("TextButton");
f.Size = UDim2.new(0, 200, 0, 40);
f.Position = UDim2.new(.5, 0, .5, 0);
f.AnchorPoint = Vector2.new(.5, .5);
f.Visible = false;

local elements = {};
for i = 1, 6 do
	table.insert(elements, f:Clone());
	elements[i].Parent = sgui;
	elements[i].Text = "element " .. tostring(i);
	elements[i].MouseButton1Down:Connect(function()
		--Note: spawn(Rotate) fixes the flicker.
		Rotate();
		--spawn(Rotate);
	end)
end

local COLORS = {
	Color3.fromRGB(255, 0, 0);
	Color3.fromRGB(0, 255, 0);
	Color3.fromRGB(0, 0, 255);
}
local function PlaceElement(obj, position)
	--Note how we're changing BackgroundColor3 & Position; I believe BackgroundColor3 changes this frame, whereas Position changes next.
	obj.Position = UDim2.new(.5, 0, .5, (position - 2) * 45);
	obj.BackgroundColor3 = COLORS[position];
end

local set = 1;
local offset = 0;
function Rotate()
	--Now, place the next 3 elements in place, then make them visible
	for i = 1, 3 do
		PlaceElement(elements[set* 3 + i], (i + offset - 1) % 3 + 1);
	end
	--Make the upcoming set visible. There should be no flicker even if they have a higher
	--ZIndex due to the fact that their Color exactly matches the GUIs underneath.
	for i = 1, 3 do
		elements[set * 3 + i].Visible = true;
	end
	set = (set + 1) % 2
	for i = 1, 3 do
		elements[set * 3 + i].Visible = false;
	end
	offset += 1;
end

while true do
	Rotate(); --This doesn't cause any flicker; probably because of when the thread runs?
	wait(1);
end
2 Likes

Hi @blobbyblob - thanks for the bug report! We’ve created a ticket to investigate this issue.

4 Likes

Is there any update for this? I’m bumping into it or other similar manifestations of it maybe twice a week now. Has this been closed as won’t-fix?

I keep having to add silly task.delay statements all over the place to remove the flicker.