How to make a simple global leaderboard

#1

Global leaderboards are great for adding competition to a game. Players want to become the best of them all, and will work hard to compete to become the best. If you make your leaderboard look nice, players will do anything to be better than the rest.


Taken from my game, The Tower Tycoon
Just look how hard people worked just to have a number and their name displayed on a virtual board.


Several Notes:

For this tutorial, it is recommended you know the basics of manipulating parts in studio and making a surface gui .

This tutorial is based off of pointsService for simplicity sake. This can be changed. For more info, check the FAQ at the bottom once you have read through. For now, don’t worry about it.

I don't want to read the tutorial, just give me the leaderboard

Don’t want to make the physical leaderboard and just want to code it? Skip to the coding section (has download for basic leaderboard without script). Don’t want to do that either? Download for the whole thing is at the end. (FAQ is available there too)


Let’s Begin!

With that out of the way, welcome. Today I will be showing you how to make a nice global leaderboard to let your players compete to become the best.


Surface GUI designing

  1. The first step to making a global leaderboard is to make the physical part it will be on. It can be any size, and you can add cylinders to create a rounded leaderboard, change colors, add transparency, and more. For this tutorial however, we will use a simple 6x9x1 part. It is important to make the leaderboard look nice and appealing, but this can be done later. For now, let’s keep it simple.
    image

  2. The second step is we want to make a Surface GUI to hold our leaderboard. You can do this by clicking the + icon on the part, and typing in Surface GUI.
    image

  3. Now that we have a Surface GUI, we want to add a scrolling frame to hold the important stuff. Name it “ScrollingFrame”, so we can reference it later. Set the size to give a bit of space at the top, as we want to have a header at the top. Style it to your liking, such as color, ect. I recommend setting scrollBarThickness to 20.

    image

  4. Add a UIListLayout to the Scrolling Frame. Name it “UI”, and set SortOrder to LayoutOrder.image image

  5. There’s one more thing we need to do before we get into the code; We need a frame to display the information. Make a sample frame such as the below one (Called “Sample”), and have imageLabel named “Image”, and 3 textLabels named “Place”, “PName”, and “Value”. Parent “Place” to the Image, as it will make it easier to scale (Scale & Pos = {0.5, 0},{0.5, 0}).
    image image

  6. We now have a Surface GUI that’s ready to be scripted! Add a script (not Local Script) to the surface gui. Parent the frame “Sample”.
    image


It’s Coding Time!

Time to code! At this point, you should have a leaderboard like this: Leaderboard Without Script.rbxl (21.7 KB).

We need to define all of our variables first, so we can easily reference items. (You can make your variable names longer, but I prefer shorter names. If you can remember them, use them.)

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

Nest, we need to choose the name for our global Ordered Data Store and set it to a variable. This will allow us to use Roblox to sort the data for us, without having to worry about thousands of entries to sort through. Note: Changing the key will change were roblox fetches our data from. This is useful for having multiple leaderboards, or just reseting data.

local dataStoreService = game:GetService("DataStoreService")
--The data store service
local dataStore = dataStoreService:GetOrderedDataStore("Leaderboard")
--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!
I want daily leaderboards!

Not all people want all-time leaderboards. To acheive daily leaderboards, simply create a unique key for each day using os.date()

local dateTable = os.date("*t", os.time())
local key = "Y:"..dateTable["year"].." M:"..dateTable["month"].." D:"..dateTable["day"]
--Unique key for each day

Now, start a loop to update data and fetch data.

wait(5)
while true do
--will add wait(120) at the end
--loop that runs every 2 minutes	

Then, we need to update the data. We loops through all the players, and update their data. In this tutorial, we use player points as the data to store (How To Award Player Points). However, you can easily reroute this to feature and value by defining w as something else.

	for i,plr in pairs(game.Players:GetChildren()) do--Loop through players
		if plr.UserId>0 then--Prevent errors
            local ps = game:GetService("PointsService")--PointsService
			local w = ps:GetGamePointBalance(plr.UserId)--Get point balance
			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

Now, we need to fetch our data. We can do this by using our dataStore we defined earlier.

    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

Yay! We now have our data, nice and organized with it’s key (userid), and value (points). But wait, we don’t have the player’s image or username! We need to fetch those before displaying our data.

   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

Our data is all organized and tidy! Now, we want to take this tidy data and display it! (username, points, image)

	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(1,1,0)--1st place color
		elseif number == 2 then
			color = Color3.new(0.9,0.9,0.9)--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

Our full code masterpiece!

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("Leaderboard")
--Get the data store with key "Leaderboard"

wait(10)
while true do
	for i,plr in pairs(game.Players:GetChildren()) do--Loop through players
		if plr.UserId>0 then--Prevent errors
			local ps = game:GetService("PointsService")--PointsService
			local w = ps:GetGamePointBalance(plr.UserId)--Get point balance
			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(1,1,0)--1st place color
		elseif number == 2 then
			color = Color3.new(0.9,0.9,0.9)--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
I want the top bar from the first photo!

For those wanting a top bar, you can duplicate the sample frame and put it outside of the scrolling frame. Remember, you can mess with colors and text, and make a really cool leaderboard.


FAQ (Frequently Asked Questions)

How do I make a daily, monthly, or yearly leaderboard?

To make leaderboards “Reset” we can simply change where we save and get the data from. os.time() gives us the number of seconds since the unix equinox. os.date(time) convertes those seconds into a date.

To acheive daily leaderboards, simply create a unique key for each day using os.date()

local dateTable = os.date("*t", os.time())
local key = "Y:"..dateTable["year"].." M:"..dateTable["month"].." D:"..dateTable["day"]
--Unique key for each day

To acheive monthly leaderboards, simply create a unique key for each month using os.date()

local dateTable = os.date("*t", os.time())
local key = "Y:"..dateTable["year"].." M:"..dateTable["month"]
--Unique key for each month

To acheive yearly leaderboards, simply create a unique key for each year using os.date()

local dateTable = os.date("*t", os.time())
local key = "Y:"..dateTable["year"]
--Unique key for each year
Why only pointsService? How can I use it without pointsService?

I used pointsService in this tutorial for simplicity sake. If you want to pull the value from somewhere, you can easily reroute the value by defining w as something else.

In the example below, we set w to the player’s cash leaderstat. plr is the player, so reroute w however you like.

	for i,plr in pairs(game.Players:GetChildren()) do--Loop through players
		if plr.UserId>0 then--Prevent errors
			local w = plr.leaderstats.Cash.Value--Get 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
I can't scroll on my leaderboard! How do I fix it?

If you put a part inside of the leaderboard, it might prevent you from scrolling. To prevent this, move the part that has the Surface GUI FORWARD. This way, the scrolling frame is over the part, which originally was blocking the scrolling frame.


Thanks for reading!

Thanks for reading! (and hopefully following along, thought it’s totally fine if you just wanted the leaderboard) If there is anything you are confused about, or think I missed, let me know. Feel free to PM me, or reply below. I can help solve your problem!


Downloads

Here all all the respective downloads for each of the stages. Feel free to use them as you please.

Model without code: Leaderboard Without Script.rbxl (21.7 KB).

Full Model: https://www.roblox.com/catalog/2992458938
Downloadable Full Model: Global Leaderboard With Script.rbxm (12.5 KB)

Un-copylocked Working DEMO: https://www.roblox.com/games/2992413931/Leaderboard-with-script
Downloadable Working DEMO: Leaderboard With Script Working DEMO.rbxl (24.7 KB)

Happy Coding

48 Likes
How to script a Global Leaderboard
Global Leaderboard of Race Times
OrderedDataStores uses and documentation?
closed #2

This topic was automatically closed after 8 minutes. New replies are no longer allowed.

opened #3
1 Like
#4

Thanks for this thread! I just started coding 2 days ago and tried some small stuff. I think I would try this one out. Amazing tutorial!

5 Likes
#5

Glad you found it useful! If you have any questions, feel free to DM them to me.

2 Likes
Leaderboard which loads the characters Roblox profile icon
#6

Wow this is really nice. Good job! How long did it take you to write this?

1 Like
#7

I’ve already made countless leaderboards, the problem was trying to make it easy to understand. It took me about 2 hours to get the images and text done. Glad you found it useful.

4 Likes
#8

This is an amazing tutorial on how to make a global leaderboard! I have a question. Why are you using PointsService to handle the ranking? Isn’t it deprecated? (Sorry, about this because you wrote in your post to PM you if there are any questions)

#9

PointsService doesn’t handle the ranking. It’s just the score you can have. You can change it, for example:
local w = player.leaderstats.Points.Value

1 Like
#10

Yes, it is depreciated. I wanted to use something simple so I did not have to go through how data stores work ect.

Change this line of code:

	for i,plr in pairs(game.Players:GetChildren()) do--Loop through players
		if plr.UserId>0 then--Prevent errors
			local ps = game:GetService("PointsService")--PointsService
>>>>>>>>>>>	local w = ps:GetGamePointBalance(plr.UserId)--Get point balance
			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

If cash was a leaderstat for players, and you wanted to make a leaderboard for it:

local w = plr.leaderstats.Cash.Value

Note: Also remove were pointsService is defined if you do not need it.

If you have more questions, feel free to DM me.

2 Likes
How to script a Global Leaderboard
#11

Please help me, I want the list to come out in order but they only come out cluttered, I’ve put “true” where it says that the small numbers appear first but it does not work, they keep coming out just as messy

#12

I have no idea how this went 2 months without anyone saying anything. Change these lines:

local new = sample:Clone()
new.Name = number
--Add this line:
new.LayoutOrder = tonumber(number)

Then, go to ScrollingFrame.UI and change ScrollingFrame.UI.SortOrder to ‘LayoutOrder’.

Roblox’s name ordering system puts 10 after 1. This is why I should of used LayoutOrder.

This should be fixed in the original model now, so you can sort backwards.