Unity Netcode for GameObjects ローカル対戦ゲームを作ってみる!

Unity

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あたりを使えそう?

Unity.Netcode.NetworkSceneManager | Unity Multiplayer Networking
Main class for managing network scenes when EnableSceneManagement is
NetworkManager.Singleton.SceneManager.LoadScene("NextSceneName", UnityEngine.SceneManagement.LoadSceneMode.Single);

Host側で LoadScene すると client側も反映されました。

接続承認を行う connectionApproval

Connection Approval | Unity Multiplayer Networking
With every new connection, Netcode for GameObjects (Netcode) performs a handshake in addition to handshakes done by the transport. This is to ensure that the N...

今回は必要ないけど、パスワード認証して拒否したりもできる様子

と思ったのですが、接続上限やゲーム状態によって接続できないようにする必要がありました!

  • 接続上限
  • ゲーム開始後は枠があっても拒否(参加可能状態かチェックする必要がある

接続上限設定などで利用

Limiting the maximum number of players | Unity Multiplayer Networking
Netcode for Gameobjects (Netcode) provides a way to implement a connection approval delegate that can reject incoming connections based on custom logic.

メッセージを送る CustomMessagingManager

Unity.Netcode.CustomMessagingManager | Unity Multiplayer Networking
The manager class to manage custom messages, note that this is different

OverflowException: Writing past the end of the buffer
Unity.Netcode.FastBufferWriter.WriteValueSafe (System.String s, System.Boolean oneByteChars)

(Netcode For Gameobjects) Getting "Reading past the end of the buffer" when using Custom Messaging
I appear to be misunderstanding how to use Custom Messaging. Allow me to explain fully in case someone can help with the bigger picture. I am...

文字を送ろうとして

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は登録しておかないといけないので

シングルプレイヤーゲームを Unity Multiplayer に変換 - Unity マニュアル
ここでは、新しい Unity Multiplayer ネットワークシステムを使用して、シングルプレイヤーゲームをマルチプレイヤーゲームに変換する手順について説明します。 ここで説明するプロセスは、ゲームのために実際に使用する単純化された、より高レベルのプロセスを説明します。 必ずしも、この通りに動作するとは限りませんが...
Migrating From UNet to Netcode for GameObjects | Unity Multiplayer Networking
Use this step-by-step guide to migrate your projects from Unity UNet to Netcode for GameObjects (Netcode) Sample code is provided as available. We also recommen...
  • UNet → Netcode で変更点もあり
  • NetworkIdentity → NetworkObject など
  • NetworkTransformは使わず NetworkBehaviour を継承したスクリプト内で状態更新

自分のゲームは自動で動くものなので、ユーザー操作を必要とするオブジェクトだけ NetworkBehaviourを使えば良いのかなx 同期を必要とするものはすべてNetworkBehaviourが必要

NPO的な感じになるけどどれくらいのオブジェクトを出して良いものかなど

オブジェクトの生成 Object spawning

Object Spawning | Unity Multiplayer Networking
In Unity, you typically create a new game object using the Instantiate function. Creating a game object with Instantiate will only create that object on that pl...
  1. prefabを作成
  2. prefabに NetworkObjectを追加
  3. NetworkManagerの prefab listに追加
  4. 以下のスクリプトで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 になぜかなっている(オーナーが自分でないもの)。不明。一応使う前に初期化。

接続ロジック Host , Client 接続画面を追加

コメント

タイトルとURLをコピーしました