【Roblox】DataStoreのデータを順位付けする
はじめに
Robloxは、サーバー上にユーザーデータなどを保存しておけるDataStoreという機能があります。
ただし、DataStoreはデータ内容によるソートができません。
OrderedDataStoreなら、ソートされた状態のリストを取得できるのですが、保存できるデータは整数値のみになります。
というわけで、これらを組み合わせてDataStoreのデータを順位付けしてみました。
やり方はごく単純で、「順位付けに用いたい整数値のパラメータをOrderedDataStoreに保存しておき、それ以外のデータを共通のキーでDataStoreに保存しておく」という方法です。
バージョン:0.641.2.6410741
1. データの保存
例えば、以下のようなデータを個別に保存し、Scoreで順位付けしたいとします。
type UserData = {
Score :number,
PlayCount :number,
PlayerComment :string,
-- その他、色々なデータ.
}
その場合、保存の際にScoreをそのままOrderedDataStoreにも保存してしまえばOKです。
local DataStoreService = game:GetService("DataStoreService")
local userDataStore = DataStoreService:GetDataStore("UserData") -- UserDataを保存するDataStore
local scoreOrderedDataStore = DataStoreService:GetOrderedDataStore("Score") -- Scoreを保存し順位付けするOrderedDataStore
-- アップロード用の関数.
local function Upload(key :string, data :UserData) :(boolean, string?)
-- dataStoreにdataを保存する.
do
local result :{} = {pcall(userDataStore.SetAsync, userDataStore, key, data)}
local success = result[1]::boolean
if not success then
local errorMassage = result[2]::string
warn(errorMassage)
return false, errorMassage
end
end
-- 同時にOrderedDataStoreにScoreを保存する.
do
local result :{} = {pcall(scoreOrderedDataStore.SetAsync, scoreOrderedDataStore, key, data.Score)}
local success = result[1]::boolean
if not success then
local errorMassage = result[2]::string
warn(errorMassage)
return false, errorMassage
end
end
return true
end
本来はもっと堅い作りにした方がいいのですが、サンプルコードなのでこれくらいで。
同じScoreのデータをDataStore側にも重複して持たせるべきかどうかは場合によるかと思いますが、ソートと関係なくDataStoreのデータを単独で取得した際にScoreのデータが必要になるようであれば、毎回OrderedDataStoreに取得しにいくのはリクエスト数的にも処理時間的にもよろしくないので、重複して持たせてしまってもよいのではないかと思います。
2. データの読み込み
では仮に、上位10位までのデータのリストを取得してみましょう。
まずは先ほどのUploadを使って、適当なデータを10個保存しておきます。
-- 適当なデータをアップロードする.
for i = 1, 10, 1 do
local testData = {}::UserData
testData.Score = i
testData.PlayCount = 1
testData.PlayerComment = "test"
Upload("TestData_" .. i, testData)
end
データの読み込みでは、まずOrderedDataStoreのGetSortedAsync()
で上位10位までのデータを取得した後、そのkey情報を用いてDataStoreからデータを取得していきます。
-- 個別のデータをロードする関数.
local function Load(key :string) :(boolean, any)
local result :{} = {pcall(userDataStore.GetAsync, userDataStore, key)}
local success = result[1]::boolean
if not success then
local errorMassage = result[2]::string
warn(errorMassage)
return false, errorMassage
end
local data = result[2]
if not data then
warn("Data is nil")
return false, "Data is nil"
end
return true, data
end
-- ソートされたデータを取得する関数.
local function GetSortedData(ascending :boolean, pagesize :number, minValue :number?, maxValue :number?) :(boolean, any)
-- OrderedDataStoreからソートされたキー情報を取得する.
local result :{} = {pcall(scoreOrderedDataStore.GetSortedAsync, scoreOrderedDataStore, ascending, pagesize, minValue, maxValue)}
local success :boolean = result[1]::boolean
if not success then
local errorMassage = result[2]::string
warn(errorMassage)
return false
end
local dataStorePages = result[2]::DataStorePages
local ranking = {}::{[number] :{key :string, data :UserData}}
local page :{[number] :{key :string, value :number}} = dataStorePages:GetCurrentPage()
-- 取得した情報を元にDataStoreから個別のデータを取得する.
for i = 1, #page, 1 do
local key :string = page[i].key
local loadResult :{} = {Load(key)}
success = loadResult[1]
if not success then
local errorMassage = loadResult[2]::string
warn(errorMassage)
i -= 1
wait(1)
continue
end
ranking[i] = {key = key, data = loadResult[2]::UserData}
end
return true, ranking
end
-- ソートされたデータを取得.
local success, ranking = GetSortedData(false, 10)
print(ranking)
▼ {
[1] = ▼ {
["data"] = ▼ {
["PlayCount"] = 1,
["PlayerComment"] = "test",
["Score"] = 10
},
["key"] = "TestData_10"
},
[2] = ▼ {
["data"] = ▼ {
["PlayCount"] = 1,
["PlayerComment"] = "test",
["Score"] = 9
},
["key"] = "TestData_9"
},
[3] = ▼ {
["data"] = ▼ {
["PlayCount"] = 1,
["PlayerComment"] = "test",
["Score"] = 8
},
["key"] = "TestData_8"
},
[4] = ▼ {
["data"] = ▼ {
["PlayCount"] = 1,
["PlayerComment"] = "test",
["Score"] = 7
},
["key"] = "TestData_7"
},
[5] = ▼ {
["data"] = ▼ {
["PlayCount"] = 1,
["PlayerComment"] = "test",
["Score"] = 6
},
["key"] = "TestData_6"
},
[6] = ▼ {
["data"] = ▼ {
["PlayCount"] = 1,
["PlayerComment"] = "test",
["Score"] = 5
},
["key"] = "TestData_5"
},
[7] = ▼ {
["data"] = ▼ {
["PlayCount"] = 1,
["PlayerComment"] = "test",
["Score"] = 4
},
["key"] = "TestData_4"
},
[8] = ▼ {
["data"] = ▼ {
["PlayCount"] = 1,
["PlayerComment"] = "test",
["Score"] = 3
},
["key"] = "TestData_3"
},
[9] = ▼ {
["data"] = ▼ {
["PlayCount"] = 1,
["PlayerComment"] = "test",
["Score"] = 2
},
["key"] = "TestData_2"
},
[10] = ▼ {
["data"] = ▼ {
["PlayCount"] = 1,
["PlayerComment"] = "test",
["Score"] = 1
},
["key"] = "TestData_1"
}
}
Score順にソートされたデータを取得することができました!
OrderedDataStoreはリスト化する際、昇順、降順のほか、値の最小値、最大値も指定できるため、色々なリストを作ることができます。
これを応用して、タグのような値を整数で設定しておき、最小値と最大値を同じ値に指定して特定のタグのデータだけ抜き出す、というようなこともできるかもしれません。
ただし、取得したいデータが大量だったり、取得する頻度が高かったりする場合、リクエスト数の制限には注意が必要です。
3. まとめ
- DataStoreとOrderedDataStoreを組み合わせて、ソートされたデータリストを取得できる
- アップロードの際に、DataStoreとOrderedDataStoreに同時に書き込んでおく
- OrderedDataStoreから取得したキーリストを使って、DataStoreのデータを取得する
- 工夫次第で、いろんなリストが作れるかも
単純に、上位○○位までのデータが欲しい、というだけだったら、OrderedDataStoreは使わず、自分でランキングの仕組みを作り、データ保存の際にランキングを更新して、そのランキングデータをDataStoreに保存する、という方法でも良いかと思います。
やりたい事に応じて、方法を選択してみてください!
4. 参考
当社ではRobloxを活用したゲームの開発、 また企業の商品やサービスの認知度拡大に寄与する3Dワールドの制作など、 Robloxにおける様々な活用支援を行っております。 Robloxのコンテンツ開発をご検討されている企業様は、お気軽にご相談ください。 landho.co.jp/
Discussion