Como fazer um ranking global no Roblox?
Use um OrderedDataStore, grave a pontuação quando ela muda e leia o topo com GetSortedAsync num cronômetro — uma vez por minuto para o servidor inteiro, não uma por jogador. Guarde a página em memória, mande para os clientes e deixe os nomes fora do store: busque pelo id do usuário.
Em resumo
- OrderedDataStore guarda só números. Os nomes ficam em outro lugar.
- Leia num cronômetro do servidor, nunca na entrada de cada jogador.
- Guarde a página em memória; todo cliente lê a mesma lista.
Ranking é um problema de leitura, não de escrita. Gravar um número por jogador é barato. Ler o top dez é a parte que faz um jogo ser limitado, porque a implementação óbvia lê uma vez por jogador por entrada — e num servidor cheio são cinquenta leituras de uma lista que é a mesma para todos.
Passo a passo
1. Grave a pontuação onde dá para ordenar
Um OrderedDataStore guarda inteiros e mais nada. É essa a restrição que desenha o resto.
local DataStoreService = game:GetService("DataStoreService")
local board = DataStoreService:GetOrderedDataStore("Coins_AllTime_v1")
local function publicarPontos(userId: number, pontos: number)
-- Só inteiros: um float é recusado, e um negativo também, num ranking que
-- você vai ler em ordem decrescente.
local valor = math.max(0, math.floor(pontos))
local ok, err = pcall(function()
board:SetAsync(tostring(userId), valor)
end)
if not ok then
warn(`[ranking] falhou gravar {userId}: {err}`)
end
end
2. Leia o topo num cronômetro, uma vez para o servidor
Uma leitura, guardada, compartilhada. O tamanho da página é o que você mostra; pedir cem para exibir dez são cem linhas que você paga.
local Players = game:GetService("Players")
local TOPO = 10
local INTERVALO = 60
local guardado: { { userId: number, score: number, name: string } } = {}
local function atualizar()
local ok, paginas = pcall(function()
return board:GetSortedAsync(false, TOPO) -- false = decrescente
end)
if not ok then return end
local linhas = {}
for posicao, entrada in paginas:GetCurrentPage() do
local userId = tonumber(entrada.key)
if userId then
-- O nome NÃO está no store: ele muda, e guardá-lo faz uma troca de
-- nome aparecer errada para sempre.
local nomeOk, nome = pcall(function()
return Players:GetNameFromUserIdAsync(userId)
end)
linhas[posicao] = {
userId = userId,
score = entrada.value,
name = nomeOk and nome or `Usuário {userId}`,
}
end
end
guardado = linhas
end
3. Mande a página guardada
Os clientes nunca leem o store. Eles recebem a mesma lista que o servidor já tem.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local feed = Instance.new("RemoteEvent")
feed.Name = "Leaderboard"
feed.Parent = ReplicatedStorage
task.spawn(function()
while true do
atualizar()
feed:FireAllClients(guardado)
task.wait(INTERVALO)
end
end)
-- Quem entra entre duas atualizações recebe o que está guardado, não uma
-- leitura nova.
Players.PlayerAdded:Connect(function(player: Player)
task.defer(function()
feed:FireClient(player, guardado)
end)
end)
4. Desenhe
Dez linhas, o número primeiro. Ranking em que a pontuação é menor que o nome é ranking que ninguém lê.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local feed = ReplicatedStorage:WaitForChild("Leaderboard")
local lista = script.Parent:WaitForChild("Rows") -- um Frame com UIListLayout
local modelo = script.Parent:WaitForChild("RowTemplate")
feed.OnClientEvent:Connect(function(linhas)
lista:ClearAllChildren()
for posicao, linha in linhas do
local item = modelo:Clone()
item.Name = string.format("%02d", posicao)
item.Rank.Text = `#{posicao}`
item.PlayerName.Text = linha.name
item.Score.Text = string.format("%d", linha.score)
item.Visible = true
item.Parent = lista
end
end)
Qual store para qual ranking
| O que você quer | Store | Por quê |
|---|---|---|
| Top 10 de todos os tempos | GetOrderedDataStore, uma chave | Leitura ordenada é a única coisa que ele faz |
| Top 10 da semana | Um store ordenado por semana (..._w2026W41) | Uma janela é uma chave nova, nunca um filtro |
| A posição do próprio jogador | Uma leitura da chave dele mais a página do topo | Posição exata para todos não é recurso de DataStore |
| Top por algo que não é número | DataStore normal mais a sua ordenação | Store ordenado guarda só inteiros |
Perguntas
Por que o meu ranking fica vazio no Studio?
O Studio só alcança DataStores com "Enable Studio Access to API Services" ligado em Game Settings, e um OrderedDataStore no Studio começa vazio porque é o store de verdade e nada nele foi escrito por este place ainda. Publique uma vez, jogue no jogo real, e ele enche.
Como fazer um ranking semanal?
Ponha a janela na chave: Coins_2026W41. Na segunda o nome muda, o store antigo guarda a semana passada e o novo começa vazio. Derivar a chave de os.date("!*t") evita que alguém precise lembrar de rodar isso.
Posso guardar o nome do jogador no store?
Pode, mas não guarde. Nomes mudam, e o store continuaria mostrando o antigo. GetNameFromUserIdAsync é uma busca barata e correta — embrulhe num pcall, porque é um serviço de outra pessoa.
Com que frequência posso gravar?
Pense num orçamento por servidor, não por jogador: cerca de 60 gravações por minuto. Grave quando a pontuação realmente muda e na saída, nunca a cada moeda pega.
Atualizado: Testado no Studio: Roblox Studio, outubro de 2026Autor: GustavoLicença: O código deste guia pode ser usado em qualquer experiência do Roblox, inclusive paga
Pack relacionado
