オブジェクトが同期する処理などがコンポーネントを利用することで簡単に実現できました。しかしゲームを作る場合、特定の変数や、カスタムされた構造体を共有する必要があります。
NetworkVariableについて
Netcode for GameObjects(以下Netcode)では、各オブジェクト単位で変数を共有する方法があります。NetworkVariableを利用することで簡単に同期させることが出来ます。
変数を利用することができるNetowrkVariableについて簡単に理解を深めましょう
宣言の仕方
NetworkVariableはGeneric型を利用して宣言されます。まずはint型の変数を共有したいと仮定し、宣言する場合は以下。対象のスクリプトではusing Unity.Netcode;が必要になります。スクリプト上部に追加してください。コンストラクタで0を指定しています。バージョンによってはNetworkVariableは初期値を入力していないとエラーが発生してしまうため必要があれば初期化を行ってください。
using Unity.Netcode; // スクリプト上部にusingを追加する必要があります。
クラス内
{
private NetworkVariable<int> networkInt = new NetworkVariable<int>(0);
}
パーミッション(権限)について
Netcode系のオブジェクトには自分自身のインスタンスであっても、オーナーかどうかなどを気にする必要があります。NetworkVariableも各変数の書き込み・読み取りが行えるのは誰かを指定することが出来ます。デフォルトのままだと書き込み権限がサーバーにしかなく、現在のプログラムだと相手までパラメータの共有が出来ません。
権限の設定は、宣言するときのインスタンスを作成する時に指定することが出来ます。読み取りはだれでもOKで、書き込むのはオーナーが行うという設定は以下。
private NetworkVariable<int> networkInt = new NetworkVariable<int>(
0, // 初期値
NetworkVariableReadPermission.Everyone, // 読み取り権限
NetworkVariableWritePermission.Owner // 書き込み権限
);
パーミッション変更するのに結局初期値が必要になるというね。
パラメータが変化した時のイベント
変数はアップデートなどで毎フレーム監視を行うのは効率的ではありません。そのため変化が起きた時だけ教えてくれる機能があります。OnValueChangedを利用することで、変化前後の値をそれぞれ見比べることが出来ます。
networkInt.OnValueChanged += (int oldParam, int newParam) =>
{
// ここに新旧の変数を利用した処理を書く
// oldがいらない場合は使わなくてもOK
};
今回はint型のサンプルだから上記のようになっているだけ!変数名もお好みで変えられますよ!
ラムダ式っぽく書いていますが、もちろんメソッドでイベントを受け取ることも出来ます!
構造体を使いたい場合はシリアライズ化が必要
自作の構造体で通信を行いたい場合は、構造体にシリアライズ化を行うインターフェースを追加し、対応を行う必要があります。下のサンプルではMyCustomModelという構造体を扱っています。
public struct MyCustomModel : INetworkSerializable
{
public int intParam;
public bool boolParam;
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref intParam);
serializer.SerializeValue(ref boolParam);
}
}
private NetworkVariable<MyCustomModel> networkData = new NetworkVariable<MyCustomModel>(
new MyCustomModel()
{
intParam = 0,
boolParam = false
}, // 初期値
NetworkVariableReadPermission.Everyone, // 読み取り権限
NetworkVariableWritePermission.Owner // 書き込み権限
);
文字列を使う場合の注意点
文字列は、そのままstring型を使うことが出来ません。これは通信でやり取りをおこなうデータには参照型を使うことが出来ないからです。そのため、文字列を使う場合はFixedString系を利用します。FixedStringは文字列に必要なバイト数を指定することで、固定のメモリを割り当てることが可能になります。反面確保したバイト数より大きな文字列を扱うことが出来ないので注意してください。
using Unity.Collections;
public FixedString64Bytes message;
サンプルプログラムで確認してみる
上記の知識を踏まえ、前回までのキャラクターが移動するスクリプトに加筆しながら変数の共有を行ってみましょう。
方針とソースコード
表示用の処理を作るのは面倒なので、インスペクターで数字を確認します。なのでエディター側でしか確認出来ません。今回は全部載せておきます。前回のPlayerMovementスクリプトを下記のように変更してみてください。
using UnityEngine;
using Unity.Netcode;
public class PlayerMovement : NetworkBehaviour
{
private NetworkVariable<int> networkData = new NetworkVariable<int>(
0, // 初期値
NetworkVariableReadPermission.Everyone, // 読み取り権限
NetworkVariableWritePermission.Owner // 書き込み権限
);
public int previewInt;
private void Start()
{
networkData.OnValueChanged += (int oldParam, int newParam) =>
{
previewInt = newParam;
};
}
private void Update()
{
if (!IsOwner)
{
return;
}
if (Input.GetKeyDown(KeyCode.Space))
{
networkData.Value += 1;
}
Vector2 direction = new Vector2()
{
x = Input.GetAxisRaw("Horizontal"),
y = Input.GetAxisRaw("Vertical")
};
float moveSpeed = 3f;
transform.Translate(direction * moveSpeed * Time.deltaTime);
}
}
実際に動かして確認する
クライアントとUnityエディター両方で起動したら、Unity側でクライアント側から作られたプレイヤーのGameObjectを選択します。インスペクタが表示されている状態からクライアント側でスペースキーを押し下げて、PreviewIntが増えていくのを確認してみてください。どちらがホストでも構いませんが、両方確認しておくといいかな。
コメント