持続データを使用すると、プレイ セッション間でプレイヤーごとのデータを追跡し、保存することができます。 持続データは、個々のプレイヤーのプロフィールや統計といったデータを Verse に格納することで機能します。 このデータは値が変わるたびに何度でも更新できます。 このデータは持続可能であるため、複数のゲームセッションの間で維持され、プレイヤーがゲーム内でオンラインであればいつでも利用できます。 詳細については、Verse で持続データを使用するを参照してください。
このページでは、Verse で持続データを操作する際のベスト プラクティスをいくつか紹介します。
クラスを使用して後で新規フィールドを追加する
現在、島を公開した後に変更できる持続データの型はクラス型のみですが、新しいフィールドにデフォルト値がある場合に限ります。 つまり、以前のバージョンから保存されたデータをロードすると、新しいフィールドとそのデフォルト値が含まれることになります。
以下の持続データを持つプロジェクトを公開する例を見てみましょう。
player_profile_data := class<final><persistable>:
Class:player_class = player_class.Villager
XP:int = 0
Rank:int = 0プロジェクトは公開され、稼動しているため、ゲームをプレイしたことのあるプレイヤーには、このような関連付けられている持続データがあります。 クエスト回数や履歴といったフィールドをプレイヤーのプロフィール データにさらに追加すると、その持続データは更新されたプロジェクトで次のようになります。
player_profile_data := class<final><persistable>:
Class:player_class = player_class.Villager
XP:int = 0
Rank:int = 0
CompletedQuestCount:int = 0
QuestHistory:[]string = array{}最初のバージョンの player_profile_data クラスでプレイしたプレイヤーの持続データに、次の新しいフィールドが追加されました。
CompletedQuestCountに0という値を設定します。これは指定されたデフォルト値です。指定されたデフォルト値である空の文字列配列を持つ
QuestHistory
これは、古いバージョンのデータを更新できるように、新しいフィールドにデフォルト値が用意されているためです。
プロジェクトが公開された後に更新される可能性があるのはクラスのみであるため、モジュールにスコープされた weak_map 変数の値型としてクラスを使用することを強くお勧めします。
持続クラスの作成方法の詳細については、持続型を参照してください。
コンストラクタを使用して部分更新をする
クラスを使用する場合は、コンストラクタを使用して更新された状態を含むクラスの新しいインスタンスを作成することをお勧めします。コンストラクタを使用するとクラスの部分的な更新を行うことができるからです。
以下は PlayerProfileDataMap を更新する方法の例です。 GrantXP() 関数は対象プレイヤーの現在のデータを取得し、次に MakePlayerProfileData() コンストラクタを呼び出して新しいバージョンのプロフィール データを作成します。 プレイヤーのソース データは新しい XP 値とともにコンストラクタに渡されるため、XP 値のみが更新され、他のデータはすべて同じ状態が保たれます。
MakePlayerProfileData<constructor>(Src:player_profile_data)<transacts> := player_profile_data:
Version := Src.Version
Class := Src.Class
XP := Src.XP
Rank := Src.Rank
CompletedQuestCount := Src.CompletedQuestCount
QuestHistory := Src.QuestHistory
GrantXP(Agent:agent, GrantedXP:int):void=
if:
前の例では 1 つのフィールドを更新する方法を示しましたが、次の方法を使用すれば必要な数だけ更新することができます。
set PlayerProfileDataMap[Player] = player_profile_data:
QuestHistory := UpdatedSaveData.QuestHistory
CompletedQuestCount := OldData.CompletedQuestCount + 1
MakePlayerProfileData<constructor>(OldData)持続データのバージョンを管理する
持続クラスでバージョン管理を使用して、以前に保存されたプレイヤー データのインスタンス バージョンを検出することをお勧めします。 バージョンを使用することで、持続クラスの定義やゲームプレイ ロジックが変更された場合に、移行の検出と適用が可能になります。
整数値または文字列値を使用して永続クラスのバージョンを示すことができますが、オプション値を使用して現在および過去のバージョンのデータへの参照を格納することをお勧めします。 次の設定について検討します。
var SavedPlayerData:weak_map(player, player_data) = map{}
# A player data class containing optional fields of versioned player data. Only one of these
# optional values should contain a real value at any given time.
player_data := class<final><persistable>:
V1:?v1_player_data = false
V2:?v2_player_data = false
# Original version of player data.
ここで、player_data クラスには、関連するデータ クラスの第 1 バージョンと第 2 バージョンのオプション値が含まれており、それらは v1_player_data クラスと v2_player_data クラスによって表されます。 プレイヤーに複数のバージョンのデータが関連付けられないように、V1 または V2 のどちらか一方が設定される必要があります
元の V1 プレイヤー データには、3 つの int フィールドがあります。 V2 バージョンのデータでは、プレイタイム フィールドが float に変更され、さらに 2 つの新しいフィールドが追加されます。 V2 バージョンで プレイタイム フィールドの型が変更されたため、古い V1 のデータを持つプレイヤーのために変換が必要になります。 古い V1 データのプレイヤーが体験に参加する場合、ヘルパー コンストラクタ関数を使用して、次のように古いデータに基づいて新しい V2 データ クラスをビルドすることができます。
# Create v1_player_data using existing v1_player_data.
MakeV1PlayerData<constructor>(SourceData:v1_player_data)<transacts> := v1_player_data:
XP := SourceData.XP
Rank := SourceData.Rank
Playtime := SourceData.Playtime
# Create v2_player_data using existing v2_player_data.
MakeV2PlayerData<constructor>(SourceData:v2_player_data)<transacts> := v2_player_data:
XP := SourceData.XP
Rank := SourceData.Rank
島に参加するプレイヤーのデータを強制的にリセットしたい状況がおそらく出てきます。 weak_map の持続データのデフォルト値を全プレイヤーに再割り当てし、クラスのバージョン フィールドを変更すれば、それが可能です。 オプションのバージョン付けされたデータを使用する場合、オプションのフィールドを false に設定することでデータをリセットできます。
プレイヤーのデータがすでにリセットされているかどうかについては、プレイヤーの持続データのバージョン値が最新かどうかを確認すれば判明します。
持続データが制限内であるかをテストする
更新によって持続データの合計サイズが影響を受ける可能性がある場合は、持続データが Verse の持続システムの制限内に収まっていることを確認する必要があります。 持続データを更新しようとしても、サイズ制限を超えていると Verse のランタイム エラーが発生します。 詳細については、「最大持続オブジェクト サイズ」を参照してください。
FitsInPlayerMap() 関数を使用することで、更新によって合計サイズがどう変化するかをチェックすることができます。
以下の例では、持続データに文字列の配列が含まれています。 その配列が大きすぎて weak_map に格納できなくなった場合、つまり FitsInPlayerMap() が失敗した場合、この例では配列を空にし、最後に保存された要素だけを追加しています。
# Construct and return a new player_profile_data with updated quest history.
SetQuestHistory(Src:player_profile_data, NewQuestHistory:[]string)<transacts>:player_profile_data =
NewData:player_profile_data = player_profile_data:
MakePlayerProfileData<constructor>(Src)
QuestHistory := NewQuestHistory
# Set a player's quest history in the PlayerProfileDataMap.
RecordQuestHistory(Agent:agent, QuestHistory:string):void=
if:
CheckSaveDataForPlayer[Agent]
プレイヤーが島に参加したときに反応する
新しいプレイヤーが島に参加したとき、そのプレイヤーのエントリが持続 weak_map に自動的に追加されることはありません。 Verse にエントリを追加する必要があります。
そうするには、アクセスしたときにプレイヤーがすでに weak_map にいるかどうかをチェックするか、プレイヤーが参加したときにデフォルトのデータを weak_map に追加する必要があります。これは、ゲームの PlayerAddedEvent() イベントをサブスクライブすることで知ることができます。
GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
# Later in your file
OnPlayerAdded(Player:player):void=
if:
not PlayerProfileDataMap[Player]
set PlayerProfileDataMap[Player] = player_profile_data{}