Greetings! I’m delighted to present my first tutorial on this platform. In this session, we’ll be creating a top donators leaderboard that is not only visually appealing but also fully functional. By the end of this tutorial, you’ll be able to confidently design a leaderboard that showcases the most generous contributors to your cause.
What we’re going to learn:
- Datastore
- Players Service
- Particles
- BadgeService
Step 1: Making the leaderboard
Insert a part into your workspace, and insert a SurfaceGui into the part. Insert a frame as the MainFrame/root. Set the size to {1, 0}, {1, 0}
, transparency to 1. Now we’re gonna make the donator list as a ScrollingFrame and set the size to {1, 0}, {1, 0}
, and maybe make it a bit transparent. Lastly we’re gonna make the leaderboard title so players know what they’re looking at
(not necessarily needed script-wise)
Leaderboard.rbxm (11.8 KB)
Step 2: Where the magic begins
Before we go into scripting we first need to make the donator frame as a way to visualize the donators
Donator.rbxm (8.1 KB)
Alright, now for the scripting part. Insert a script into the MainFrame and parent the donator frame you just created in the script.
-- services
local DatastoreService = game:GetService('DataStoreService')
local Players = game:GetService('Players')
-- the datastore
local DonatorsDatastore = DatastoreService:GetOrderedDataStore('Donators')
-- the MainFrame
local MainFrame = script.Parent
-- the Donators list
local Donators = MainFrame.Donators
-- how often the leaderboard refreshes (in seconds)
local refreshInterval = 30
-- will only show # donators in the leaderboard
local donatorsShown = 100
local function refresh()
-- get the donators
local pages: DataStorePages = DonatorsDatastore:GetSortedAsync(false, donatorsShown, 1, 2^63)
-- clear the previous frames
for _, v in Donators:GetChildren() do
if v:IsA('Frame') then
v:Destroy()
end
end
-- loop over the donators
-- rank is a range from 1 to #donatorsShown
-- data is a dictionary/table containing the userId and the amount donated
for rank, data in pages:GetCurrentPage() do
local userId = data.key
local amount = data.value
-- get the name based on the userId
local name = Players:GetNameFromUserIdAsync(userId)
-- get the headshot profile image of the donator
local profile = Players:GetUserThumbnailAsync(userId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size48x48)
-- clone the donator frame
local clone = script.Donator:Clone()
clone.Username.Text = name
clone.Profile.Image = profile
clone.Robux.Text = amount
clone.LayoutOrder = rank
-- set the donator frame parent into the donators list
clone.Parent = Donators
end
end
-- main loop
while true do
refresh()
task.wait(refreshInterval)
end
:GetSortedAsync
will return a DataStorePages
which contains sorted individual Page
, which we can use it to get the key (userId) and the value (amount). Here is a visual example of how the datastore works
{
page1 = {
[1] = {[68174910418] = 100};
[2] = {[18146901461] = 76};
[3] = {[5151819109] = 75};
}
}
Step 3: Donating logic
I’m not gonna explain how to create the gui needed to donate, but if you already know how to do it, you can skip this part. Download and insert the file into StarterGui
DonationGui.rbxm (11.3 KB)
After that you can make the donation amounts as Developer Products
in the Monetization tab in the Game Settings
Set the DonationFrame and the TextButtons visibility to true and set the amount & name of each button to match your products
Insert a RemoteEvent in ReplicatedStorage called “PromptDonation”. Create a badge to show donators apart. Insert a script in ServerScriptService and insert a part with ParticleEmitters for a little show
Emitter.rbxm (4.3 KB)
with badge:
-- services
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local MarketplaceService = game:GetService('MarketplaceService')
local BadgeService = game:GetService('BadgeService')
local DatastoreService = game:GetService('DataStoreService')
local Players = game:GetService('Players')
local Debris = game:GetService('Debris')
-- the datastore
local DonatorsDatastore = DatastoreService:GetOrderedDataStore('Donators')
-- the badgeId to show donators apart from regular players
local badgeId = 2129510374
--[[
format:
```
[productId] = {
Name = buttonName;
Price = donationAmount;
};
```
]]
local Products = {
[1338470979] = {
Name = 'Small';
Price = 50;
};
[1338471308] = {
Name = 'Big';
Price = 100;
};
[1338471406] = {
Name = 'Plenty';
Price = 300;
};
[1338471485] = {
Name = 'Huge';
Price = 1000;
};
}
ReplicatedStorage.PromptDonation.OnServerEvent:Connect(function(player: Player, donation: string)
local product
for id, v in Products do
if v.Name == donation then
product = id
break
end
end
if not product then return end
MarketplaceService:PromptProductPurchase(player, product, true, Enum.CurrencyType.Robux)
end)
MarketplaceService.PromptProductPurchaseFinished:Connect(function(userId: number, productId: number, purchased: boolean)
-- get the player by userId
local player = Players:GetPlayerByUserId(userId)
-- check if the player purchased it or not
if purchased then
-- get the price/amount
local price = Products[productId].Price
-- award the badge to show gratitude towards them
pcall(BadgeService.AwardBadge, BadgeService, userId, badgeId)
-- increment the donator's amount
pcall(DonatorsDatastore.IncrementAsync, DonatorsDatastore, userId, price)
-- get the cframe to position the emitter
local position = player.Character.HumanoidRootPart.Position - Vector3.new(0, player.Character.Humanoid.HipHeight, 0)
local cf = CFrame.new(position)
-- clone and show the emitter
local clone = script.Emitter:Clone()
clone:PivotTo(cf)
clone.Parent = workspace
-- destroy the emitter after 10 seconds
Debris:AddItem(clone, 10)
end
end)
without badge:
-- services
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local MarketplaceService = game:GetService('MarketplaceService')
local DatastoreService = game:GetService('DataStoreService')
local Players = game:GetService('Players')
local Debris = game:GetService('Debris')
-- the datastore
local DonatorsDatastore = DatastoreService:GetOrderedDataStore('Donators')
--[[
format:
```
[productId] = {
Name = buttonName;
Price = donationAmount;
};
```
]]
local Products = {
[1338470979] = {
Name = 'Small';
Price = 50;
};
[1338471308] = {
Name = 'Big';
Price = 100;
};
[1338471406] = {
Name = 'Plenty';
Price = 300;
};
[1338471485] = {
Name = 'Huge';
Price = 1000;
};
}
ReplicatedStorage.PromptDonation.OnServerEvent:Connect(function(player: Player, donation: string)
local product
for id, v in Products do
if v.Name == donation then
product = id
break
end
end
if not product then return end
MarketplaceService:PromptProductPurchase(player, product, true, Enum.CurrencyType.Robux)
end)
MarketplaceService.PromptProductPurchaseFinished:Connect(function(userId: number, productId: number, purchased: boolean)
-- get the player by userId
local player = Players:GetPlayerByUserId(userId)
-- check if the player purchased it or not
if purchased then
-- get the price/amount
local price = Products[productId].Price
-- increment the donator's amount
pcall(DonatorsDatastore.IncrementAsync, DonatorsDatastore, userId, price)
-- get the cframe to position the emitter
local position = player.Character.HumanoidRootPart.Position - Vector3.new(0, player.Character.Humanoid.HipHeight, 0)
local cf = CFrame.new(position)
-- clone and show the emitter
local clone = script.Emitter:Clone()
clone:PivotTo(cf)
clone.Parent = workspace
-- destroy the emitter after 10 seconds
Debris:AddItem(clone, 10)
end
end)
Final Step: Testing
Congratulations, you’ve completed all the necessary steps to set up your donation system and leaderboard! Before you launch your system to the public, it’s crucial to ensure that everything is functioning correctly.
To do so, we recommend testing the donating system and leaderboard thoroughly. You can start by running several test transactions to ensure that donations are being processed correctly and that they are reflected on the leaderboard.
Once you’re confident that everything is working smoothly, you can finally launch your donation system and start accepting contributions from your community. Thank you for following this tutorial, and we wish you the best of luck in your fundraising efforts!
Remember, the key to success is to stay committed to your cause and continue to engage with your donors regularly. With a functional donation system and a visually appealing leaderboard, you’ll be well on your way to achieving your fundraising goals. Goodbye, and best of luck!
If your leaderboard doesn’t work, don’t hesitate to reach out to me; I will try to debug the problem as quickly as possible
some code snippets
remove test donations from the board:
game:GetService('DataStoreService'):GetOrderedDataStore('Donators'):RemoveAsync(userId)
set donations manually to the board:
game:GetService('DataStoreService'):GetOrderedDataStore('Donators'):SetAsync(userId, value)
(paste one of the snippet into the console in game or studio and replace the values)
e.g:
game:GetService('DataStoreService'):GetOrderedDataStore('Donators'):SetAsync(1, 50)