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