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

Page Not Found | Unity Multiplayer Networking
NetworkManager.Singleton.SceneManager.LoadScene("NextSceneName", UnityEngine.SceneManagement.LoadSceneMode.Single);

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

接続承認を行う connectionApproval

Page Not Found | Unity Multiplayer Networking

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

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

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

接続上限設定などで利用

Page Not Found | Unity Multiplayer Networking

メッセージを送る CustomMessagingManager

Page Not Found | Unity Multiplayer Networking

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 ネットワークシステムを使用して、シングルプレイヤーゲームをマルチプレイヤーゲームに変換する手順について説明します。 ここで説明するプロセスは、ゲームのために実際に使用する単純化された、より高レベルのプロセスを説明します。 必ずしも、この通りに動作するとは限りませんが...
Page Not Found | Unity Multiplayer Networking
  • UNet → Netcode で変更点もあり
  • NetworkIdentity → NetworkObject など
  • NetworkTransformは使わず NetworkBehaviour を継承したスクリプト内で状態更新

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

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

オブジェクトの生成 Object spawning

Page Not Found | Unity Multiplayer Networking
  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 接続画面を追加

お気軽にコメントください!

スパム対応のためコメント認証に数日かかることがありますが、お気軽にコメントいただけると嬉しいです^^

コメント

  1. 紅鮭 より:

    Unity初心者です。NetCodeを使ってみたいと思ったのでとても参考になりました!!
    ところで実際にNetCodeを用いて軽いサンプルを作ってみたところ利用者全てが同じプレイヤーアバターしか使うことが出来なかったのですが、赤と青の色違いのプレイヤーを表示させるのはどのように行ったか知りたいです。

    • 編集者じゃっく より:

      コメントありがとうございます^^
      色違いのマテリアルを用意して
      後からマテリアルを変更することで変更できます。

  2. 紅鮭 より:

    返信ありがとうございます!
    試してみたいと思います!!

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