Pular para o conteúdo
PTEntrar

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ê querStorePor quê
Top 10 de todos os temposGetOrderedDataStore, uma chaveLeitura ordenada é a única coisa que ele faz
Top 10 da semanaUm store ordenado por semana (..._w2026W41)Uma janela é uma chave nova, nunca um filtro
A posição do próprio jogadorUma leitura da chave dele mais a página do topoPosição exata para todos não é recurso de DataStore
Top por algo que não é númeroDataStore normal mais a sua ordenaçãoStore 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

Ultimate Script Pack

Ultimate Script Pack

US$ 40Ver o pack

Quero ser avisado

Em breve

Deixe seu e-mail e a gente te avisa na hora em que este pack sair.