UNET⑫ NetworkManagerHUDを自作のUIに置き換える
UNETのNetworkManagerHUD(あのクソダサいデフォルトのUI)を見限って、自作のUIで接続などを行えるようにします。
※なお今回作るのはLAN接続前提のUIです。インターネット接続のゲームを作りたい場合は、この記事に加えて、こちらの記事も参考にしてみてください。
あと、ついでに、UNETにおけるシーンの切り替え方法も学びます。
NetworkMangerHUDの仕事
NetworkManagerHUDを自作のUIに置き換えるには、NetworkManagerHUDがどのような仕事をしているのかを把握しなければなりません。
OnGUIを使っているせいで無駄に読みづらいソースになっていますが、やっていることは大したことなくて、基本的に、NetworkManagerの以下の3つのメソッドを呼び出しているだけです。
- StartServer
- StartHost
- StartClient
メソッドの効果は名前の通りです。
作成準備
では作っていきましょう。
新規プロジェクトを作成します。
空のオブジェクトを追加し、名前をNetworkManagerとします。NetworkManagerコンポーネントをアタッチします。
UIの作成
UGUIを使って、次のようなUIを作成してください。
あとでスクリプトから検索する関係上、黄色下線のオブジェクトは名前を厳守でお願いします。
あと、親子関係も上図の通りにしてください(Button3つとInputFieldをMainUIsという空のオブジェクトの子とする。ConnectingTextは子にしない)
スクリプトの作成
UIの配置が終わったら、空のオブジェクトを作成し、「MyNetworkManagerHUD」という名前にしてください。
新規スクリプトを作成・アタッチします。名前はMyNetworkManagerHUDとしましょう。
using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; public class MyNetworkManagerHUD : MonoBehaviour { // ボタン等の主なUIを含むGameObject GameObject m_MainUIs; // 接続中であることを示すUIのGameObject GameObject m_ConnectingText; // 接続状態種別 enum ConnectionState { // 純粋なサーバーとして起動中 Server, // ホスト(サーバー兼クライアント)として起動中 Host, // リモートクライアントとして接続確立済み RemoteClientConnected, // リモートクライアントとして接続試行中 RemoteClientConnecting, // 接続なし Nothing, } // ネットワーク接続の状態を取得する ConnectionState GetConnectionState() { // サーバーが起動しているか? if (NetworkServer.active) { // クライアントとして接続しているか? if (NetworkManager.singleton.IsClientConnected()) { // ホストとして起動中 return ConnectionState.Host; } else { // サーバーとして起動中 return ConnectionState.Server; } } // クライアントとして接続しているか? else if (NetworkManager.singleton.IsClientConnected()) { // リモートクライアントとして接続確立済み return ConnectionState.RemoteClientConnected; } else { NetworkClient client = NetworkManager.singleton.client; // Connectionが存在するか? if (client != null && client.connection != null && client.connection.connectionId != -1) { // 接続試行中 return ConnectionState.RemoteClientConnecting; } else { // 接続なし(何もしていない) return ConnectionState.Nothing; } } } void Start() { m_MainUIs = GameObject.Find("MainUIs"); m_ConnectingText = GameObject.Find("ConnectingText"); } void Update() { ConnectionState state = GetConnectionState(); // 接続試行中 if (state == ConnectionState.RemoteClientConnecting) { m_MainUIs.SetActive(false); m_ConnectingText.SetActive(true); // Escapeキーで接続中止 if (Input.GetKeyDown(KeyCode.Escape)) { NetworkManager.singleton.StopHost(); } } else { m_MainUIs.SetActive(true); m_ConnectingText.SetActive(false); } } // 「サーバーとして起動」ボタンが押された時の処理 public void OnServerButtonClicked() { NetworkManager.singleton.StartServer(); } // 「ホストとして起動」ボタンが押された時の処理 public void OnHostButtonClicked() { NetworkManager.singleton.StartHost(); } // 「サーバーへ接続(クライアント)」ボタンが押された時の処理 public void OnClientButtonClicked() { InputField input = GameObject.Find("ServerAddressInputField").GetComponent<InputField>(); NetworkManager.singleton.networkAddress = input.text; NetworkManager.singleton.StartClient(); } }
現在の接続状態に応じてUIを出したり消したりする必要があるため、現在の接続状態を調べるGetConnectionStateメソッドを作っています。
サーバーに接続試行中の場合は、それを示すUIを表示し、ボタン類は非表示にしています。それ以外の状況では、逆に、接続中を示すUIは隠し、ボタン類を表示しています。
あとは、ボタンが押されたときに関数が呼ばれるようにUnity上で設定していきます。
「サーバーとして起動」ボタンが押されたらOnServerButtonClickedが呼ばれるようにします。
同様に、「ホストとして起動」ボタンが押されたらOnHostButtonClickedが呼ばれるようにします。
「サーバーへ接続(クライアント)」ボタンが押されたらOnClientButtonClickedが呼ばれるようにします。
オフラインシーン/オンラインシーン
オフラインシーンとオンラインシーンの設定を行っていきます。オフラインシーンとは、オンラインになる前、つまり接続する前のシーンのことです。オンラインシーンとは、接続後のシーンのことです。これらをNetworkManagerに登録しておくと、接続や切断のタイミングで、NetworkManagerがシーンを切り替えてくれます。
先程まで作っていたシーンを保存しましょう。名前は「Title」としてください。
新規シーンの作成
新しいシーンを作成します。
プレイヤープレハブの作成
適当にCubeかCapsuleなどを追加し、名前をPlayerとします。
NetworkIdentityコンポーネントをアタッチします。
Playerという名前で新規スクリプトを作成・アタッチします。
using UnityEngine; using UnityEngine.Networking; public class Player : NetworkBehaviour { void Update() { if (!isLocalPlayer) return; // Escapeキーで切断する if (Input.GetKeyDown(KeyCode.Escape)) { // 切断は、サーバーもクライアントもStopHost()でOK NetworkManager.singleton.StopHost(); } } }
Escapeキーでネットワークを切断するだけのシンプルなプログラムです。
スクリプトが書けたら、Playerオブジェクトをプレハブ化し、シーンからは除去しておいてください。
シーンの保存
どんな名前でも良いのですが、今回は説明の都合上、Stage1という名前でシーンを保存してください。
シーンの登録
メニュー>File>Build Settings...を開き、TitleシーンとStage1を登録してください。
Titleシーンを開き、NetworkManagerを選択し、Offline Scene欄にTitleシーンを、Online Scene欄にStage1シーンを設定してください。
Playerプレハブも登録してください。
以上で完了です。
動作確認
ゲームを複数起動して、動作確認してみてください。
実行中のシーンの切り替え
続けて、オンラインプレイ中のシーンの切り替えを試してみましょう。
新規シーンの作成
新規Sceneを作成し、内容は何でも良いのですが、とにかくStage1とは違うシーンなんだということがわかるようにしてください。
シーンを保存します。名前はStage2としましょう。
BuildSettings画面に登録してください。
シーン切り替え機能の作成
Stage1を開き、空のオブジェクトを追加します。名前は「ChangeSceneSample」とでもしましょう。
ChangeSceneSampleオブジェクトにNetworkIdentityコンポーネントをアタッチします。
Server Onlyをオンにします。シーンの切り替えはサーバーにのみ許された操作だからです。
さらに新規スクリプトを作成・アタッチします。名前は「ChangeSceneSample」とでもしましょう。
using UnityEngine; using UnityEngine.Networking; public class ChangeSceneSample : NetworkBehaviour { void Update () { if (Input.GetKeyDown(KeyCode.Space)) { NetworkManager.singleton.ServerChangeScene("Stage2"); } } }
NetworkManagerのServerChangeSceneメソッドを使ってシーンを切り替えます。これを使うと、全クライアントで一斉にシーンが切り替わります。
この操作はサーバー上で呼び出したときのみ有効に動作します。リモートクライアントで呼び出すと、エラーにはなりませんが、正常に動作しません(自分だけシーンが切り替わる)。
動作確認
ゲームを複数起動して動作確認してみてください。サーバー側でSpaceキーを押すと、シーンが切り替わります。
なお、ServerChangeSceneを使ってシーンを切り替えると、プレイヤーオブジェクトは古いシーンと共に一度破棄され、新しいシーン読み込み後に再生成されます。つまり、プレイヤーオブジェクトの変数等は初期化されます。もしそれが困る場合は、DontDestroyOnLoadをしておきましょう。NetworkManagerは、プレイヤーオブジェクトが無いときだけ再生成するため、同じプレイヤーオブジェクトが2つ作られてしまうといったようなことはありません。