UNET⑫ NetworkManagerHUDを自作のUIに置き換える

UNETのNetworkManagerHUD(あのクソダサいデフォルトのUI)を見限って、自作のUIで接続などを行えるようにします。

f:id:motoyamablog:20171125231950p:plain
※なお今回作るのはLAN接続前提のUIです。インターネット接続のゲームを作りたい場合は、この記事に加えて、こちらの記事も参考にしてみてください。

あと、ついでに、UNETにおけるシーンの切り替え方法も学びます。

NetworkMangerHUDの仕事

NetworkManagerHUDを自作のUIに置き換えるには、NetworkManagerHUDがどのような仕事をしているのかを把握しなければなりません。

NetworkManagerHUDのソースはこちら

OnGUIを使っているせいで無駄に読みづらいソースになっていますが、やっていることは大したことなくて、基本的に、NetworkManagerの以下の3つのメソッドを呼び出しているだけです。

  • StartServer
  • StartHost
  • StartClient

メソッドの効果は名前の通りです。

 

作成準備

では作っていきましょう。

新規プロジェクトを作成します。

空のオブジェクトを追加し、名前をNetworkManagerとします。NetworkManagerコンポーネントをアタッチします。

 

UIの作成

UGUIを使って、次のようなUIを作成してください。

f:id:motoyamablog:20171125235253p:plain
あとでスクリプトから検索する関係上、黄色下線のオブジェクトは名前を厳守でお願いします。

あと、親子関係も上図の通りにしてください(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が呼ばれるようにします。

f:id:motoyamablog:20171126000829p:plain

同様に、「ホストとして起動」ボタンが押されたら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を登録してください。

f:id:motoyamablog:20171126002824j:plain

Titleシーンを開き、NetworkManagerを選択し、Offline Scene欄にTitleシーンを、Online Scene欄にStage1シーンを設定してください。

f:id:motoyamablog:20171126002939p:plain

Playerプレハブも登録してください。

以上で完了です。

動作確認

ゲームを複数起動して、動作確認してみてください。

f:id:motoyamablog:20171126004138g:plain

実行中のシーンの切り替え

続けて、オンラインプレイ中のシーンの切り替えを試してみましょう。

新規シーンの作成

新規Sceneを作成し、内容は何でも良いのですが、とにかくStage1とは違うシーンなんだということがわかるようにしてください。

f:id:motoyamablog:20171126010256p:plain

シーンを保存します。名前はStage2としましょう。

BuildSettings画面に登録してください。

f:id:motoyamablog:20171126010415j:plain

シーン切り替え機能の作成

Stage1を開き、空のオブジェクトを追加します。名前は「ChangeSceneSample」とでもしましょう。

ChangeSceneSampleオブジェクトにNetworkIdentityコンポーネントをアタッチします。

Server Onlyをオンにします。シーンの切り替えはサーバーにのみ許された操作だからです

f:id:motoyamablog:20171126010641p:plain

さらに新規スクリプトを作成・アタッチします。名前は「ChangeSceneSample」とでもしましょう。

using UnityEngine;
using UnityEngine.Networking;

public class ChangeSceneSample : NetworkBehaviour
{
    void Update ()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            NetworkManager.singleton.ServerChangeScene("Stage2");
        }
    }
}

NetworkManagerのServerChangeSceneメソッドを使ってシーンを切り替えます。これを使うと、全クライアントで一斉にシーンが切り替わります。

この操作はサーバー上で呼び出したときのみ有効に動作します。リモートクライアントで呼び出すと、エラーにはなりませんが、正常に動作しません(自分だけシーンが切り替わる)。

動作確認

ゲームを複数起動して動作確認してみてください。サーバー側でSpaceキーを押すと、シーンが切り替わります。

f:id:motoyamablog:20171126011953g:plain

なお、ServerChangeSceneを使ってシーンを切り替えると、プレイヤーオブジェクトは古いシーンと共に一度破棄され、新しいシーン読み込み後に再生成されます。つまり、プレイヤーオブジェクトの変数等は初期化されます。もしそれが困る場合は、DontDestroyOnLoadをしておきましょう。NetworkManagerは、プレイヤーオブジェクトが無いときだけ再生成するため、同じプレイヤーオブジェクトが2つ作られてしまうといったようなことはありません。

 

 

次へ進む

目次へ