🔍

【Roblox】ジャンル別ステージ抽選機能の作り方

に公開

はじめに

今回は弊社からリリースした『UGC Romance Obby Fall Game』で実装したジャンル別ステージ抽選機能について紹介します。
https://www.roblox.com/games/119782301016393/NEW-UGC-Romance-Obby-Fall-Game
https://www.youtube.com/watch?v=ByJM7H46rX0
RobloxのデータストアではDataStoreに保存されたデータをフィルタリングして取得することができないので、一定の条件にあてはまるステージを取得するのに工夫が必要になります。
今回は上記のゲームで実装した条件に当てはまったステージのリストを取得する方法について紹介したいと思います。

Roblox バージョン: 0.661.0.6610708

ジャンル機能

冒頭にも記載しましたがRobloxのDataStoreには保存されたデータの内容を参照してのフィルタリングはすることができません。なので前もって条件にあったステージリストをテーブルとして作成しておいて取得する必要があります。今回紹介する方法ではステージ情報の更新タイミングでジャンル機能の条件にあっているかの判定を行い、保存されているリスト情報と異なっていた場合のみ更新を行うようにします。こうすることでジャンルごとのテーブルを取得しなくてもすでにジャンルに割り振られているかを確認することができるようになります。
各ジャンルのステージリストはランダムに取得することができるようにテーブル型にArrayのように保存しています。

ステージごとのデータ構造

local stageData = {
    PlayCount :number,
    ClearCount :number,
    GoodCount :number,
    BadCount :number,
    StageGenre :{number}
}

ジャンルのID

1:ハード
2:ポピュラー

ステージデータのDataStoreへの更新

local HttpService = gamge:GetService("HttpService")

local DataStoreService = game:GetService("DataStoreService")
local StageDataStore = DataStoreService:GetDataStore("StageData")
local StageGenreStore = DataStoreService:GetDataStore("StageGenre")

--ステージリストを保存するキー
local HardStage = "HardStageList"
local PopularStage = "PopularStageList"

local StageDataManager = {}

--ここで渡しているplayDataはステージを遊んだプレイヤーごとのキャッシュデータです。
function StageDataManager:UpdateStageData(stageId, playData)
    local oldStageGenre, newStageGenre
    StageDataStore:UpdateAsync(stageId, function(oldData)
        --データがなければ作成を行う
        if not oldData then
            oldData = {
                PlayCount = 0,
                ClearCount = 0,
                GoodCount = 0,
                BadCount = 0,
                StageGenre = {}
            }
        end

        --ステージジャンル情報以外のデータを更新する
        local newData = {
                PlayCount = oldData.PlayCount + playData.PlayCount,
                ClearCount = oldData.ClearCount + playData.ClearCount,
                GoodCount = oldData.GoodCount + playData.GoodCount,
                BadCount = oldData.BadCount + playData.BadCount,
                StageGenre = oldData
        }

        --ジャンルごとの基準を満たしているか判定を行う
        oldStageGenre = newData.StageGenre
        newStageGenre = {}
        -- クリア率が30%以下ならハードのジャンルナンバーを追加
        if newData.ClearCount / newData.PlayCount <= 0.3 then
            table.insert(newStageGenre, 1)
        end
        -- いいね率が70%以上ならポピュラーのジャンルナンバーを追加
        if newData.GoodCount / (newData.GoodCount + newData.BadCount) >= 0.7 then
            table.insert(newStageGenre, 2)
        end
        newData.StageGenre = newStageGenre
        return newData
    end)

    --tableをJSONにエンコードしてstringとして比較を行い、ジャンル情報に更新があった場合はステージリストを更新する
    if HttpService:JSONEncode(oldStageGenre) ~= HttpService:JSONEncode(newStageGenre) then
        -- 新しいデータにあって古いデータにないものは追加を行う
        for i,newGenre in ipairs(newStageGenre) do
            if not table.find(oldStageGenre, newGenre) then
                StageGenreStore:UpdateAsync(HardStage, function(oldList)
                    local index = table.find(oldList, stageId)
                    if not index then
                        table.insert(oldList, stageId)
                    end
                    return oldList
                end)
            end
        end
        --古いデータにあって新しいデータにないものは削除を行う
        for i,oldGenre in ipairs(oldStageGenre) do
            if not table.find(newStageGenre, oldGenre) then
                StageGenreStore:UpdateAsync(HardStage, function(oldList)
                    local index = table.find(oldList, stageId)
                    if index then
                        table.remove(oldList, stageId)
                    end
                    return oldList
                end)
            end
        end
    end
end

return StageDataManager

以上がステージ情報の更新タイミングでジャンルごとのステージリストを更新する方法になります。
上記の方法だとステージの更新を行うタイミングでDataStoreへ複数回アクセスする必要があるので、ゲームによってはステージのプレイ情報をキャッシュしておいて更新回数を減らしたり、ステージ情報内にジャンル情報の最終更新時間を保存しておいて一定時間は処理を行わないようにするなどの工夫をする必要があります。

まとめ

  • ステージ情報の更新タイミングでジャンル機能の条件にあっているかの判定を行う
  • ステージ情報にジャンル情報を入れておくことでジャンルごとのテーブルの参照回数を減らす
  • DataStoreへのアクセスが集中する可能性があるため回数制限に注意する

今回は条件に当てはまったステージのリストを取得する方法について紹介しました。
ステージのクリア率などの変動のあるデータを基準にフィルタリングを行うにはそれなりに工夫が必要になってくるので、ゲームごとにあった設計をするように心がけましょう。
最後までお読みいただき、ありがとうございました!

参考

https://create.roblox.com/docs/cloud-services/data-stores

ランド・ホー Roblox開発チーム

Discussion