How to make a simple global leaderboard

@ThatTimothy Do you have any idea on how I can achieve something similar to the circled part of this leaderboard?
Screenshot 2021-03-28 135611

3 Likes

It’s just different ui styling. If the position is equal to 1, then use a different sample frame. This can be customized however you want, with different layouts like the one above.

3 Likes

Since my leaderstat value is a decimal, how would I make it work with that? Will it work the same if I use :GetDataStore instead of :GetOrderedDataStore?

3 Likes

OrderedDataStores only accept integers, which means that you would need to use math.floor or math.ceil (documentation for that can be found here) in order to convert the decimal into a whole number.

You wouldn’t be able to use :GetDataStore for this kind of leaderboard because the values are not sorted in regular DataStores.

2 Likes

I tried them both but the value on the leaderboard is still nil.

(Error)

Could you provide the code you’re using?

The OrderedDataStore can only store numerical integers, so you need to make sure you’re saving valid values.

Several solutions:

local someValue = 931.3819199293
--Just make it an integer
math.floor(someValue) --> 931
local someValue = 931.3819199293
--Store a certain number of decimal places:
local storedValue = math.floor( someValue * 1000 ) --> 931381
--And then convert is back when displaying:
storedValue / 1000 --> 931.381
3 Likes

Thanks very useful it helps a lot of new developers like me :slight_smile:

1 Like

How do I make it so that it shows people who are not in the server?

That’s the entire point of it being a global leaderboard. Please explain what you mean?

1 Like

Ignore what I wrote I just realised how it works lol sorry for bothering you.

1 Like

I realized that the issue I was having was with

I printed b.Value and it came as nil.
How do I make it the actual value?

Whole script
local sg = script.Parent
local sample = script:WaitForChild("Sample")
local sf = sg:WaitForChild("ScrollingFrame")
local ui = sf:WaitForChild("UI")

local dataStoreService = game:GetService("DataStoreService")
local dataStore = dataStoreService:GetOrderedDataStore("Leaderboard")

wait(5)
while true do
	for i,plr in pairs(game.Players:GetPlayers()) do
		if plr.UserId>0 then
            local w = plr.leaderstats.Highscore.Value
			if w then
				pcall(function()
					dataStore:UpdateAsync(plr.UserId,function(oldVal)
						return tonumber(w)
					end)
				end)
			end
		end
	end    
	local smallestFirst = false
    local numberToShow = 10
    local minValue = 1
    local maxValue = 10e30
    local pages = dataStore:GetSortedAsync(smallestFirst, numberToShow, minValue, maxValue)
    local top = pages:GetCurrentPage()
	local data = {}
	for a,b in ipairs(top) do
		local userid = b.key
		local highscore = b.value
		local username = "[Failed To Load]"
		local s,e = pcall(function()
		 username = game.Players:GetNameFromUserIdAsync(userid)
		end)
		if not s then
		   warn("Error getting name for "..userid..". Error: "..e)
		end
		local image = game.Players:GetUserThumbnailAsync(userid, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size150x150)
		table.insert(data,{username,highscore,image})
	end
	ui.Parent = script
	sf:ClearAllChildren()
	ui.Parent = sf
	for number,d in pairs(data) do
		local name = d[1]
		local val = d[2]
		local image = d[3]
		local color = Color3.new(1,1,1)
		if number == 1 then
			color = Color3.new(1,1,0)
		elseif number == 2 then
			color = Color3.new(0.9,0.9,0.9)
		elseif number == 3 then
			color = Color3.fromRGB(166, 112, 0)
		end
		local new = sample:Clone()
		new.Name = name
        new.LayoutOrder = number
		new.Image.Image = image
		new.Image.Place.Text = number
		new.Image.Place.TextColor3 = color
		new.PName.Text = name
		new.Value.Text = val
		new.Value.TextColor3 = color
		new.PName.TextColor3 = color
		new.Parent = sf
	end
	wait()
	sf.CanvasSize = UDim2.new(0,0,0,ui.AbsoluteContentSize.Y)
        wait(120)
end

That’s weird. b.value should never be nil, regardless if data is stored are not. Make sure you are using b.value and not b.Value.

Could you show the code you’re using to print the value?

In your example,
isn’t

The same as 931*1000 (= 931000)
How is it

?

Lol I was using b.Value it worked when I changed it.

I have another question, will it work the same if I change the wait(120) to:

for i = 120,1,-1 do
    updateTimer.Text = "Updating in "..i.." seconds"
    wait(1)
end

Just so it tells the player when it will update?

1 Like

Yes, as long as the total wait is long enough. Experiment around with it, depending on the game lower intervals can work. However, I tend to suggest keeping it above 2 minutes because saving a player’s data should be more important than updating the leaderboard.

1 Like

I clarified the example to make it clearer, there are two solutions, one which just floors the value, and one which saves a few decimal places by storing the decimal places as an integer. The original example didn’t have a separation between the two, which I can see why that would be confusing.

1 Like

I have another problem. @ThatTimothy


It displays the info of the top 3 people but after that it only shows the image of the other players and nothing else.
local sg = script.Parent --Surface GUI
local sample = script:WaitForChild(“Sample”) --Our Sample frame
local sf = sg:WaitForChild(“ScrollingFrame”) --The scrolling frame
local ui = sf:WaitForChild(“UI”) --The UI list layout

local dataStoreService = game:GetService("DataStoreService")
--The data store service
local dataStore = dataStoreService:GetOrderedDataStore("TotalTime")
--Get the data store with key "Leaderboard"
--Want to make different leaderboards?
--Change this key to a different string ("Leaderboard2", ect.), and you can have multiple leaderboards!

wait(5)
while true do
	--will add wait(120) at the end
	--loop that runs every 2 minutes
	for i,plr in pairs(game.Players:GetChildren()) do--Loop through players
		if plr.UserId>0 then--Prevent errors
			local w = plr.leaderstats["Time Played"].Value
			if w then
				pcall(function()
					--Wrap in a pcall so if Roblox is down, it won't error and break.
					dataStore:UpdateAsync(plr.UserId,function(oldVal)
						--Set new value
						return tonumber(w)
					end)
				end)
			end
		end
	end
	
	
	local smallestFirst = false--false = 2 before 1, true = 1 before 2
	local numberToShow = 100--Any number between 1-100, how many will be shown
	local minValue = 1--Any numbers lower than this will be excluded
	local maxValue = 10e30--(10^30), any numbers higher than this will be excluded
	local pages = dataStore:GetSortedAsync(smallestFirst, numberToShow, minValue, maxValue)
	--Get data
	local top = pages:GetCurrentPage()--Get the first page	
	
	local data = {}--Store new data
	for a,b in ipairs(top) do--Loop through data
		local userid = b.key--User id
		local points = b.value--Points
		local username = "[Failed To Load]"--If it fails, we let them know
		local s,e = pcall(function()
			username = game.Players:GetNameFromUserIdAsync(userid)--Get username
		end)
		if not s then--Something went wrong
			warn("Error getting name for "..userid..". Error: "..e)
		end
		local image = game.Players:GetUserThumbnailAsync(userid, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size150x150)
		--Make a image of them
		table.insert(data,{username,points,image})--Put new data in new table
	end
	
	ui.Parent = script
	sf:ClearAllChildren()--Remove old frames
	ui.Parent = sf
	for number,d in pairs(data) do--Loop through our new data
		local name = d[1]
		local val = d[2]
		local image = d[3]
		local color = Color3.new(1,1,1)--Default color
		if number == 1 then
			color = Color3.new(.8,.6,0)--1st place color
		elseif number == 2 then
			color = Color3.new(0.7,0.8,0.6)--2nd place color
		elseif number == 3 then
			color = Color3.fromRGB(166, 112, 0)--3rd place color
		end
		local new = sample:Clone()--Make a clone of the sample frame
		new.Name = name--Set name for better recognition and debugging
		new.LayoutOrder = number--UIListLayout uses this to sort in the correct order
		new.Image.Image = image--Set the image
		new.Image.Place.Text = number--Set the place
		new.Image.Place.TextColor3 = color--Set the place color (Gold = 1st)
		new.PName.Text = name--Set the username
		new.Value.Text = val--Set the amount of points
		new.Value.TextColor3 = color--Set the place color (Gold = 1st)
		new.PName.TextColor3 = color--Set the place color (Gold = 1st)
		new.Parent = sf--Parent to scrolling frame
	end
	wait()
	sf.CanvasSize = UDim2.new(0,0,0,ui.AbsoluteContentSize.Y)
	--Give enough room for the frames to sit in
	wait(120)
end--End the while loop

Here is the script.

1 Like

If you’re using the same code, you have to update it if you change the hierarchy of your text labels. Make sure you check the output for more information, as there likely is an error.

For an example of how you should setup the ui, see the OP. There is also a download and demo for more information.

1 Like

Are you talking about the variables?

the error is occurring here?

If you’ve changed the layout of the ui elements, then you have to modify the code to compensate for it.

If you dont understand what that means, you’ll want to use the copy provided in the demo that works and modify it from there.

If the text isn’t displaying, it’s either the same color as the background, or the script is referencing the wrong items. If it’s the later, you should see errors in your ouput.

I can’t help you much more with just an image. I’d need to know your object hierarchy as well.