Netcode for GameObjects(旧名MLAPI)のチュートリアルで大苦戦した記録

Netcodeのチュートリアル的な動画を自分でも実践してみましたが、かなり苦戦したので、せっかくなので記事として記録したいと思います。内容はレベルの低い話も多いかと思いますがご容赦くださいませ。

チュートリアル動画との差異

まず初めに前提として書いておかないといけないのは、苦戦した理由の一つに、UnityのStarter Assetsで再現しようとしたことがあります。

これは動画の中のサンプルが公開されていましたが、勉強のためにもUnityの標準アセットでも再現できれば、今後応用したいと思ったときに楽になると思ったからです(結果として大苦戦しますが🥺)

Starter Assetによる再現方法

もしかすると、この記事を見て自分で同じことをやって見たいという人がいるかもしれないので、構築方法を書いておきます。

環境
Windows10、Unity2021.2.5f1(NetcodeはUnity2021から対応)、Visual Studio Code

①Starter Assetを全てインポートしたら「Asset > Starter Assets > Environment > Prefabs > Environment_Prefab.prefab」を自分のシーンにドラッグ&ドロップして配置します。

②元からあるMainCameraを削除して「Asset > Starter Assets > ThirdPersonController > Prefabs > MainCamera.prefab」を自分のシーンにドラッグ&ドロップして配置します。

③「Asset > Starter Assets > ThirdPersonController > Prefabs > PlayerArmature.prefab」を自分のシーンにドラッグ&ドロップして配置します。

④このPlayerArmatureを編集したいので、自分の「Asset > Scenes」にドラッグ&ドロップして、バリアントとしてコピーします(コピーするときに選択肢が出ます)。

⑤最後に③で作ったコピー元のプレハブを削除します。

あとはチュートリアル動画の進む通りにNetworkManagerなどを入れていきます。

※以下は、チュートリアル動画を見た前提で書いていますのでご注意ください。

苦戦①InputSystemが動画と違う

Starter AssetのPlayerオブジェクトは、チュートリアル動画のようにUpdate()の中で入力を取得していません。

具体的には、ThirdPersonController.csで移動を、StarterAssetsInputs.csで入力を取得しています。

なので、この2つのスクリプトをMonoBehaviour→NetworkBehaviourに変更して、IsOwner、IsServerなどで制御する必要があります。

また、動画では古いInputSystem、Starter AssetはUnityの最新のInputSystemを使っています。

この新しいInputSystemには大いに苦しめられることになります。

苦戦②Visual Studio CodeがNetcodeをサポートしていない

バリアント化したプレイヤープレハブのThirdPersonController.cs、StarterAssetsInputs.csをコンポーネント削除をして、Starter Assetのプレハブからスクリプトを自分のところにコピーします。

元の名前と同じだとややこしいので、それぞれ適当にリネームして下さい。
(自分は接頭にMyを付けてリネームしました。この辺はお好みでどうぞ。)

そして、Visual Studio Codeでスクリプトを編集するわけですが、Netcode系、新InputSystem系のアセンブリ参照が見つからない問題にぶち当たります。

アセンブリ参照が出来ないので、コード補完や定義参照などができなくなる

これに関しては、最後まで解決できませんでしが、Unity側でのコンパイルには問題ないようです。

おそらくVisual Studio Code Editor がそのうち修正されて対応されると思います。

苦戦③IsServerの解釈違い

チュートリアル動画の説明では、難しいところがあったので自分なりに考えて解釈していたところ大はまりしたので、解釈違いを起こしていた部分とその解説を入れたいと思います。

まず理解が必要だと思ったのは、ServerRpcの挙動です。

以下のリンクを見て下さい。

ServerRpc | Unity Multiplayer Networking
https://docs-multiplayer.unity3d.com/docs/advanced-topics/message-system/serverrpc/index.html

ここの図を見ていただければ口で説明するより分かりやすいと思います。

[Unity.Netcode.ServerRpc]がついたSetMoveInputServerRpc()は、呼び出されたときに直接実行されるわけではなく、リンクの図のように(別のネットワークの)Serverで実行されます。

つまり、ClientでMoveInput()は実行されません。
全てServerでMoveInput()が実行されます。

StarterAssetsInputs.csの実例

ここでややこしいと頭を悩ませるのが、このスクリプト自体がServerでも使われ、Clientでも使われることです。

そこで、スクリプトをServer、Clientで分岐させる必要があります。

そのための関連フラグを軽く説明します。

IsOwener:スクリプトが実際に操作しているプレイヤーのものかどうか

マルチ実行中は操作プレイヤーだけでなく、他のプレイヤーもオブジェクトとして同じスクリプトを使って複数生成されます。

そうなると、キーボードなどで操作したい(入力を取得したい)オブジェクトを判別する必要があるので、そのために使われます。

このフラグの管理は、NetworkManagerによって行われます。

IsClient:クライアント⇔サーバーにおけるクライアント側(操作のみ)
IsServer:クライアント⇔サーバーにおけるサーバー側(処理の集約と展開)

動画の中では、Host、Clientで説明されるので自分は誤解してしまいましたが、HostとClientは対ではありません。

Hostは、IsServerであり、IsClientでもあります。
(自分は最初、HostはIsClientではないと思っていた。)

対になっているのは、ClientとServerであり、HostもまたClient扱いになります。

IsServerを使って、Move()をServer側(Host側)にだけ実行させます。

Client側の移動はどうなるかというと、全てオブジェクトに付けたNetworkTransformによってNetworkManagerにより、まずServerに全て集約され、ネットワーク経由で各々配信され行われます。

この辺りの理解はかなり誤解していました。

苦戦①に関連して、2つのファイルにわたって動画と実装が異なっているので注意して下さい。

ThirdPersonController.csの実例

苦戦④新InputSystemの仕様に苦しめられる

出来たアプリをビルド実行して(ウィンドウ化設定をした方がいい)、HostボタンでPlayerArmatureを作ります。

そして次に、Unityのエミュレーターを実行して、ClientボタンでPleyerArmatureが作られると、Client側ではHostで作られたものと合わせて2つ作成されます。

この時に、PleyerArmatureに設定されているInputSystemは、2つのオブジェクトそれぞれに存在するので、それぞれが操作できるように自動で割り振りを行います。

そうすると、1つ目はキーボードになり、なんと2つ目はXboxコントローラーに割り振られます。

この現象と、Netcodeが必ずHostキャラから生成する仕様と合わさり、Clientボタンで2番目に作成されたPleyerArmatureが操作できないという問題にぶち当たりました。

これには大いに苦しめられました。
(操作できない理由にたどり着くまでに紆余曲折がありました😭)

2つ目がXboxコントローラーになる件については、厳密には「操作無効になった状態をXboxとしてデバッグ出力していた」です。

この状態を何とかするために、関係ないオブジェクト(Client側から見たらHost側のオブジェクト)にInputSystemは必要ないので、プレハブのPlayerInputのenabledチェックを外しておいて、スクリプトの中でtrueにすることで解決しました。

最終的なPlayerArmatureのプレハブ設定(バリアント)詳細
GetComponet<PlayerInput>()を追加してInputSystemを呼び出し、Start()内でIsOwnerならtrueにする

まとめ

やった対応自体は、ほんの一握りのことですが、これにたどり着くまでに数十のサイトで調べに調べたので丸一日ぐらいの時間がかかりました。

疲れた。

願わくば、この記事が何かの役に立てばいいな😊

ちなみにチュートリアル動画のリレーサーバー以降は、ノーヒント状態なので再現するのは諦めました。


Comments

コメントを残す