Netcode for GameObjects を追加
自分のプロジェクトに Netcodeを追加
https://docs-multiplayer.unity3d.com/docs/migration/install
2020.3.x の場合
Package Manager > + Add packages from git url
com.unity.netcode.gameobjects
を入力して追加!
1.0.0-pre.5が追加されました。
Host, Clientを自動で切り替えて自動で接続するのを待つ
とりあえず自動で Network.Singleton.StartHost() と Network.Singleton.StartClient()を順番に繰り返して、選択なしで接続できました。
Max Connections
Max Connections を 3にすると Hostに3clients接続できるので同時に4人対戦ということになります。u
同じ設定では、2対戦同時に行えないので、portを設定して実行させればローカルで複数の対戦を同時にできると思います。
左は 7778で接続2 , 右は 7777で接続4 でできました。
2,3秒で切り替えているので30秒接続できなかったらエラーで終了するようにします。
メッセージを出してトップメニューに戻る
接続したらシーン変更してゲーム開始!
NetworkSceneManagerあたりを使えそう?
NetworkManager.Singleton.SceneManager.LoadScene("NextSceneName", UnityEngine.SceneManagement.LoadSceneMode.Single);
Host側で LoadScene すると client側も反映されました。
接続承認を行う connectionApproval
今回は必要ないけど、パスワード認証して拒否したりもできる様子
と思ったのですが、接続上限やゲーム状態によって接続できないようにする必要がありました!
- 接続上限
- ゲーム開始後は枠があっても拒否(参加可能状態かチェックする必要がある
接続上限設定などで利用
メッセージを送る CustomMessagingManager
OverflowException: Writing past the end of the buffer
Unity.Netcode.FastBufferWriter.WriteValueSafe (System.String s, System.Boolean oneByteChars)
文字を送ろうとして
var writer = new FastBufferWriter(sizeof(byte) * payloadByteCount, Unity.Collections.Allocator.Temp); writer.WriteValueSafe(payload, false); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage(nameof(ReceiveClientToServerSetPlayer_CustomMessage), NetworkManager.Singleton.ServerClientId, writer);
こんな感じで stringを送ろうとして sizeのところが・・・ダメらしく。
しかしここを maxSize 1300にしたらいけました。仕様?バグ?しかしpriority:lowのようで?
それ以上のものは NetworkBehaviour で同期して通信したらどうかなという感じのようです?
MLAPIの方では普通にできているようですが
自動接続しようとしてるのでわりとタイミングがめちゃくちゃに・・・難しいですね^^;
ローカルなので対面でタイミングを取るので戻るがあればあまり難しくはないのかな・・・
- shutdownのタイミングでHandler解除
- isListeningになったっらHandlerを登録
- onConnectedClientなどのOnのhandlerは isListening前、startまでに登録
ざっくり順番は
OnServerStartedなど規定ハンドラー登録 ↓ StartHost ↓ OnClientConnectedCallback ↓ OnServerStarted ↓ IsListening ↓ CustomMessagingManager.RegisterNamedMessageHandlerでカスタムハンドラー登録
こんな感じでした。
isHost, isClient, isConnectedClient
StartHostしたものは isHost(isServer), isClientで
OnClientConnectedCallback は呼ばれるけど isConnectedClientではない。なぜか。
StartClientしたものがHostに接続すると isClient かつ isConnectedClientになっている。
ん
ゲーム制御
イメージとしてユーザー操作を反映する部分はclientからの操作でHostに依頼
全体の制御は Hostの操作かなと clientからの依頼もHostが反映と
prefabは登録しておかないといけないので
- UNet → Netcode で変更点もあり
- NetworkIdentity → NetworkObject など
- NetworkTransformは使わず NetworkBehaviour を継承したスクリプト内で状態更新
自分のゲームは自動で動くものなので、ユーザー操作を必要とするオブジェクトだけ NetworkBehaviourを使えば良いのかなx 同期を必要とするものはすべてNetworkBehaviourが必要
NPO的な感じになるけどどれくらいのオブジェクトを出して良いものかなど
オブジェクトの生成 Object spawning
- prefabを作成
- prefabに NetworkObjectを追加
- NetworkManagerの prefab listに追加
- 以下のスクリプトでSpawnする
GameObject go = Instantiate(myPrefab, Vector3.zero, Quaternion.identity); go.GetComponent<NetworkObject>().Spawn();
Player Object を登録すると 自動的に生成されるらしい。必須かどうかはわからない。
Player Object プレイヤーを生成
生成した後、権限をクライアントごとに振り分けても良い気もしますが、あるので使ってみます。
自動で生成されるのですが、これを制御できないので ///
最初のシーンでは使わないので一旦カメラ外に・・・
シーンを跨いでも消えない? 消えないようです。
シーンを跨ぐとオブジェクトは全て消えると書いてあったけどnetwork objectは大丈夫なのか?player objectが大丈夫なのかな?と。
とりあえず出来たので、データの同期を取ります。
外から見えるパラメータはすべて同期が必要かな、タイミングはそれぞれあるけど、まとめるには。
生成タイミング、データ同期タイミング
まずgameobjectの生成タイミングが違う。
NetworkManagerと連れてきたPlayerが先に生成されてから、ローカルのObjectが生成されている?
playerにはローカルのobjectをみてメニューなど制御もするので ローカルのobject(StageManagerこれは個人の作ったオブジェクト)
StageManagerの準備を待ってからplayerのデータを同期したりしたい。
ので、playerにフラグを入れて、ローカル準備ができた時点で playerのフラグを変更する、フラグの変更を合図に
ざっくり
// 例えばプレイヤースクリプトという名前だとして class Player : NetworkBehaviour { public NetworkVariable<int> team = new NetworkVariable<int>(0); public bool readyStageManager = false; int localTeam = 0; private void Start() { // 最初にハンドラー登録、サーバーからteam, teamは初回にclientごとに1度設定されます。 team.OnValueChanged += UpdateTeamForSetup; } public void SetTeam(int myTeam) { // 自分のプレイヤーのみ設定 if(IsLocalPlayer) { if( IsServer ) { team.Value = myTeam; } else { // クライアントからはサーバーへリクエスト SubmitSetTeamRequestServerRpc(myTeam); } } } [ServerRpc] void SubmitSetTeamRequestServerRpc(int myTeam) { team.Value = myTeam; } void UpdateTeamForSetup(int prevTeam, int newTeam) { // localTeam = newTeam; // チームを設定したらゲームを開始してしまう。 StartGame(); } async void StartGame() { // しかしローカルのstagemanagerというゲームオブジェクトの準備ができていないとエラーになるのでフラグの更新を待ってから実際の開始となる。 await UniTask.WaitUntil(() => readyStageManager); core.Start(); playing = true; } }
ごちゃっと書いたので適当ですが、stagemanagerの準備と network内のプレーヤーのteam設定が両方完了した段階でゲームスタートできる状態にする。という感じで。
こんな感じで赤と青のプレイヤーは別ウインドウですが同期できました(分かりにくい・・・
ちなみに position, rotationもteam変数と同じようにサーバーから反映させる形で同期していきます。
ユーザー入力の関係ないところの動きはすべてサーバーからという感じになりそうです。
- なぜエラーかわからないときはプロジェクトを一旦閉じてみる・・・となぜか治っていることが多い。
- シーン遷移した時に変数の値が 0 になぜかなっている(オーナーが自分でないもの)。不明。一応使う前に初期化。
コメント
Unity初心者です。NetCodeを使ってみたいと思ったのでとても参考になりました!!
ところで実際にNetCodeを用いて軽いサンプルを作ってみたところ利用者全てが同じプレイヤーアバターしか使うことが出来なかったのですが、赤と青の色違いのプレイヤーを表示させるのはどのように行ったか知りたいです。
コメントありがとうございます^^
色違いのマテリアルを用意して
後からマテリアルを変更することで変更できます。
返信ありがとうございます!
試してみたいと思います!!