はじめに
LiteNetLib4Mirrorとは、LiteNetLibという.NET用のUDPライブラリを、Unity用ネットワークライブラリMirrorのLow level networking transportとして使えるようにするものです。特に、Mirrorには無いNetwork DiscoveryとuPnPをサポートしてくれるのが嬉しいので、その辺の話を中心にメモします。なお、原稿執筆時のLiteNetLib4Mirrorのバージョンは1.2.5です。
LiteNetLib4Mirrorの入手
こちらからUnityPackageをダウンロードして、自分のプロジェクトにインポートしてください。ただし、これにはサンプルシーンが含まれないので、初心者はこちらのレポジトリをクローンして試してみるのが良いと思います。使うUnityのバージョンはUnity 2018.3.6f1以降が推奨です(IL2CPPでのIPv6サポートの問題のため)。私は執筆時点でUnity2018.3.8f1を使ってます。
サンプルシーンから学ぶ
公式ドキュメントが少ないので、サンプルシーンを勉強してみましょう。
レポジトリをクローンしてプロジェクトを開くとAssetフォルダに"Scene"というシーンのファイルがあるので開いてください。Mirrorに付属のPongデモと同じようなシーンが開きます。
ただし、NetworkManagerゲームオブジェクトにアタッチされてるコンポーネントに、以下のような違いがあります。
LiteNetLib4MirrorTransport
MirrorのPongデモではTelepathy Transportがアタッチされてますが、こちらではLiteNetLib4MirrorTransportがアタッチされています。このコンポーネントはLiteNetLib4Mirrorを使う上で必須であり、必ずNetwork Managerと同じゲームオブジェクトにアタッチされている必要があります。
コンポーネントのパラメータについては、以下の参考資料が役に立つかもしれません。
127.0.0.1とlocalhostと0.0.0.0の違い
UPnPでNAT越え ~オンラインゲーム開発における課題~
LiteNetLib4MirrorDiscovery
Network Discoveryを行うためのコンポーネントです。On Discovery Responseを設定しておくことで、Hostが見つかった後の処理を行えます。
NetworkDiscoveryHUD
これがあると、シーンに"Start Discovery"というボタンが表示され、これをClient側で押すとHostを探し始めてくれます。
Network Manager
このコンポーネント自体はMirrorのPongデモにもありますが、Network Infoの"Transport"がLiteNetLib4MirrorTransportになっているのに注意してください。
ためしてみる
とりあえず、プロジェクトをビルドして試してください。ビルドしたアプリを二つ起動させて、一方をClient、一方をHostにしてみましょう。Host側で"LAN Host"、Client側で"Start Discovery"を押せば接続されてPongが始まります。
Network Discoveryの方法
サンプルシーンのNetworkDiscoveryHUD.csが参考になります。この中のOnGUIメソッドの中に、Discoveryを始めるか止めるかの処理があります。コード中の_noDiscoveringは、初期状態ではtrueです。
//OnGUI内
if (!NetworkClient.isConnected && !NetworkServer.active){
if (_noDiscovering){
if (GUILayout.Button("Start Discovery")){
StartCoroutine(StartDiscovery());
}
}
else{
GUILayout.Label("Discovering..");
GUILayout.Label($"LocalPort: {LiteNetLib4MirrorTransport.Singleton.port}");
if (GUILayout.Button("Stop Discovery")){
_noDiscovering = true;
}
}
}
else{
_noDiscovering = true;
}
"Start Discovery"ボタンを押すと、StartDiscoveryメソッドがコルーチンとして実行されます。
StartDiscoveryメソッドでは、LiteNetLib4MirrorDiscoveryコンポーネントのInitializeFinder()で初期化し、Singleton.onDiscoveryResponse.AddListenerでOnClientDiscoveryResponseをイベントハンドラとして登録した後に、LiteNetLib4MirrorDiscovery.SendDiscoveryRequestでリクエストを送信し続けます。
private IEnumerator StartDiscovery()
{
_noDiscovering = false;
LiteNetLib4MirrorDiscovery.InitializeFinder();
LiteNetLib4MirrorDiscovery.Singleton.onDiscoveryResponse.AddListener(OnClientDiscoveryResponse);
while (!_noDiscovering)
{
LiteNetLib4MirrorDiscovery.SendDiscoveryRequest("NetworkManagerHUD");
yield return new WaitForSeconds(discoveryInterval);
}
LiteNetLib4MirrorDiscovery.Singleton.onDiscoveryResponse.RemoveListener(OnClientDiscoveryResponse);
LiteNetLib4MirrorDiscovery.StopDiscovery();
}
サンプルでは、LiteNetLib4MirrorDiscovery.SendDiscoveryRequestの引数として"NetworkManagerHUD"を与えてるのですが、他の文字列を与えても動作に影響しないです。理由は調査中です
(追記: shienaさんのコメントにあるとおり、与えた引数はホスト側のLiteNetLib4MirrorDiscoveryのProcessDiscoveryRequestに渡されるようです。ProcessDiscoveryRequestの中では渡された文字列を使わないので何を与えていても影響ないですが、LiteNetLib4MirrorDiscoveryを継承したクラスを作る場合、この文字列を使って処理を行うこともできます。)
いずれにせよDiscoveryが上手くいくと、登録したOnClientDiscoveryResponseにホストのIP Addressが渡されるので、Mirrorの通常の方法でクライアントを起動できます。
private void OnClientDiscoveryResponse(IPEndPoint endpoint, string text)
{
string ip = endpoint.Address.ToString();
NetworkManager.singleton.networkAddress = ip;
NetworkManager.singleton.maxConnections = 2;
LiteNetLib4MirrorTransport.Singleton.clientAddress = ip;
LiteNetLib4MirrorTransport.Singleton.port = (ushort)endpoint.Port;
LiteNetLib4MirrorTransport.Singleton.maxConnections = 2;
NetworkManager.singleton.StartClient();
_noDiscovering = true;
}
#おわりに
多分、もっと色々なことができるんだと思いますが、サンプルから理解できたのはこれくらいです。公式サンプルが追加されたら、こちらにも情報追加します。