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

Data Dictionaries

Data Dictionariesは、C# Dictionariesと同様に、キーと値のペアで Data Tokens を保持します。Data Dictionaryの関数のほとんどは、内部のC# Dictionaryをラップしただけのものなので、より詳細な情報が必要な場合はC# Dictionaryのドキュメントも併せて参照してください。

Data Dictionaryのキーと値は、どちらもData Tokensです。つまり、キーとして事実上どのようなデータ型でも使用可能です。ただし、VRCJSON にシリアライズする場合は、string型のキーのみがサポートされています。

UdonSharpを使用している場合は、Data Dictionariesを利用するために using VRC.SDK3.Data; ディレクティブを含めてください。

コンストラクタ

コンストラクタ 結果
DataDictionary() デフォルトの初期容量を持つ空の DataDictionary を構築します。
DataDictionary(int) 指定された初期容量を持つ空の DataDictionary を構築します。詳細については、C# ドキュメント を参照してください。

プロパティ

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

Functions

Function Input Output Result
Add DataToken key, DataToken value 指定したキーに値を追加します。この関数が SetValue と異なる点は、キーが既に存在する場合に例外が発生することです。これは初期化時に使用するとエラーを発生させることができるため便利ですが、実行時エラーを引き起こして UdonBehaviour を停止させる可能性があるため、通常の用途には推奨されません。
Clear この辞書からすべてのキーと値を削除します。
ContainsKey DataToken key bool result 指定したキーがこの辞書に存在する場合に true を返します。
ContainsValue DataToken value bool result 指定した値がこの辞書に存在する場合に true を返します。
DeepClone DataDictionary result DataDictionary を、すべての値が同じである新しい DataDictionary に複製します。ShallowClone とは異なり、ディープクローンでは、各 DataListDataDictionary の内部を再帰的に走査し、その中身もコピーします。TokenType が "Reference" のアイテムは、配列を含め、ディープクローンされず、元の参照が維持されます。
GetKeys DataList keys この Data Dictionary に存在するすべてのキーの Data List を返します。Data Dictionary 内のすべてのアイテムを for ループで反復処理する場合に使用してください。
GetValues DataList values この Data Dictionary に存在するすべての値の Data List を返します。
Remove DataToken key bool success この辞書から特定のキーを削除します。何かを正常に削除できた場合に true を返します。
Remove DataToken key bool success, DataToken value この辞書から特定のキーを削除します。何かを正常に削除できた場合に true を返します。削除に成功した場合は、削除された値が out DataToken にコピーされます。
SetValue DataToken key, DataToken value 指定したキーに値を設定します。そのキーが存在しない場合は、新しく追加されます。
ShallowClone DataDictionary result DataDictionary を、すべての値が同じである新しい DataDictionary に複製します。DeepClone とは異なり、DataDictionary が他の DataListDataDictionary を含んでいる場合、それらは同じ参照のままとなります。
TryGetValue DataToken key bool success, DataToken output 指定したキーからトークンを取得し、out DataToken に格納します。取得が成功した場合は true を、失敗した場合は false を返します。値の取得に失敗した場合、結果の代わりに out DataTokenDataError が格納されます。
TryGetValue DataToken key, TokenType expected bool success, DataToken output 指定したキーからトークンを取得し、out DataToken に格納します。取得が成功した場合は true を、失敗した場合は false を返します。値の取得に失敗した場合、結果の代わりに out DataTokenDataError が格納されます。このバージョンの TryGetValueTokenType を含んでおり、自動的に型チェックが行われます。型が一致しない場合は、DataError.TypeMismatch と共に false が返されます。

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

DataDictionary から値を取得する

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

TryGetValue

辞書から安全に値を取得したい場合で、その場所にある型を特に気にしないのであれば、TryGetValue を使用することをお勧めします。これは、値の取得に成功したかどうかに応じて true または false を返す関数です。ifbranch の条件式内で使用することで、成功時と失敗時の動作を明確に分けることができます。

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

取得に失敗した場合、受け取る DataToken は有効な状態ですが、データではなく error が格納されます。

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

この関数には型チェック機能が組み込まれていないため、ifbranch、あるいは switch などを使用して、何らかの形で型チェックを行うようにしてください。特定の型のみが必要な場合は、自動で型チェックを行ってくれる TryGetValue の TokenType 指定版を使用することをお勧めします。

TryGetValue with TokenType

辞書から値を取得する際、その型が不明な場合は型チェックを行うことが重要です。自身でコードを書くこともできますが、煩雑になりがちです。その代わりに、TokenType を引数に取る TryGetValue を使用できます。これを使用すると、期待する型である場合のみ値を取得するよう指定できます。型が一致しない場合は false が返され、適切に処理を行うことができます。

このメソッドは、特定の場所から特定の値を取得したいものの、データが外部ソースから提供されるため、正しいデータが格納されているか確信が持てない場合に適しています。

// 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");
}

Shorthand bracket syntax

UdonSharp の dictionary["key"] = "value"; や、Udon Graph の 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;

Data Dictionaryの初期化

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

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 変数に対してのみ使用してください。これは現在開発中の機能に対する追加要素です。

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

辞書(Dictionary)は順序付けされていないため、リストに対する反復処理とは少し異なります。辞書には直接インデックスでアクセスできないため、キーを使用する必要があります。これを行うには 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 文字列との間で相互にシリアライズが可能です。これが、UdonSync で Data Dictionary を同期する現在推奨されている方法です。

このための方法の一つとして、OnPreSerializationOnDeserialization を使用して JSON 文字列のシリアライズおよびデシリアライズを行うものがあります。この手法を用いると、コードの他の部分でシリアライズを意識する必要がなくなり、値を設定するだけで同期が完結します。

[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());
    }
}

最終更新: