Network Collections
概要
Fusionが提供する構造体ベースのコレクション型は、ネットワークプロパティに使用できます。
- NetworkArray<T>
- NetworkDictionary<K, V>
- NetworkLinkedList<T>
- NetworkString<Size>
これらのコレクションは構造体であり、参照型ではないことに注意してください。FusionのコレクションとC#のコレクションとの間で、細かい使用方法の違いがあります。(INetworkStructでの使用方法をご覧ください)
使用できるT/K/Vタイプは以下の通りです。
| Blittable Primitives | ||||
|---|---|---|---|---|
| byte | sbyte | short (Int16) | int (Int32) | long (Int64) | 
| ushort (UInt16) | uint (UInt32) | ulong (UInt64) | float (Single) | double | 
| Blittable Unity Struct Types | ||||
| Vector2 | Vector3 | Vector4 | Quaternion | Matrix4x4 | 
| Vector2Int | Vector3Int | BoundingSphere | Bounds | Rect | 
| BoundsInt | RectInt | Color | Color32 | |
| System and User Defined Blittable Types | ||||
| Enums | System Types such as System.Guid | Structs | Other INetworkStructs | |
| Fusion Defined INetworkStructs | ||||
| NetworkString<IFixedStorage> | NetworkBool | Ptr | Angle | TickTimer | 
| PlayerRef | PlayerRefSet | SceneRef | NetworkId | NetworkObjectGuid | 
| NetworkObjectHeader | NetworkPrefabRef | NetworkPrefabId | NetworkRNG | NetworkButtons | 
| BitSet64 | BitSet128 | BitSet192 | BitSet256 | |
| IFixedStorage (_2, _4, _8, _16, _32, _64, _128, _256, _512) | ||||
宣言
NetworkArray/NetworkDictionary/NetworkLinkedListは、[Networked]属性を付けたプロパティで定義する必要があります。[Networked]属性が付いたコレクションでは、そのコレクションが紐づいているNetworkRunnerとSimulationインスタンスのバッキングメモリに接続するコードが、ILWeaverによって生成されます。
[Capacity]属性で最大要素数を指定します。使用量にかかわらず、指定した分のメモリが割り当てられるため、想定する最大要素数をカバーするのに十分なだけの数を選択してください。要素の値が変わらなければ、ネットワークのトラフィックは発生しません。
C#
// A basic Network Collection declaration
[Networked, Capacity(4)]
NetworkArray<int> NetArray => default;
初期化
NetworkArray/NetworkDictionary/NetworkLinkedListをNetworkBehaviourのネットワークプロパティとして宣言している場合、MakeInitializer()を使用して初期値を定義できます。
C#
[Networked]
[Capacity(4)] // Sets the fixed capacity of the collection
NetworkArray<int> NetArray { get; }
  // Optional initialization
  = MakeInitializer(new int[] { 0, 1, 2, 3 });
[Networked, Capacity(4)]
NetworkLinkedList<int> NetLList { get; }
  // Optional initialization
  = MakeInitializer(new int[] { 0, 1, 2, 3 });
[Networked, Capacity(2)]
NetworkDictionary<int, int> NetDict { get; }
  // Optional initialization
  = MakeInitializer(new Dictionary<int, int> { { 0, 0 }, { 1, 1 } });
NetworkArray<T>
NetworkArrayは、C#の配列を代替する構造体で、ネットワークプロパティに使用できます。C#の配列と完全に同等ではありませんが、NetworkArrayはIEnumerableコレクションで、値の取得/設定や、他のコレクションからのコピーメソッドを持ちます。
主なメソッド:
- Get(int index)
- Set(int, T)
C#
public class CollectionTestsArray : NetworkBehaviour
{
  [Networked]
  [Capacity(4)] // Sets the fixed capacity of the collection
  NetworkArray<NetworkString<_32>> NetArray { get; } =
    MakeInitializer(new NetworkString<_32>[] { "#0", "#1", "#2", "#3" });
  public override void FixedUpdateNetwork()
  {
    NetArray.Set(0, "Zero");
    NetArray.Set(1, "One");
    NetArray.Set(2, "Two");
    // This is invalid with a NetworkDictionary property, use Set() instead.
    // NetArray[3] = "Three";
    NetArray.Set(0, NetArray[0].ToUpper());
    NetArray.Set(1, NetArray.Get(1).ToLower());
    NetArray.Set(2, default);
    for (int i = 0; i < NetArray.Length; ++i)
    {
      Debug.Log($"{i}: '{NetArray[i]}''");
    }
  }
}
NetworkDictionary<K, V>
NetworkDictionaryは、C#のDictionaryを代替する構造体で、ネットワークプロパティに使用できます。IDictionaryを完全に実装していませんが、主要なメンバーは全て使用可能で、Dictionaryと類似しています。
主なメソッドとプロパティ:
- Clear()
- Add(K, V)
- Remove(k)
- ContainsKey(K)
- ContainsValue(V)
- Get(K)
- Set(K, V)
- this[K]
- Capacity
- Count
C#
public class NetworkDictionaryExample : NetworkBehaviour
{  
  [Networked]
  [Capacity(4)] // Sets the fixed capacity of the collection
  [UnitySerializeField] // Show this private property in the inspector.
  private NetworkDictionary<int, NetworkString<_32>> NetDict => default;
  public override void FixedUpdateNetwork()
  {
    NetDict.Clear();
    NetDict.Add(0, "Zero");
    NetDict.Add(2, "Two");
    NetDict.Add(5, "Five");
    // Setting values with the indexer are not valid, this is one of the cases
    // where the struct based NetworkDictionary differs in usage from Dictionary.
    // NetDict[0] = "Foo"; // this is not valid.
    if (NetDict.ContainsKey(0))
    {
      NetDict.Set(0, NetDict[0].ToUpper());
    }
    if (NetDict.TryGet(5, out var value))
    {
      NetDict.Set(5, value.ToLower());
    }
    NetDict.Remove(2);
    foreach(var kvp in NetDict)
    {
      Debug.Log($"{NetDict.Count}/{NetDict.Capacity} Key:{kvp.Key} Value:{kvp.Value}");
    }
  }
}
NetworkLinkedList<T>
NetworkLinkedListは、NetworkArrayを代替する構造体で、。要素を追加すると、要素はバッキングデータの空きスロットに追加され、要素を削除しても、バッキングデータ配列の他の要素はシフトされません。このコレクションは、データを要素順ではなく、構造体の一部として格納します。列挙子(Enumerator)やインデクサー(Indexer)が要素を返す順序は、バッキングデータ配列に格納されている順ではなく、要素が追加された順です。
C#
public class NetworkLinkedListExample : NetworkBehaviour
{
  [Networked]
  [Capacity(4)] // Sets the fixed capacity of the collection
  [UnitySerializeField] // Show this private property in the inspector.
  private NetworkLinkedList<NetworkString<_32>> NetList { get; }
    = MakeInitializer(new NetworkString<_32>[] { "Zero", "One", "Two", "Four"});
  public override void Spawned()
  {
    // Remove the second entry, leaving one open capacity.
    NetList.Remove("One");
    // Find an entry by value
    NetList.Set(NetList.IndexOf("Two"), "TWO");
    // Add a new entry. In memory it backfills the now open memory position.
    NetList.Add("Five");
    // The indexed order however remains in sequence,
    // so only the changed memory position is dirty and networked.
    Debug.Log($"List {NetList.Count}/{NetList.Capacity}" +
      $"0:'{NetList[0]}' 1:'{NetList[1]}' 2:'{NetList[2]} 3:'{NetList[3]}'");
  }
}
NetworkString<Size>
NetworkStringは、文字列データ用の固定サイズのコレクションです。サイズは、定義済みのIFixedStorage型のいずれかを指定できます。サイズ型の名前は_Xで、Xがコレクションのサイズになります。例えば_32は、uint[32]でバッキングされ、最大32文字の文字列を格納できます。
C#
public class NetworkStringExample : NetworkBehaviour
{
  [Networked]
  public NetworkString<_16> NetString { get; set; }
  public override void FixedUpdateNetwork()
  {
    if (Runner.IsServer) {
      NetString = System.IO.Path.GetRandomFileName();
    }
  }
}
INetworkStructでの使用方法
INetworkStruct型のネットワークプロパティの値は、かならず以下のどちらかの方法で変更してください。
- INetworkStructのコピーを作成し、変更したコピーをネットワークプロパティに代入する
- ネットワークプロパティをrefで定義する(ネットワークプロパティをrefで宣言すると、値のコピーが不要になり、参照を直接変更できる)
INetworkStruct型のネットワークプロパティは、初期化することができます。(以下のコードの例をご覧ください)
ネットワークコレクションは、INetworkStruct内のネットワークプロパティで使用できます。
C#
public class NetworkDictionaryTest : NetworkBehaviour
{
  [System.Serializable]
  struct NetworkStruct : INetworkStruct
  {
    [Networked, Capacity(16)]
    public NetworkDictionary<int, int> NestedDict => default;
    // NetworkString is a normal struct, so it doesn't require any Fusion attributes
    public NetworkString<_16> NestedString;
    // Define default initialization as a property if needed.
    public static NetworkStruct Defaults
    {
      get
      {
        var result = new NetworkStruct();
        result.NestedDict.Add(0, 0);
        result.NestedString = "Initialized";
        return result;
      }
    }
  }
  // Property declared normally as a value type
  [Networked]
  [UnitySerializeField]
  private NetworkStruct NestedStruct { get; set; } = NetworkStruct.Defaults;
  public void ModifyValues()
  {
    // NestedStruct is a value type, so modifications need to be performed on a copy,
    // and the modified result must be applied back to the property.
    var copy = NestedStruct;
    copy.NestedDict.Add(copy.NestedDict.Count, default);
    copy.NestedString = System.IO.Path.GetRandomFileName();
    NestedStruct = copy;
  }
}
また、ネットワークコレクションは、INetworkStruct内のネットワークプロパティでrefが使用できます。
C#
public class NetworkDictionaryTest : NetworkBehaviour
{
  [System.Serializable]
  struct NetworkStruct : INetworkStruct
  {
    [Networked, Capacity(16)]
    public NetworkDictionary<int, int> NestedDict => default;
    // NetworkString is a normal struct, so it doesn't require any Fusion attributes
    public NetworkString<_16> NestedString;
    public static NetworkStruct Defaults
    {
      get
      {
        var result = new NetworkStruct();
        result.NestedDict.Add(0, 0);
        result.NestedString = "Initialized";
        return result;
      }
    }
  }
  // Property declared as ref type, allowing direct modification of values
  [Networked]
  [UnitySerializeField]
  private ref NetworkStruct NestedStruct => ref MakeRef(NetworkStruct.Defaults);
  public void ModifyValues()
  {
    // NestedStruct was declared as a ref above, so modifications
    // may be made directly to the reference, without need for copies.
    NestedStruct.NestedDict.Add(NestedStruct.NestedDict.Count, default);
    NestedStruct.NestedString = System.IO.Path.GetRandomFileName();
  }
}