スコア・データ初級Robux不要

Roblox Studioでコイン数を保存する:Data Storeの使い方

Data StoreへプレイヤーのScoreを保存し、ゲームを退出して入り直したあとも、集めたコイン数を復元できるようにしよう。

公開日
Studioへ再参加した直後からプレイヤー一覧のScoreが3に復元された画面

ゲームを退出したあともコイン数を残し、次に参加したとき、拾う前から前回のScore = 3へ戻るようにする。完成までの目安は12分だ。Robuxは必要ないが、自分が所有するゲームをRobloxへPublishしておく必要がある。

Studioへ再参加した直後からプレイヤー一覧のScoreが3に復元された画面

この記事は、触れると増えるコインの記事の続きだ。Playするたびに3枚が戻り、Scoreが0から始まる状態を用意しよう。まだRobloxへ保存していない場合は、先にゲームを非公開で保存する記事を進める。

重要 — 公開中の本番ゲームでは試さない

Studioのテストと実際のゲームは同じData Storeを使う。Roblox公式も、公開中の本番ゲームではなく別のテスト版でStudio accessを有効にするよう案内している。この記事では、テスト用ゲームとPlayerScore_v1という新しい保存箱を使おう。

1. StudioからData Storeを使えるようにする

Creator Hubで対象のテストゲームを開き、左側の環境設定 > 設定へ進む。APIまで下へスクロールし、API サービスへのStudio アクセスを有効にするへチェックを入れて、変更を保存を押そう。

Creator HubのAPIサービスへのStudioアクセス設定

StudioではFile > Experience Settings > Securityから同じ設定を開ける。Experience Settingsの公式一覧では、この項目をData Storeなどのテストに使う設定として説明している。

Tip — APIは保存箱へつなぐ窓口

Data Storeは端末内のファイルではなく、Roblox側へ置かれる保存箱だ。APIはStudioとその保存箱をつなぐ窓口になる。ローカルファイルのままではゲームを識別できないため、Publish済みゲームが必要だ。

この設定を有効にするのは、この記事の動作確認に使うテストゲームだけにしよう。チェックを入れると、Studioで実行したScriptもそのゲームのData Storeを読み書きできるようになる。公開中のゲームで試すと、遊んでいる人の保存値と混ざる可能性がある。テスト用ゲームなら、失敗しても実際の進み具合へ影響させずにやり直せる。

2. SaveScoreを追加する

Studioへ戻り、ExplorerのServerScriptServiceへScriptを追加する。名前をSaveScoreにしよう。LeaderboardCoinCollectorは消さず、3本を並べる。

ServerScriptServiceへ置いたSaveScoreとData Storeのコード

SaveScoreのコードをすべて消し、次へ置き換えよう。

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local scoreStore = DataStoreService:GetDataStore("PlayerScore_v1")
local loadedPlayers = {}
local savedPlayers = {}

local function getScore(player)
	local leaderstats = player:WaitForChild("leaderstats")
	return leaderstats:WaitForChild("Score")
end

local function getKey(player)
	return "player_" .. player.UserId
end

local function loadScore(player)
	local score = getScore(player)
	local success, savedScore = pcall(function()
		return scoreStore:GetAsync(getKey(player))
	end)

	if not success then
		warn("Score load failed:", savedScore)
		return
	end

	if typeof(savedScore) == "number" then
		score.Value += savedScore
	end
	loadedPlayers[player] = true
	print("Score loaded:", player.Name, score.Value)
end

local function saveScore(player)
	if not loadedPlayers[player] then
		warn("Score save skipped because load failed:", player.Name)
		return
	end
	if savedPlayers[player] then
		return
	end

	local score = getScore(player)
	local success, errorMessage = pcall(function()
		scoreStore:UpdateAsync(getKey(player), function()
			return score.Value
		end)
	end)

	if success then
		savedPlayers[player] = true
		print("Score saved:", player.Name, score.Value)
	else
		warn("Score save failed:", errorMessage)
	end
end

Players.PlayerAdded:Connect(loadScore)
Players.PlayerRemoving:Connect(saveScore)

game:BindToClose(function()
	for _, player in Players:GetPlayers() do
		saveScore(player)
	end
end)

3. 読み込みの仕組みを確認する

GetDataStore("PlayerScore_v1")は、このゲーム内で使う保存箱の名前を決める。名前を変えると別の保存箱になるため、途中で書き換えないようにしよう。

その中では、参加者ごとにkeyとvalueを1組で保存する。

key:   player_11328632018
value: 3

keyはロッカー番号、valueは中身のような関係だ。表示名ではなくUserIdを使うため、あとで名前を変えても同じ人のScoreを開ける。

PlayerScore_v1は全員で共通する保存箱の名前で、player_から始まるkeyは参加者ごとの引き出しだ。参加者が10人いても保存箱を10個作るのではなく、1つの保存箱の中で10個のkeyを使い分ける。v1は最初の保存形式だと分かる目印で、今後データの形を大きく変えるときに別名へ切り替えやすくなる。

GetAsync()は参加時に保存値を読む。公式のData Store解説にあるように、保存先への通信は失敗する場合がある。そのためpcall()で囲み、成功したかと結果を分けて受け取る。

Tip — pcallは失敗を受け止める囲い

pcall()の中でAPIが失敗しても、Script全体を赤いエラーで止めず、success = falseとして次の判断へ進める。今回は警告を出し、保存済みか分からない0をあとで上書きしない。

保存値が数字なら、現在のScoreへ加える。読み込みを待つ短い間に1枚取っても、その1点を消さずに保存値と合わせられる。初参加で保存値がない場合は0のままだ。

4. 退出時に現在のScoreを保存する

PlayerRemovingはプレイヤーがゲームを離れるときの合図だ。そこでUpdateAsync()を呼び、現在のScoreを返す。公式は、複数serverから同じkeyを更新する可能性がある場合にUpdateAsyncを使う方法を示している

BindToClose()はserverを閉じる直前の最後の保存機会だ。StudioでStopしたときやserver終了時にも、残っている参加者を保存する。savedPlayersは同じセッションの成功書き込みを1回に制限する。

読み込みに失敗した参加者はloadedPlayersへ入らない。退出時の保存を見送るため、以前の値があるか分からない状態で0を上書きしない。失敗したセッションで取ったコインは保存されないが、以前の安全な値を守れる。

コインへ触れるたびには保存しない

この記事では、コインを取った瞬間はScoreだけを変更し、Data Storeへの書き込みは退出時にまとめる。Data Storeは画面内の数字を毎回変える場所ではなく、セッションをまたいで残したい値を預ける場所だ。

3枚なら触れるたびに保存しても少なく見えるが、コインが100枚になったり参加者が増えたりすると、短時間のAPI通信が多くなる。Play中はScoreを正本として素早く更新し、退出時に最後の値を保存する流れをまず身につけよう。

5. 2回のPlayで保存と復元を確かめる

まず1回目のPlayを始めよう。Outputに次のような行が出れば、読み込みは成功している。

Score loaded: robtsuku 0

3枚のコインを拾ってScore = 3にし、Stopを押す。Outputで保存成功を確認しよう。

Score 3がData Storeへ保存されたOutput

Score saved: robtsuku 3

もう一度Playを押す。今回はコインへ触れる前に、右上のScoreが3へ戻り、Outputにも読み込み結果が出る。

次のPlayでScore 3が復元された画面

Score loaded: robtsuku 3

次の4点が見えれば完成だ。

  1. 1回目に3枚を取るとScore 3
  2. Stop時にScore savedと3
  3. 2回目は触れる前からScore 3
  4. 赤いData Storeエラーがない

2回目のPlayで確認するときは、キャラクターを動かす前に右上とOutputを見る。コインへ触れてからScore 3になった場合は、保存値が戻った証拠にならない。参加直後から3なら、GetAsync()で前のセッションの値を読めたと判断できる。

保存されないとき

  • StudioAccessToApisNotAllowed: Creator HubのAPI設定へチェックを入れ、変更を保存してからStudioでPlayし直す
  • API設定が見つからない: ゲームをローカルファイルではなくRobloxへPublishし、所有者のアカウントでCreator Hubを開く
  • 毎回0になる: PlayerScore_v1"player_" .. player.UserIdを両方の処理で同じまま使う
  • LocalScriptでエラーになる: Data Storeへアクセスできるのはserver側だけなので、SaveScoreServerScriptServiceの通常のScriptへ置く
  • Score load failed: 一時的な通信失敗の可能性がある。現在のセッションでは保存を試さず、少し待って新しいPlayで再確認する
  • 短時間に何度も警告が出る: PlayとStopの連打をやめ、1回の読み込み、取得、保存が終わってから次のテストを始める

完成

これで、コインを拾ったScoreがRoblox側へ保存され、退出と再参加をまたいで戻るようになった。leaderstatsは画面表示、Data Storeは長期保存という別の役割だ。

次は、この保存済みデータを使いながら、ゲームパス所有者だけが通れるVIPドアを作る。

公式リファレンス

画像の拡大表示