INetworkSerializableを実装して自作の型を同期可能にする【MLAPI】

MLAPI で同期可能な型

MLAPI でデフォルトで NetworkVariable や RPC で同期可能な型は C# プリミティブ型,Unity プリミティブ型,Enum型です.

C# Primitives | Unity Multiplayer Networking

Unity Primitives | Unity Multiplayer Networking

Enum Types | Unity Multiplayer Networking

上記以外,例えば自作したクラスや構造体をNetworkVariableやRPCで同期させたい場合はどうすればよいでしょうか.

INetworkSerializable

MLAPI v0.1.0 から INetworkSerializable インタフェースが実装されました.
このインタフェースを実装すれば,自作の型の同期が出来るようです.

INetworkSerializable | Unity Multiplayer Networking

サンプル

早速やってみましょう.以前私が作ったMLAPI-QuickStartを利用します.

xrdnk.hateblo.jp

名前がstringになっていますが,FirstName,LastNameを定義した構造体FullNameに変更してみましょう.

using MLAPI.Serialization;

namespace MLAPIQuickStart
{
    public struct FullName : INetworkSerializable
    {
        public string FirstName => _firstName;
        public string LastName => _lastName;

        private string _firstName;
        private string _lastName;

        public FullName(string firstName, string lastName)
        {
            _firstName = firstName;
            _lastName = lastName;
        }

        public void NetworkSerialize(NetworkSerializer serializer)
        {
            serializer.Serialize(ref _firstName);
            serializer.Serialize(ref _lastName);
        }
    }
}

作成したので,PlayerScript.cs を変えます.変更した箇所のみ記載.

        /// <summary>
        /// プレイヤー名の同期変数
        /// </summary>
        private readonly NetworkVariable<FullName> _networkPlayerName = new NetworkVariable<FullName>
            (new NetworkVariableSettings {WritePermission = NetworkVariablePermission.OwnerOnly});


        private void Start()
        {
            // ローカルプレイヤーの場合,カメラを一人称視点に設定し,プレイヤー名のテキストを画面下に表示する
            if (IsOwner)
            {
                _sceneScript.PlayerScript = this;

                var thisTransform = transform;
                // thisTransform.position = new Vector3(Random.Range(-5, 5), 0, Random.Range(-5, 5));
                _cameraTransform.transform.SetParent(thisTransform);
                _cameraTransform.localPosition = new Vector3(0, 0, 0);

                floatingInfo.transform.localPosition = new Vector3(0, -0.3f, 0.6f);
                floatingInfo.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);

                var playerName = new FullName("Player", Random.Range(100, 999).ToString());
                var color = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f));

                SubmitPlayerNameServerRpc(playerName);
                SubmitPlayerColorServerRpc(color);
                SubmitJoinedMessageServerRpc();
            }
        }

        /// <summary>
        /// プレイヤー名を設定する
        /// </summary>
        /// <param name="playerName">playerName</param>
        /// <param name="serverRpcParams">ServerRpcParams</param>
        [ServerRpc(RequireOwnership = true)]
        private void SubmitPlayerNameServerRpc(FullName playerName, ServerRpcParams serverRpcParams = default)
            => _networkPlayerName.Value = playerName;

        /// <summary>
        /// 入室時メッセージを表示する
        /// </summary>
        /// <param name="serverRpcParams">ServerRpcParams</param>
        [ServerRpc(RequireOwnership = true)]
        private void SubmitJoinedMessageServerRpc(ServerRpcParams serverRpcParams = default)
        {
            if (_sceneScript != null)
            {
                // 一応ここでは LastName のみ表示するようにしてみます
                _sceneScript.SetMessage($"{_networkPlayerName.Value.LastName} joined");
            }
        }

これで入室してみると以下のようになりました.

f:id:xrdnk:20210714232624p:plain

f:id:xrdnk:20210714232633p:plain

ちなみに INetworkSerializable をインタフェースに持たない状態で RPC や NetworkVariable に当てはめようとすると,
コンパイル後に以下のエラーが出ます.

f:id:xrdnk:20210714232906p:plain