【Unity】UNetからMirrorへの乗り換え入門
以前からobsoleteだったUNetがついにUnity2019で無くなりました。
と見せかけてパッケージ化してPackageManagerでMultiplayer HLAPIとして生きてます。これに気づかずにMirror乗り換えを結構まじめにやっちゃったのでせっかくなのでまとめて公開します!
*検証バージョンはUnity2018.4+Unet → Unity2019.1+Mirror v3.7.3です。Mirrorが精力的にアップデートされてるので情報が古い可能性があります。
Mirrorとは
UNetの代替となるOSSのアセットで、uMMORPGでも使われている(というか開発者が同じ)MMOスケールでもばっちり動くUNetみたいな感じです。UNetなくなるの辛い!ってときには最も乗り換えコストが少ない選択肢だと思います。
使ってみた感想
そもそもMirrorに乗り換えて大丈夫なのかという点が気になる方も多いかと思いますが、触った感じ不可解な挙動もなく大丈夫そうです。つまづくところはだいたいUNetとの相違点で、後述の部分と少し被りますが公式のMigration Guideもあります。
なお、UNet未経験でMirrorから入るのはちょっとハードルが高そうです。
UNetの概念的なものはMirrorのドキュメントだと少し説明が少なめなのでUnityのドキュメントをざっと撫でてから始めるとよさそうです。
乗り換え
ほぼUNetと同等のインターフェースなのですが、より便利にしたり思想的に異なったりする部分があり、そのままでは動かないこともあります。ここでは主に自分が気づいた部分について紹介します。
おしながき
挙動の変更
・PlayerがないとSpawnされない
・DontDestroyOnLoadなやつはSpawnされない
コードの変更
・NetworkSettingsAttribute廃止
・SyncListStructがSyncListに
・MsgTypeの代わりにSend<T>で型で区別する
ほとんど影響のない変更
・通信プロトコル変更化
・Time系のサービス追加
・一部のシリアライズ系の命令削除
*ロビーやマッチング周りはもともと使ってなかったのでノータッチです。
PlayerがないとSpawnされない
UNetにはプレイヤーの概念がありますが特に用意しなくても動作していました。しかしMirrorではプレイヤーを作らないとクライアント側でオブジェクトがSpawnされません。
これは次のようにクライアントプレイヤーをチェックして何を見せるかを判断するようになっているためで、プレイヤーの位置や状態に応じた部分同期や不正なクライアントによるセキュリティリスクを考慮しているようです。
1.クライアントからサーバーにプレイヤー追加を通知
2.クライアントプレイヤーをチェックしてSpawnするオブジェクトの選定
3.選定したオブジェクトをクライアント側でSpawn
別にプレイヤーいらないんだけどーって場合はとりあえず空のプレイヤーを用意してやりすごすのが良さそうです。
DontDestroyOnLoadなやつはSpawnされない
タイミングによっては消したはずのプレイヤーがDontDestroyOnLoadにいてこれを拾ってしまうためのようです。オフラインシーンになっても次オンラインシーンに復帰したら継続して・・・ってやつで結構使ってたので個人的になかなか痛い変更です。
NetworkSettingsAttribute廃止
[NetworkSettings(channel=1,sendInterval=0.05f)]
public class NetStreamer : NetworkBehaviour
{
...
}
replace it with:
public class NetStreamer : NetworkBehaviour
{
void Start()
{
syncInterval = 0.05f;
}
}
SyncListStructがSyncListに
UNetがnamespaceを考慮せずにSyncListStructを触ってしまうため(おそらく同居しているプロジェクトを想定して)名前を変えたかったようです。
また、SyncList<T>.Callbackにitemが渡るようになってます。
public delegate void SyncListChanged(Operation op, int itemIndex, T item);
ほかにもSyncDictionaryなどが増えていたりSyncList系はいい感じに発展しています。
MsgTypeの代わりにSend<T>で型で区別する
UNetでは独自のメッセージをやりとりするのにMsgTypeをほかと被らないように作る必要があったのですが、これがメッセージの型から自動生成されるようになっていて不要になりました。便利!
こんなのが、
public class MyMsgType {
public static short Score = MsgType.Highest + 1;
};
~
NetworkServer.SendToAll(MyMsgType.Score, msg);
https://docs.unity3d.com/ja/current/Manual/UNetMessages.html
こんなふうに書けるようになってます。
NetworkServer.SendToAll(msg);
通信プロトコル変更化
トランスポート層を別ライブラリ扱いにしていてユーザーが自由に選べます。選択肢も充実しています。
Time系のサービス追加
UNetでは一足はやく消滅したNetwork.timeに変わるものが用意されています。またRTTが取れたり機能追加されています。
一部のシリアライズ系の命令削除
次のメソッドがなくなっています。
NetworkWriter.SeekZero()
NetworkReader.readMessage<T>()
基本的にライブラリ内で使用されるものなので影響あるケースは少なそうですが、たまたま自分は使っていたので気づきました。
余談
UNetの頃からWeaverという名前で変なタイミングでエラーを吐くことがよくあったのですが今回Mirrorを見ててこれがなにかわかりました。なんとビルドした実行ファイルを書き換えてるんですね。
Mono.CecilというコンパイルしたILをいじれるライブラリを使って、Messageのシリアライザや前述のMsgTypeなしでも動くコードを生成しているようです。前者なんかはリフレクションで動的にでも出来そうですがもしかしたらパフォーマンスを気にしてるのかもしれません。
Mono.Cecilについてはこちらのサイトわかりやすいです。
これテストとか、いざってときにめっちゃ使えそうですね。
まとめ
自分が検証して気づいたUNetとMirrorの相違点を中心に書いてみました。乗り換えを検討をしてる方の参考になれば幸いです。
また、これからネットワーク対応を検討してる方にはUnityがフローチャートを出してるのでこちらも参考になるかもしれません。