VRChat 非公式日本語ドキュメント

Data Dictionaries

Data Dictionaryは、Data Tokenをキーと値のペアで保存するコンテナであり、C#のDictionaryと同様のものです。Data Dictionaryの関数の多くは、内部的なC# Dictionaryのラッパーであるため、より詳細な情報が必要な場合はC# Dictionaryのドキュメントも併せて参照してください。

Data Dictionaryのキーと値は、どちらもData Tokenです。つまり、実質的にあらゆるものをキーとして使用できます。ただし、VRCJSONへのシリアライズを予定している場合は、文字列型のキーのみがサポートされています。

UdonSharpを使用する場合は、using VRC.SDK3.Data; ディレクティブを追加してData Dictionaryを使用してください。

プロパティ

プロパティResult
Count辞書内の要素数を取得します。

関数

関数InputOutputResult
AddDataToken key, DataToken value指定したキーに値を設定します。SetValueと異なり、キーが既に存在する場合は例外がスローされるのがこの関数の主な目的です。初期化時に使用すると重複を検知するのに役立ちますが、通常の用途では実行時エラーが発生してUdonBehaviourが停止する可能性があるため、推奨されません。
Clearこの辞書からすべてのキーと値を削除します。
ContainsKeyDataToken keybool Result指定したキーがこの辞書に存在する場合、trueを返します。
ContainsValueDataToken keybool Result指定したキーがこの辞書に存在する場合、trueを返します。
DeepCloneDataDictionary ResultDataDictionaryを、すべての同じ値を含む新しいDataDictionaryに複製します。ShallowCloneとは異なり、DeepCloneは各DataListやDataDictionaryの内部を再帰的に走査し、その内容もコピーすることを意味します。TokenTypeが"Reference"であるアイテムは、配列を含め、元のオブジェクトと同じ参照を保持し、ディープコピーはされません。
GetKeysDataList keysこのData Dictionary内に存在するすべてのキーのData Listを返します。これを使用して、forループ内でData Dictionary内のすべての項目を反復処理します。
GetValuesDataList valuesこのData Dictionary内に存在するすべての値のData Listを返します。
RemoveDataToken keybool successこの辞書から特定のキーを削除します。何かが正常に削除された場合はtrueを返します。
RemoveDataToken keybool success, DataToken valueこの辞書から特定のキーを削除します。何かが正常に削除された場合はtrueを返します。削除に成功した場合、削除された値がout DataTokenにコピーされます。
SetValueDataToken key, DataToken value指定したキーの値を設定します。そのキーが存在しない場合は、新しく追加されます。
ShallowCloneDataDictionary resultDataDictionaryを、同じ値をすべて含む新しいDataDictionaryに複製します。DeepCloneとは異なり、DataDictionaryが他のDataListやDataDictionaryを含んでいる場合、それらは同じ参照のままとなります。
TryGetValueDataToken keybool success, DataToken output指定されたキーからトークンを取得し、out DataTokenに格納します。取得に成功した場合はtrue、失敗した場合はfalseを返します。取得に失敗した場合、結果の代わりにout DataTokenにDataErrorが格納されます。
TryGetValueDataToken key, TokenType expectedbool success, DataToken output指定されたキーからトークンを取得し、out DataTokenに格納します。取得に成功した場合はtrue、失敗した場合はfalseを返します。取得に失敗した場合、結果の代わりにout DataTokenにDataErrorが格納されます。このバージョンのTryGetValueにはTokenTypeが含まれており、自動的に型チェックが行われます。型が一致しない場合はfalseを返し、DataError.TypeMismatchが設定されます。

Jsonから生成されたDataDictionaryに対して、ContainsValueShallowCloneGetValuesのように、すべての値に影響を与えたり、すべての値を参照したりする関数を呼び出す際は注意してください。これらを呼び出すと、まだ解析されていない最上位レベルのすべての値が解析されるため、値が多数ある場合は負荷が高くなる可能性があります。一度解析されれば、その後の操作は低コストになります。

DataDictionaryから値を取得する

辞書から値を取得する方法はいくつかあります。それぞれに適した用途があるため、どれを使用するかは状況に応じて選択してください。

TryGetValue

辞書から安全に値を取得したいが、その場所にどの型が存在するかを気にしない場合は、TryGetValueを使用することをお勧めします。これは、値の取得に成功したかどうかに応じてtrueまたはfalseを返す関数です。成功時と失敗時の処理が明確になるよう、ifbranchの条件式の中に配置することを想定しています。

TryGetValueの例
if (dictionary.TryGetValue("key", out DataToken value)) {
Debug.Log($"Success! {value}");
} else {
Debug.Log($"Failed! {value}");
}

失敗した場合、受け取るDataToken自体は有効ですが、データが含まれる代わりにエラーが含まれます。

このメソッドは、特定の場所から値を取得したいものの、それが正確に何であるかを気にしない場合に適しています。

この関数には型チェックが組み込まれていないため、ifbranchswitchなどを用いて、何らかの形で型チェックを行う必要があります。特定の型のみを対象とする場合は、自動的に型チェックを行ってくれるTokenTypeを指定するバージョンのTryGetValueを使用することをお勧めします。

TokenTypeを指定したTryGetValue

辞書から値を取得したいものの、その値がどの型であるか不明な場合は、型チェックを行うことが重要です。コード内で独自に実装することもできますが、複雑になりがちです。その代わりに、TokenTypeを含んだバージョンの TryGetValue を使用することができます。これを使用すると、期待する型である場合にのみ値を取得するように指定できます。型が一致しない場合はfalseを返し、適切に処理を行うことができます。

このメソッドは、特定の場所から特定の値を取得したいが、データが外部ソース由来であるため、そのソースに正しいデータが含まれているか確信が持てない場合に適しています。

TokenTypeを使用した TryGetValue の例
// You could do it this way, but it's a bit ugly
if (dictionary.TryGetValue("key", out DataToken value)) {
if (value.TokenType == TokenType.DataDictionary)
{
Debug.Log($"Success! Matching dictionary has {value.DataDictionary.Count} items");
}
}

// This approach has a type check built in! It's functionally the same, but streamlined.
if (dictionary.TryGetValue("key", TokenType.DataDictionary, out value)) {
Debug.Log($"Success! Matching dictionary has {value.DataDictionary.Count} items");
}

ブラケットによる短縮構文

UdonSharpでは dictionary["key"] = "value"; のようなブラケット構文を、Udonグラフでは DataDictionary Get Item ノードを使用して、Data Dictionaryに対してアイテムの設定や取得を行うことができます。この方法はより簡潔で使いやすいですが、完全に安全ではない点に注意してください。存在しないキーから値を取得しようとするなど、無効な操作を実行しようとすると、UdonBehaviourが停止する可能性があります。

このメソッドは、データの内容を完全に把握しており、その存在と期待する型が保証されている場合に適しています。それ以外の場合は、何らかの形の TryGetValue を使用することが推奨されます。

ブラケットによる短縮構文の例
dictionary["A"] = 5;
dictionary["B"] = 10;

// This makes the assumption that A and B will always contain integers.
// This is a safe assumption to make since we set them just above in a controlled environment.
// If the data is coming from an external source, we shouldn't make these assumptions!
int sum = dictionary["A"].Int + dictionary["B"].Int;

DataDictionaryの初期化

UdonSharpでは、DataDictionaryをプライベート変数で初期化できます。これにより、コードが実行される前に定義された既存のデータセットを持つことが可能になります。また、ネストされた辞書や、DataTokenがサポートするあらゆる型にも対応しています。この構文の使用例を以下に示します:

DataDictionaryの初期化例
private DataDictionary users = new DataDictionary()
{
{ "John Doe", new DataDictionary()
{
{"email", "johndoe@example.com"},
{"age", 35},
{"address", new DataDictionary()
{
{"street", "123 Main St"},
{"city", "Anytown"},
{"state", "CA"},
{"zip", 12345}
}
}
}
},
{ "Jane Smith", new DataDictionary()
{
{"email", "janesmith@example.com"},
{"age", 28},
{"address", new DataDictionary()
{
{"street", "456 Elm St"},
{"city", "Anytown"},
{"state", "CA"},
{"zip", 12345}
}
}
}
},
{ "Bob Johnson", new DataDictionary()
{
{"email", "bobjohnson@example.com"},
{"age", 42},
{"address", new DataDictionary()
{
{"street", "789 Oak St"},
{"city", "Anytown"},
{"state", "CA"},
{"zip", 12345}
}
}
}
}
};

現在、UdonSharpは関数内でのこの形式のイニシャライザをサポートしていません。これはUdonSharpに対する機能リクエストとなります。

現在、UnityはDataDictionaryをシリアライズしないため、これは推奨されません。 private または [NonSerialized] public 変数に対してのみ使用する必要があります。これは現在開発中の機能への追加要素です。

辞書の全エントリーを反復処理する

辞書内の全エントリーを反復処理する方法は、リストとは少し異なります。辞書には順序がないためです。辞書に直接インデックスでアクセスすることはできず、キーを使用する必要があります。これを行うために GetKeys() 関数が用意されています。この関数は、辞書内の全キーを含む DataList を返します。これを入手すれば、for ループを使用してすべてのキーを反復処理し、各キーの値にアクセスできます。

辞書内の全エントリーを反復処理する例
// First get all the keys in the dictionary
DataList keys = dictionary.GetKeys();

// For loop over all the keys
for (int i = 0; i < keys.Count; i++)
{
// Get the key at the current index
DataToken key = keys[i];

// Access the entry connected to that key
Debug.Log(dictionary[key].ToString());
}

GetKeys() は一見すると処理負荷が高そうに見えるかもしれませんが、キーが追加または削除されない限りキャッシュされるため、Udon 自体のオーバーヘッドを除けば、頻繁にアクセスしても概してパフォーマンスは良好です。

辞書をソートされた状態で反復処理する必要がある場合、このメソッドを活用した巧妙なテクニックとして、GetKeys() でキーを取得してから Sort() で並べ替え、その後に for ループで処理する方法があります。辞書自体には順序という概念がなくソートすることはできませんが、アクセスに使用するリストをソートすることは可能です!

辞書の値をキーとは別に扱いたい場合は、 GetValues() を使用することもできます。これはすべての値を明示的に展開する必要があるアプリケーションでは有用ですが、辞書に慣れていない場合、このメソッドは誤解を招く可能性があることに注意してください。他の理由として、辞書には順序がないため、GetValues() で取得した際に特定のアイテムが常に特定のインデックスにあると想定してはいけません。また、これらのインデックスが GetKeys() で取得されるインデックスと一致すると期待すべきでもありません。ほとんどの場合、GetKeys() だけで十分であり、GetValues() は辞書に対してより高度な制御が必要な場合のための選択肢です。

Data Dictionaryをネットワーク経由で他のプレイヤーと同期する

Data Dictionaryを直接同期することはできません。ただし、VRCJsonを使用してJSON文字列への Serialize および Deserialize を行うことは可能です。これが、UdonSyncを使用してData Dictionaryを同期するための現在の推奨方法です。

これを行う一つの方法は、OnPreSerialization と OnDeserialization を使用して、json 文字列の Serialize および Deserialize を行うことです。この手法を用いれば、コードの他の部分でシリアル化について気にする必要はなく、単純に値を設定するだけで済みます。

ネットワーク経由で他のプレイヤーと Data Dictionary を同期する例
[UdonSynced]
private string _json = "";
private DataDictionary _dictionary;

public override void OnPreSerialization()
{
if (VRCJson.TrySerializeToJson(_dictionary, JsonExportType.Minify, out DataToken result))
{
_json = result.String;
}
else
{
Debug.LogError(result.ToString());
}
}

public override void OnDeserialization()
{
if(VRCJson.TryDeserializeFromJson(_json, out DataToken result))
{
_dictionary = result.DataDictionary;
}
else
{
Debug.LogError(result.ToString());
}
}

最終更新: