Unityでオンラインマルチプレイなゲームを作りたい その5 ルーム作成

[前回の記事](https://qiita.com/koyuki2007/items/f0e16adc3938081eb06e)でロビーまで入室できるようになりました。
今回はルームの作成を実装しようと思います。

# 今回の目標
![Untitled Diagram_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469983/69a50471-3c68-3f99-4cd7-8558c13d8446.png)
画像上の赤〇で囲っている部分を実装していきます。

※イメージ図(UIが小さすぎますが気にしないでください)
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469983/4c28520b-db76-64b1-3ed8-f5f8f0d2dd34.jpeg)


# ルーム
http://www.monobitengine.com/doc/mun/contents/Reference/Glossary.htm#%E3%83%AB%E3%83%BC%E3%83%A0
実際にネットワーク処理で同期/共有を行うところになるようです。

# ルーム作成！の前に
すっかり忘れてた処理があるのでそちらのほうを先に追加しようと思います。

#### プレイヤーネーム
自身に名前を設定できます。ロビー内でプレイヤー検索を行えたり、ルーム内で共有できたりするので、つけておくと便利です。
http://www.monobitengine.com/doc/doxygen/client/html/class_monobit_engine_base_1_1_monobit_network.html#aa6c5e376fd4c53276f36218e2c8409c2
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469983/12334c2e-abdd-11dd-133b-753e50c46482.jpeg)

```c#
 MonobitNetwork.playerName = "お名前";
```
つけたい名前を代入するだけですね。

ということでタイトル画面に名前入力を追加します。
(本当はロビー画面やルーム作成/入室画面でやりたかったんですが、なぜかサーバー接続後からルーム入室するまでの間で、playerNameが反映されなかったので、タイトル画面で実装しています。リファレンスにはいつ設定しても有効って書いてあったんですが...。)

※プレイヤーネーム入力の個所以外の処理は省いてます

```c#
    /// <summary>タイトル画面</summary>
    public class SceneTitle : MonobitEngine.MonoBehaviour
    {
        /// <summary>ユーザー名入力時に呼ばれる</summary>
        /// <param name="userName">入力された文字列</param>
        private void OnInputUserName(string userName)
        {
            MonobitNetwork.playerName = userName;
        }
    }
```
<code>OnInputUserName</code>をInputFieldのイベントにでも設定しておいてください。

## ルーム作成
忘れてた処理が書けたのでルーム作成に戻ります。
###### ルームパラメータ
http://www.monobitengine.com/doc/mun/contents/FeatureClient/RoomParameter.htm
作成するルームの基本設定を行うものです。
###### ルームカスタムパラメータ
http://www.monobitengine.com/doc/mun/contents/FeatureClient/RoomCustomParameter.htm
ルーム検索時や入室条件等に使える、より詳細な設定を行うものです。

まずはルームの仕様を決め、この二つを設定していきます。

・1ルーム最大4人
・ゲーム開始後の途中参加は不可
・パスワード設定有り
・パスワードを設定した場合はプライベート扱い

とりあえずこんな感じですかね。

実際にコード上で設定します。
※例によってuGUI等の処理は省いてます。

```c#
/// <summary>ルーム作成画面</summary>
public class SceneCreateRoom : MonobitEngine.MonoBehaviour
{
    /// <summary>ルーム設定情報</summary>
    static private RoomSettings m_RoomSettings;

    /// <summary>ルーム名</summary>
    private string m_RoomName = string.Empty;

    // Start is called before the first frame update
    void Start()
    {
        if (!MonobitNetwork.inLobby) { Debug.Log("Not in lobby"); }

        m_RoomSettings = new RoomSettings() {
             // 公開か非公開かを設定できる
             isVisible = true,

             // 入室を許可するかを設定できる
             isOpen = true,

             // 作成するルームの最大人数を4人に設定する
             maxPlayers = 4,

             // パスワードとして利用するためのカスタムパラメータを作成
             roomParameters = new Hashtable() { { "password", "empty" }, },

             // 上記だけだと、ルーム外からカスタムパラメータを扱うことが出来ないので、扱えるように設定する。
             lobbyParameters = new string[] { "password" }
         };
    }
}
```
<code>RoomSettings</code>に必要な情報を設定するだけです。
※一応<code>MonobitNetwork.inLobby</code>([その3](https://qiita.com/koyuki2007/items/247c13603244020f6bb8)参照)でロビーに入室されているかどうかを出力するようにしています。
この設定を用いてルームを実際に作成する処理を入れます。

http://www.monobitengine.com/doc/mun/contents/FeatureClient/CreateRoom.htm

```c#
MonobitEngine.MonobitNetwork.CreateRoom("roomName", settings, lobby);
```
この関数を呼ぶことで実際にルームの作成を行います。

ということでルーム作成をコードに追加します。

```c#
/// <summary>ルーム作成画面</summary>
public class SceneCreateRoom : MonobitEngine.MonoBehaviour
{
    /// <summary>ルーム設定情報</summary>
    static private RoomSettings m_RoomSettings;

    /// <summary>ルーム名</summary>
    private string m_RoomName = string.Empty;

    // Start is called before the first frame update
    void Start()
    {
        if (!MonobitNetwork.inLobby) { Debug.Log("Not in lobby"); }

        m_RoomSettings = new RoomSettings() {
             // 公開か非公開かを設定できる
             isVisible = true,

             // 入室を許可するかを設定できる
             isOpen = true,

             // 作成するルームの最大人数を4人に設定する
             maxPlayers = 4,

             // パスワードとして利用するためのカスタムパラメータを作成
             roomParameters = new Hashtable() { { "password", "empty" }, },

             // 上記だけだと、ルーム外からカスタムパラメータを扱うことが出来ないので、扱えるように設定する。
             lobbyParameters = new string[] { "password" }
         };
    }

    /// <summary>ルーム名入力時に呼ばれる</summary>
    /// <param name="roomName">入力された文字列</param>
    private void OnInputRoomName(string roomName)
    {
        m_RoomName = roomName;
    }

    /// <summary>プライベート設定の変更時に呼ばれる</summary>
    /// <param name="isOn">トグルの状態</param>
    private void OnCheckPrivateSetting(bool isOn)
    {
        m_PasswordInputFieldObj.SetActive(isOn);
        if (!isOn) { m_RoomSettings.roomParameters["password"] = "empty"; }
    }

    /// <summary>パスワード入力痔に呼ばれる</summary>
    /// <param name="password">入力された文字列</param>
    private void OnInputPassword(string password)
    {
        if (!m_RoomSettings.roomParameters.ContainsKey("password")) { return; }

        m_RoomSettings.roomParameters["password"] = password;           
    }

    /// <summary>作成ボタンが押された際に呼ばれる</summary>
    private void OnClickCreate()
    {
        // 引数にはルーム名、Start()内で設定したルーム設定、ロビーはデフォルトのロビーなためnull、を入れています。
        MonobitNetwork.CreateRoom(m_RoomName, m_RoomSettings, null);
    }
}
```
ボタンクリック時にルームが作成されるようにしています。
※ついでにルーム名とパスワードを入力する処理も追加しています。

仕上げにルーム作成後のコールバックを追加します。
http://www.monobitengine.com/doc/mun/contents/FeatureClient/CallbackFunction.htm#OnCreatedRoom%20%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89

```c#
public void OnCreatedRoom();
```
ルームの作成に成功した際に呼び出されます。

http://www.monobitengine.com/doc/mun/contents/FeatureClient/CallbackFunction.htm#OnCreateRoomFailed%20%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89

```c#
public void OnCreateRoomFailed(object[] codeAndMsg)
```
ルームの作成に失敗した際に呼び出されます。

この二つ + ルーム作成画面の状態でサーバーから切断した時用に<code>OnDisconnectedFromServer</code>([その3](https://qiita.com/koyuki2007/items/247c13603244020f6bb8)参照)も追加します。

```c#
/// <summary>ルーム作成画面</summary>
public class SceneCreateRoom : MonobitEngine.MonoBehaviour
{
    /// <summary>ルーム設定情報</summary>
    static private RoomSettings m_RoomSettings;

    /// <summary>ルーム名</summary>
    private string m_RoomName = string.Empty;

    // Start is called before the first frame update
    void Start()
    {
        if (!MonobitNetwork.inLobby) { Debug.Log("Not in lobby"); }

        m_RoomSettings = new RoomSettings() {
             // 公開か非公開かを設定できる
             isVisible = true,

             // 入室を許可するかを設定できる
             isOpen = true,

             // 作成するルームの最大人数を4人に設定する
             maxPlayers = 4,

             // パスワードとして利用するためのカスタムパラメータを作成
             roomParameters = new Hashtable() { { "password", "empty" }, },

             // 上記だけだと、ルーム外からカスタムパラメータを扱うことが出来ないので、扱えるように設定する。
             lobbyParameters = new string[] { "password" }
         };
    }

    /// <summary>ルームが作成された際に呼ばれるコールバック</summary>
    public void OnCreatedRoom()
    {
        SceneManager.LoadScene("RoomWait");
    }

    /// <summary>ルームの作成に失敗した際に呼ばれるコールバック</summary>
    /// <param name="codeAndMsg">エラーコード</param>
    public void OnCreateRoomFailed(object[] codeAndMsg)
    {
        Debug.Log("OnCreateRoomFailed : errorCode = " + codeAndMsg[0] + ", message = " + codeAndMsg[1]);
    }

    /// <summary>MUNサーバーとの接続を切った際に呼ばれるコールバック</summary>
    public void OnDisconnectedFromServer()
    {
        SceneManager.LoadScene("Title");
    }

    /// <summary>ルーム名入力時に呼ばれる</summary>
    /// <param name="roomName">入力された文字列</param>
    private void OnInputRoomName(string roomName)
    {
        m_RoomName = roomName;
    }

    /// <summary>プライベート設定の変更時に呼ばれる</summary>
    /// <param name="isOn">トグルの状態</param>
    private void OnCheckPrivateSetting(bool isOn)
    {
        m_PasswordInputFieldObj.SetActive(isOn);
        if (!isOn) { m_RoomSettings.roomParameters["password"] = "empty"; }
    }

    /// <summary>パスワード入力痔に呼ばれる</summary>
    /// <param name="password">入力された文字列</param>
    private void OnInputPassword(string password)
    {
        if (!m_RoomSettings.roomParameters.ContainsKey("password")) { return; }

        m_RoomSettings.roomParameters["password"] = password;           
    }

    /// <summary>作成ボタンが押された際に呼ばれる</summary>
    private void OnClickCreate()
    {
        // 引数にはルーム名、Start()内で設定したルーム設定、ロビーはデフォルトのロビーなためnull、を入れています。
        MonobitNetwork.CreateRoom(m_RoomName, m_RoomSettings, MonobitNetwork.lobby);
    }

    /// <summary>戻るボタンが押された際に呼ばれる</summary>
    private void OnClickBack()
    {
        SceneManager.LoadScene("Lobby");
    }
}
```
ルーム作成成功後に次の画面であるRoomWaitに飛ぶようにしてあげてます。
失敗した場合はエラーログを出すだけですね。
サーバーが切断された場合はタイトル画面に戻るようにしています。
ルームを作成した場合、ルームの作成者は自動で入室してくれるので何か別途入室処理を行う必要はないようです。

これでルームの作成が実装できました。
次回は作成されたルームへ入室するところをやっていこうと思います。

# 資料
http://www.monobitengine.com/doc/mun/contents/Reference/Glossary.htm#%E3%83%AB%E3%83%BC%E3%83%A0
http://www.monobitengine.com/doc/doxygen/client/html/class_monobit_engine_base_1_1_monobit_network.html#aa6c5e376fd4c53276f36218e2c8409c2
http://www.monobitengine.com/doc/mun/contents/FeatureClient/RoomParameter.htm
http://www.monobitengine.com/doc/mun/contents/FeatureClient/RoomCustomParameter.htm
http://www.monobitengine.com/doc/mun/contents/FeatureClient/CallbackFunction.htm#OnCreatedRoom%20%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89
http://www.monobitengine.com/doc/mun/contents/FeatureClient/CallbackFunction.htm#OnCreateRoomFailed%20%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89