How to create a timed leaderboard that doesn't require creating all new elements

I wanna have leaderboards in my game update to say a 5 minute timer. However, whenever they update, I don’t want the UI to be wiped, and recreated with new UI elements, as that’ll cause performance issues (I have 5 leaderboards, 4 of which have daily/weekly/alltime, therefore 13 leaderboards need 50 players listed, 650 frames created every 5 minutes)

I wanna know of a way to make it so it doesn’t require deleting the frames, but I can still update users on the board or remove and add anybody else whose new to the board.

All I do atm is just load them all on start

function LeaderboardController:Create(leaderboardGui, parent, data)
	local Template = leaderboardGui.UIListLayout.Template
	
	-- Create ranks
	for i, v in pairs(data) do
		if tonumber(v.key) > 0 then
			local Success, Error = pcall(function()
				local NewFrame = Template:Clone()
				NewFrame.Name = i
				NewFrame.LayoutOrder = i
				
				-- Set texts
				NewFrame.Rank.Text = "#" .. i
				NewFrame.PlayerName.Text = Players:GetNameFromUserIdAsync(v.key)
				
				if parent.Parent.Parent.Name == "PlayTime" then
					NewFrame.Total.Text = Library:ConvertTime(v.value, 3)
				else
					NewFrame.Total.Text = Library:BreakNumber(v.value)
				end
				
				-- Set rank extra info
				local Data = RANK_DATA[i]
				if Data then
					NewFrame.Size = Data.Size
					
					NewFrame.Rank.TextColor3 = Data.Color
					NewFrame.Rank.UIStroke.Color = Library:MakeDarker(Data.Color)
				end
				
				NewFrame.Parent = parent
				
				-- Update ScrollingFrame CanvasSize
				parent.CanvasSize = UDim2.fromOffset(
					0,
					parent.UIListLayout.AbsoluteContentSize.Y
				)
			end)
		end
	end
end

Creating new UI elements would not cause performance issues unless you’re generating thousands. Even then, updating existing UI elements isn’t going to be that big of a performance improvement. If there’s any issues, it’s likely some other part of your code.

I’m not asking about performance issues. As I said, I’d have to be creating 650 new frames every 5 minutes. Inside those frames is 3 text labels. So that’s 2,600 new UI elements every 5 minutes. I don’t want to do that. I want to find a way to have them just update as necessary

Ah, in that case, why not just only create the UI elements that would be visible within that scroll position?

You are not understanding what I am asking for. I am wanting to know of a way to efficiently update the leaderboards every 5 minutes, where I can change whoever is on the leaderboards score with the most up to date one, or if I need to delete remove people

You’re not understanding what I’m saying either. You don’t need to update every single one. All you need to update are the ones that would be visible within scrollframe at that position. You don’t need to have thousands of UI elements.

No, I am understanding what you are saying, you are not understanding what I am wanting though. This has nothing to do with updating based on scrolling frame position and what not. All the elements are created when you join once, so they out of the way, no performance issues. The issue is having to delete and recreate them every 5 minutes, I don’t want to do that

Why are you doing that? The client should only load in the amount that would fit within the visible window. When it receives an update, it can update the specific visible frames with the correct data. There wouldn’t be any issues as you’re only destroying or creating maybe 5 or 10 frames every 5 minutes, plus whenever they scroll.

I’m looking for a fix that’ll take me 15 minutes to add. Not something that’d require an entire rewrite and having to figure out all the math behind it

The math isn’t complicated. Regardless, I’m not aware of any magic bullet that would help you. Maybe somebody else has an easier idea. Or you can wait for Roblox to eventually do their UI overhaul, but I doubt they’ll be doing that anytime soon.

Can’t really provide any scripts with what you’ve given but what you would do is iterate over the OrderedDataStore data fetched from “:GetSortedAsync()” and do the following.

local Players = game:GetService("Players")
local DataStores = game:GetService("DataStoreService")
local DataStore = DataStores:GetOrderedDataStore("DataStore")

local Gui = script.Parent

local function PopulateGui()
	local Pages = DataStore:GetSortedAsync() --Returns a pages object.
	local Page = Pages:GetCurrentPage() --Get the current (first) page.
	for Index, Data in ipairs(Page) do --Page is the data returned by GetSortedAsync.
		local UserId = Data.key --Assume the key field is the UserId.
		local Username = Players:GetNameFromUserIdAsync(tonumber(UserId)) --Remember that DataStore keys are string values.
		local Value = Data.value --Assume the value field is the value.
		local Frame = Gui:FindFirstChild("Frame"..Index) --Incrementing naming scheme required.
		if Frame then
			local UserIdLabel = Frame:FindFirstChild("UserIdLabel"..Index)
			if UserIdLabel then
				UserIdLabel.Text = UserId
			end
			
			local UsernameLabel = Frame:FindFirstChild("UsernameLabel"..Index)
			if UsernameLabel then
				UsernameLabel.Text = Username
			end
			
			local ValueLabel = Frame:FindFirstChild("ValueLabel"..Index)
			if ValueLabel then
				ValueLabel.Text = tostring(Value) --Likely to be a number/integer value.
			end
			
			local IconLabel = Frame:FindFirstChild("IconLabel"..Index) --Player icon.
			if IconLabel then
				IconLabel.Image = Players:GetUserThumbnailAsync(tonumber(UserId), Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size420x420)
			end
		end
	end
end

Hopefully I’ve added enough comments so everything should make sense, I was under the impression that the gui was a “SurfaceGui” instance so everything can be easily contained in a single server script.

The trick is to just name the GuiObjects in a way such that they can be easily referenced while iterating over the data returned by “:GetSortedAsync()”.

This way you aren’t recreating the same instances over and over again, you’re just referencing the already existing instances and updating their properties accordingly.

I don’t have a problem with creating the elements. My leaderboards all work as intended. My only issue is I want to ADD a way to have my leaderboards update every 5 minutes. As I said, they only load on start. They do not update.

I was misled by the following:

(I have 5 leaderboards, 4 of which have daily/weekly/alltime, therefore 13 leaderboards need 50 players listed, 650 frames created every 5 minutes)

I thought this was the current system and you were attempting to make it less resource intensive.

Create all the mainframes with a template once, then keep changing that template after every load.

You only create the frames once, then just change each property when you update, maybe set the text to Loading... or something while doing so.

That way you create 650 frames once. Not every 5 minutes.

@MightyDantheman has put you on the right track, you don’t need 650 frames, you only need the frames that are visible to the user to be updated periodically.

I don’t want to reiterate over already existing DevForum posts, so if you intend to take that route (which is the most optimal), you can find it here: How do I optimize a scrolling frame for over 1000 frames?. The post linked covers all the math you will need.

I understand you want a solution that takes 15 minutes to add, although I’d still suggest what Dan has given you, as it solves your problem with performance and scales easier if you ever intend to have more than 650 frames.


I’d like to further note that if you want a less optimal way to extend your current solution, you could have a list of all UI elements in a table. This allows you to add people by pushing the indices downwards and inserting an index somewhere in between with that user.

You wouldn’t need to create new UI elements, only once, and all you would need to ultimately do is update the UI elements (as in just the text.)

Although this solution isn’t ideal as iirc Roblox still renders anything that isn’t visible in scrolling frames so in theory 650 text labels updating at once could still cause frame drops which is why only updating visible frames is still your best choice. Plus you’d still be rendering 650 frames (or more if you extend the amount of users displayed) regardless of visibility.

1 Like