Roblox Studioで全サーバー共通ランキングを作る方法
Ordered Data StoreへScoreを保存し、ゲーム内のボードへ全サーバー共通の上位10人を高い順で表示しよう。

ゲーム内のボードへ、すべてのサーバーを合わせたScore上位10人を表示する。完成すると、1300、900、500のように高い値から1位、2位、3位と並び、60秒ごとに更新される。完成までの目安は8分だ。Robuxは必要ない。

この記事は、ScoreをData Storeへ保存する記事の続きだ。Publish済みの専用テストゲームでleaderstats > ScoreとSaveScoreが動き、Studio Access to API Servicesを有効にした状態から始めよう。
Roblox公式のData Store解説にあるとおり、Studioからは同じゲームのクラウドデータへ接続する。公開中の本番ゲームではなく、前の記事で用意したテスト用ゲームを使おう。
1. 順位を表示するボードを作る
Stopした編集画面で、WorkspaceへPartを追加し、GlobalLeaderboardBoardへ名前を変える。Anchoredをオン、CanCollideをオフにし、Sizeを18, 12, 1へ設定しよう。
その右の+からSurfaceGui、その中へFrameを追加してPanelにする。Panelの中へ3つのTextLabelを追加し、Title、Status、Entriesへ名前を変える。区切り線が欲しい場合はDividerという細いFrameも追加してよい。
Workspace
└─ GlobalLeaderboardBoard (Part)
└─ SurfaceGui
└─ Panel (Frame)
├─ Title (TextLabel)
├─ Status (TextLabel)
├─ Divider (Frame)
└─ Entries (TextLabel)

SurfaceGuiはCanvasSize = 900, 600、AlwaysOnTopをオンにする。3つのTextLabelは背景を透明にし、文字を左寄せにして、次の値を入れよう。ボードの色やフォントは変えても、Scriptの動作には影響しない。
| 名前 | Text | Position | Size |
|---|---|---|---|
Title |
全サーバー共通ランキング |
40, 26 |
820, 64 |
Status |
更新待ち... |
42, 92 |
816, 34 |
Entries |
まだ記録がない |
48, 150 |
804, 408 |
表のPositionとSizeは、すべてOffsetのX、Yへ入れる。たとえばTitleのPositionはXへ40、Yへ26だ。Titleは黄色のGothamBold、Statusは水色、Entriesは白にすると、完成画像のように役割を見分けやすい。EntriesだけはTextYAlignment = Topにする。これで4人目以降も上から下へ追加される。
ボードへ10行が収まらない場合は、Entries.TextSizeを25前後へ下げよう。文字がボードの裏側に出た場合は、SurfaceGuiのFaceをFrontからBackへ変えるか、Partを180度回転する。SurfaceGuiが見えないままコードへ進むと、取得成功と表示失敗を区別できないため、まず更新待ち...が読めるところまで整えよう。
Tip — SurfaceGuiはPartの面へ貼るUI
SurfaceGuiは、画面に固定するScreenGuiと違い、Partの表面へ文字やボタンを表示する入れ物だ。今回はゲーム内に置いた1枚の掲示板として使う。
2. 退出時のScoreをランキングへ保存する
ServerScriptServiceへScriptを追加し、GlobalScoreWriterへ名前を変える。中身を次のコードへ置き換えよう。
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local rankingStore = DataStoreService:GetOrderedDataStore("GlobalScore_v1")
local lastSavedValue = {}
local function saveScore(player)
local leaderstats = player:FindFirstChild("leaderstats")
local score = leaderstats and leaderstats:FindFirstChild("Score")
if not score then
return
end
local userId = player.UserId
local value = score.Value
if lastSavedValue[userId] == value then
return
end
local success, saveError = pcall(function()
rankingStore:SetAsync(tostring(userId), value)
end)
if success then
lastSavedValue[userId] = value
print("Global score saved:", player.Name, value)
else
warn("Global score save failed:", player.Name, saveError)
end
end
Players.PlayerRemoving:Connect(saveScore)
game:BindToClose(function()
for _, player in Players:GetPlayers() do
saveScore(player)
end
end)
前の記事の標準Data Storeは、数値だけでなく文字やtableも保存できる。今回のGetOrderedDataStore("GlobalScore_v1")は、並び替え専用の別の保存場所だ。公式の保存方法と同じく、keyには文字列へ変えたUserId、valueには数値のScoreを保存する。
Tip — Ordered Data Storeは数値を並べられる
Ordered Data Storeは、保存した数値を大小の順で取り出せる保存場所だ。たとえば3人のScoreが500、1300、900なら、ランキング用に1300、900、500と並べられる。
lastSavedValueは、同じ終了処理で同じScoreを2回保存しないための記録だ。Data Storeへの通信は失敗することがあるため、SetAsync()はpcall()の中で実行する。
このScriptはプレイヤーの画面から送られた順位やScoreを使わない。サーバーにあるleaderstats > Scoreを読み、その値だけを保存する。ランキングの正本をサーバーへ置いておけば、LocalScriptから見た目だけを書き換えられても、ほかのプレイヤーの順位には混ざらない。
3. 上位10人を高い順で表示する
ServerScriptServiceへもう1つScriptを追加し、GlobalLeaderboardへ名前を変える。中身を次のコードへ置き換えよう。
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local rankingStore = DataStoreService:GetOrderedDataStore("GlobalScore_v1")
local panel = workspace
:WaitForChild("GlobalLeaderboardBoard")
:WaitForChild("SurfaceGui")
:WaitForChild("Panel")
local entriesLabel = panel:WaitForChild("Entries")
local statusLabel = panel:WaitForChild("Status")
local REFRESH_SECONDS = 60
local previousText = entriesLabel.Text
local function getDisplayName(userId)
local success, name = pcall(function()
return Players:GetNameFromUserIdAsync(userId)
end)
if success then
return name
end
return "User " .. userId
end
local function getTopTen()
local success, pagesOrError = pcall(function()
return rankingStore:GetSortedAsync(false, 10)
end)
if not success then
return nil, pagesOrError
end
local lines = {}
for rank, entry in ipairs(pagesOrError:GetCurrentPage()) do
local userId = tonumber(entry.key)
local name = userId and getDisplayName(userId) or entry.key
table.insert(lines, string.format(
"%d位 %-18s %d",
rank,
name,
entry.value
))
end
if #lines == 0 then
return "まだ記録がない"
end
return table.concat(lines, "\n")
end
local function refresh()
statusLabel.Text = "更新中..."
local newText, readError = getTopTen()
if newText then
previousText = newText
entriesLabel.Text = newText
statusLabel.Text = "全サーバー共通・60秒ごとに更新"
else
entriesLabel.Text = previousText
statusLabel.Text = "更新に失敗。前回の順位を表示中"
warn("Global leaderboard read failed:", readError)
end
end
while true do
refresh()
task.wait(REFRESH_SECONDS)
end
GetSortedAsync(false, 10)のfalseは大きい値から並べる指定、10は最初に受け取る件数だ。OrderedDataStoreの公式リファレンスでも、この組み合わせで上位10件を取得している。
Tip — 降順は大きい値から小さい値の順
Scoreの降順は、1300、900、500の順だ。
falseをtrueへ変えると小さい値から並ぶため、Scoreランキングではfalseのまま使おう。
UserIdはGetNameFromUserIdAsync()で名前へ変える。名前の取得だけ失敗した場合も順位全体を止めず、User 12345のような表示へ戻す。
GlobalScoreWriterとGlobalLeaderboardの両方で、保存場所の名前をGlobalScore_v1にそろえる。同じ名前を2つのScriptから開いてもデータが複製されるわけではなく、片方が保存し、もう片方が同じ保存場所を読む。どちらかをGlobalScores_v1のように変えると、ボード側には記録がない状態になる。
まだ10人が参加していないゲームでは、保存済みの人数だけ表示されればよい。3人なら1位から3位までで、空いている7行を作る必要はない。11人以上になったとき、最初のページに入った上位10人だけが残る。
4. 退出して順位を確かめる
Playし、コインを拾うなどしてScoreを増やしたらStopしよう。OutputにGlobal score saved:と名前、Scoreが出れば、Ordered Data Storeへ保存できている。
もう一度Playし、ボードを見よう。保存直後は反映まで数秒かかる場合があるため、最初の表示が古ければ60秒待つ。別のアカウントやフレンドにも参加、Score変更、退出を1回ずつ行ってもらい、全員が高いScoreから並べば完成だ。Data Storeはゲーム全体で共通なので、別サーバーで保存した値も同じランキングから読める。
複数人で確認するときは、全員が同じPublish済みゲームへ参加していることを確かめる。共有が必要なら、先にフレンド限定で共有する記事を完了しよう。1人目を1300、2人目を900、3人目を500に固定する必要はない。実際に保存された値が大きい順なら成功だ。
公式チュートリアルでも、クラウド側の反映に数秒かかる場合があると説明している。すぐ変わらなくてもScriptを連打せず、次の更新を待とう。
取得に失敗したときは、表示済みの順位を消さず、Statusだけが更新に失敗。前回の順位を表示中へ変わる。

Data Storeの制限では、短時間にGetSortedAsync()を繰り返すと失敗する場合がある。60秒更新を極端に短くせず、エラーが続く場合はOutputを確認しよう。
最初の取得から失敗した場合は、前回の順位がまだないためまだ記録がないのまま状態だけがエラーになる。通信が戻れば次の60秒更新で自動的に順位へ切り替わる。ボタン連打やScriptの複製で再試行回数を増やさないことが大切だ。
うまく動かないとき
- ずっと「まだ記録がない」: Scoreを変えたあとにStopして保存し、次のPlayで60秒待つ
- API accessのエラーが出る: Publish済みの専用テストゲームで、前の記事のStudio API設定を確認する
- 名前ではなくUser番号になる: 名前取得だけが失敗している。順位とScoreが出ていれば次の更新を待つ
- 小さいScoreが1位になる:
GetSortedAsync(true, 10)ではなくfalse, 10へ戻す - ボードが真っ暗:
SurfaceGui.Faceを見えている面へ変え、Title、Status、EntriesがPanelの中にあるか確認する - 順位が消える: 失敗した分岐で
entriesLabel.Textを空にせず、previousTextを戻しているか確認する
完成
これで、各サーバーから保存されたScoreをまとめ、上位10人を高い順で表示するランキングが完成した。次のガイドはまだ公開していないため、存在しないURLへはリンクしない。


