UNET MatchMakerを使ってインターネット経由で接続する

ネットワークゲーム」とひと口に言っても、いろんな接続方法があります。まあ、遊ぶ側の視点でネットワークゲームと言えば、インターネット経由での接続が当たり前ですが、様々な理由により、それはそんな気軽に開発できるものではありません。だからUNETの初心者向けの解説資料のほとんどがLAN(ローカルエリアネットワーク)接続前提です。当然、LAN接続では、同じ施設内でしかプレイできません。でも、やっぱりインターネット経由で、世界中の人とプレイするゲームが作りたい!そう誰もが願うはずです。

その願いを簡単に叶えてくれるのが、Unity MultiplayerおよびMatchMakerです。

今回はMatchMakerの使い方を解説します。

用語の整理

Unity Multiplayer

まず前提として、UNETは、MMORPGのように、同じ世界に数千~数万人が同時に存在するようなゲームは、たぶん想定していません。UNETが想定しているのは2~10人程度で、一回のプレイ時間が数分~数十分程度の対戦または協力ゲームです。まあ一般的なFPS、TPS、その他対戦ゲーム(格闘ゲームを除く)、協力ゲームのほとんどと同じです。

で、そういったゲームで他のプレイヤーと遊ぶ場合、遊ぶ相手を探すロビーの機能か、自動マッチングの機能が必要になるでしょう。そしてそれを実現するには、インターネット上にサーバーが必ず必要になります。これを提供するWebサービスの名称が「Unity Multiplayer」です(正確に言うと、マッチングだけでなく、通信のリレーも行ってくれます)。

有料サービスですが、お試し程度のアクセス数(同時プレイ人数20人まで)であれば無料で使えます。

MatchMaker

Unity Multiplayerを使うためのUNETの機能のことです。

Match

Match(マッチ)。これは、たぶん日本人ゲーマーの感覚から言うと、「ルーム(部屋)」と言ったほうがわかりやすいです。

自動マッチングではなく、プレイヤーが自分で対戦相手を選ぶゲームの場合、大抵は次のように、ルームによるマッチングになると思います:

  1. 最初のプレイヤーはルームを作成する
  2. 後から来たプレイヤーは既存のルームの中から好きなものを選んで参加するか、もしくは自分で新たなルームを作成する
  3. ゲーム開始のタイミングは、ルームに必要な人数が集まったら自動で始まるパターンや、ホスト(ルームを作った人)がボタンを押したら始まるパターン、全員が準備完了したら始まるパターンなどがある

以上が「ルーム」の概要です。PCでネットワークゲームをやる人にはおなじみかと思います。で、UNETが「Match」と呼んでいるのはこのルームのことです。

CCU

Concurrent Users、その瞬間に接続しているユーザー数のことです。

Unity Multiplayerの設定

クラウドサービスの登録

WebブラウザでUnityの開発者サイトにアクセスします。

https://developer.cloud.unity3d.com/projects/

使うのがはじめての場合は、次のような画面になります。

f:id:motoyamablog:20180121231835p:plain

「Create New Project」ボタンを押します。

プロジェクト名欄には、作っているゲームの名前などを入力します。

f:id:motoyamablog:20180121232329p:plain

画面が切り替わります。
「GOT IT(了解)」を押します。
f:id:motoyamablog:20180121232511p:plain

Multiplayer>Configurationを押します。

f:id:motoyamablog:20180121232814p:plain

利用規約に同意します。

f:id:motoyamablog:20180121232854p:plain

1ルームの最大人数を入力してSaveを押します。

f:id:motoyamablog:20180121233152p:plain

こういう画面になれば、準備OKです!

f:id:motoyamablog:20180121233539p:plain


もうブラウザは閉じても構いません。

 

プロジェクトのリンク

ここからはUnityエディター上での作業です。
まず、Unityにサインインしていない場合は、サインインしてください。
f:id:motoyamablog:20180121225205p:plain

サインイン済みの状態で、雲のアイコンを押すと、Servicesタブが開きます。

f:id:motoyamablog:20180121233753p:plain

「I already have a Unity Project ID」をクリックします。
f:id:motoyamablog:20180121233946p:plain

1個目のリストから組織を選ぶと、2個目のリストにて、先程Webブラウザ上で作ったProjectが選べるようになっているはずです。

f:id:motoyamablog:20180121234046p:plain
選んだらLinkを押します。

f:id:motoyamablog:20180121234225p:plain
選んだプロジェクトと、今Unityエディタ上で作っているプロジェクトを結びつけて良いのか?というダイアログです。Yesを押します。

成功すると、こんな感じの画面になって、各種サービスが使用可能となります↓

f:id:motoyamablog:20180121234445p:plain

 

とりあえずMatchMakerを使ってみよう

とりあえずMatchMakerを使って、インターネット経由で本当につながるのかを試してみましょう。以下は、まっさらなプロジェクトで始める例です。

Playerの作成

Cubeを新規作成して、名前を「Player」にします。

Network Identityをアタッチして、Local Player Authorityをオンにします。

f:id:motoyamablog:20180121235551p:plain

Network Transformをアタッチします。

新規C#スクリプト「Player」を作成し、Playerオブジェクトにアタッチします。最低限の動作確認用のプログラムです。

using UnityEngine;
using UnityEngine.Networking;

public class Player : NetworkBehaviour
{
    void Update()
    {
        if (isLocalPlayer)
        {
            // 左右キーでx方向に移動、上下キーでy方向に移動する
            Vector3 motion = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0) * Time.deltaTime;
            transform.Translate(motion);
        }
    }
}

Playerオブジェクトをプレハブ化し、シーン上からは削除します。

NetworkManagerの作成

空のオブジェクトを作成し、「NetworkManager」という名前にします。

NetworkManagerコンポーネントとNetworkManagerHUDコンポーネントをアタッチします。

NetworkManagerのPlayer Prefab欄にPlayerのプレハブを登録します。

f:id:motoyamablog:20180122000350p:plain

以上です!!!

動作確認

ビルドして実行ファイルを作成するなどして、ゲームを複数立ち上げます。

「Enable Match Maker」をクリックします。

f:id:motoyamablog:20180122000951p:plain

まずはルームを作成します。Room Name欄に部屋名としてふさわしい文字列を入力し、「Create Internet Match」を押します。

f:id:motoyamablog:20180122001215p:plain

ルームが作成されると同時に、自分はそのルームに入り、ゲームが始まります。

f:id:motoyamablog:20180122001843p:plain

他方のゲーム画面でも、「Enable Match Maker」をクリックします。

f:id:motoyamablog:20180122001915p:plain

他のプレイヤーが作成したルームに参加したい場合は、「Find Internet Match」をクリックします。

f:id:motoyamablog:20180122002005p:plain

 すると、さきほど、別のプレイヤーが作成したルームが表示されます。(もしルームが複数ある場合はその全てが一覧表示されます)

f:id:motoyamablog:20180122002035p:plain

ルーム名の書いてあるボタンを押すと、そのルームに参加します。

f:id:motoyamablog:20180122002133p:plain

動作確認は以上です!

実感がわかないかもしれませんが、これはインターネット経由での接続です!可能ならば是非、別のPCから、別のネットワーク経由で接続してみてください!

PCを2台以上持っているなら、片方を有線でインターネットに繋ぎ、もう片方をスマホテザリングPocket WiFiで繋ぐとかですかね。

もしくは、Androidアプリとしてビルドして、PCとAndroidで接続してみても良いです(PCとスマホでも、一切プログラムを書き換えること無く、繋がるはずです)。

NetworkMatchクラス

というわけで、MatchMakerを使って接続するのは、NetworkManagerHUDを使えばとても簡単にできることがわかりましたが、真剣にネットワークゲームを作る場合は、あのダサいUIを使うわけにはいきません。自作する必要があります。そのためには、NetworkMatchクラスの使い方を理解する必要があります。

NetworkMatchの使い方を理解するには、公式リファレンスまたはNetworkManagerHUDのソースが参考になります。

とりあえず動作確認するための最低限のサンプルはこちらです。

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Networking.Match;

public class MyMatchMaker : MonoBehaviour
{
    // 取得したMatch情報を格納するためのList
    List<MatchInfoSnapshot> m_Matches = null;

    void Start()
    {
        // MatchMakerを使うための準備
        NetworkManager.singleton.StartMatchMaker();
    }
    
    void Update()
    {
        // CキーでMatch(ルーム)を新規作成する
        if (Input.GetKeyDown(KeyCode.C))
        {
            NetworkManager.singleton.matchMaker.CreateMatch("Room Name", 8, true, "", "", "", 0, 0, OnCreateMatch);
        }

        // Lキーで存在するMatchの一覧を取得する
        if (Input.GetKeyDown(KeyCode.L))
        {
            NetworkManager.singleton.matchMaker.ListMatches(0, 10, "", true, 0, 0, OnListMatches);
        }

        // Jキーで存在するMatchに接続する
        if (Input.GetKeyDown(KeyCode.J))
        {
            // とりあえず見つかった一番上のMatchに参加する
            NetworkManager.singleton.matchMaker.JoinMatch(m_Matches[0].networkId, "", "", "", 0, 0, OnJoinMatch);
        }
    }

    // CreateMatch()が終わった時に呼ばれる関数
    void OnCreateMatch(bool success, string extendedInfo, MatchInfo matchInfo)
    {
        if (success)
        {
            print("CreateMatch()成功");

            NetworkManager.singleton.StartHost(matchInfo);
        }
        else
        {
            print("CreateMatch()失敗");
        }
    }

    // ListMatches()が終わったときに呼ばれる関数
    void OnListMatches(bool success, string extendedInfo, List<MatchInfoSnapshot> matches)
    {
        if (success)
        {
            print("ListMatches()成功。");
            print("見つかったMatchの数は:" + matches.Count);

            m_Matches = matches;

            // とりあえず取得したMatchの名前を列挙する
            foreach (var match in m_Matches)
            {
                print(match.name);
            }
        }
        else
        {
            print("ListMatches()失敗");
        }
    }

    // JoinMatch()が終わった時に呼ばれる関数
    void OnJoinMatch(bool success, string extendedInfo, MatchInfo matchInfo)
    {
        if (success)
        {
            print("JoinMatch()成功。");

            NetworkManager.singleton.StartClient(matchInfo);
        }
        else
        {
            print("JoinMatch()失敗");
        }
    }
}

まず、NetworkMatchのインスタンスはNetworkManagerが持っている(matchMakerという名前)ので、それを使えば良いでしょう。

Matchを作るにはCreateMatch()、既存のMatchの情報を取得するにはListMatches()、既存のMatchに参加するにはJoinMatch()という関数を呼びます。インターネット通信が発生するため、全ての関数は非同期です。処理が完了すると、引数に渡したコールバック関数が呼ばれます。

いずれの関数も引数が多いです。詳細は公式リファレンスを参照してください。Match(ルーム)を名前でフィルタリングしたり、パスワードをかけたり、プレイヤーのスキルレベル(いわゆるレート)に合わせてMatch情報を返却したりできるようです。意外に高機能です(ちゃんと正しく動くのかは未検証ですが・・・)。

Unity Multiplayerの特徴

というわけで、信じられないくらい簡単にインターネット経由での接続が出来てしまいました。

インターネット経由でPCとPCを繋ぐって、実はめちゃくちゃめんどくさいんです。NAT越えという恐ろしい課題があります。(今「NAT越え」でググったら、任天堂のページが一番上でちょっと笑った。おそらくSwitchでスプラトゥーン2をやりたいボーイたちがNAT越え問題で泣かされているのだろう。そしてそれを鮮やかに解決することは任天堂ですらできないという、非常に厄介な問題です)

乱暴に説明すると、NATとはルータのことで、間にルータがあると、PC同士をつなぐのは非常にめんどくさいのです。

Unity Multiplayerはこの問題を回避するために、リレーサーバーを提供してくれています。リレーサーバーとは、ある端末から来たデータをそのまま他の端末へ送信するものです。

間にルータがあると、PC同士の通信は困難ですが、PCと公開されたサーバーの通信は簡単です(そうじゃないとインターネット使えません)。なので、リレーサーバーに仲介してもらうことによって、PC同士も情報のやり取りができます。

こんな便利ですごいUnity Multiplayerですが、問題点もあります。私が感じた問題点は次のような点です。

  1. 遅延問題。間にリレーサーバーが介在するので、どうしてもその分遅くなる。しかも、今(2018年1月)試したところ、リレーサーバーの場所はアメリカであるため、日本人同士が対戦する場合でも、一旦情報がアメリカを経由して戻ってくるという壮大な遠回りが発生する。国内にサーバー置いてほしい・・・
  2. 値段とかサービス内容がよくわからない。無料で使えるのは同時接続数20人までというのは間違い無さそうだけど、Unity Proユーザーがどうなってるのかとか、よくわからない。Unity Mutiplayerのサービスのトップページには「Proは200人まで」って書いてあるけど、200人って、ちょっと人気のあるゲームでは全くもって足りないでしょ。たとえば、PUBGなんて同時接続数300万人ですよ。まあ、PUBGと比較するのはおかしいけど、でも200人は少ない(200人以上いけそうな表記もあるけど、よくわからない)
  3. とにかく、情報が少ない😭 日本語の採用事例とか一件も見つからない。ってか、まだ商用利用はベータ版っぽい?

個人的には、リレーサーバーを使わずに、NAT越えさせて端末同士を直接接続(P2P)させたいです。そうすれば、通信も速いし、Unityのサーバー代もかからない。でも直接接続させる方法がわかりません・・・。NetworkMatchのリファレンス読んでると、直接接続を匂わす文言があるんだけど、やり方がさっぱりわかりません・・・

 

追記:

Web上を色々調べてたら、NAT越えして直接接続することについて、Unityの中の人が直接言及しているスレッドを見つけました。

要約すると次のような感じです。

  • 現状、Unity Multiplayerではリレーサーバーを介した通信のみサポートしている。
  • リレーサーバーを介さずにNATパンチスルーして直接接続する機能をUNETに追加することも検討・評価はしている
  • 「リレーサーバーを介した通信は遅い」という考えは必ずしも正しくない。インターネット網は非常に複雑であり、直接接続するよりリレーサーバーを介したほうが速い場合もありえる
  • 現在、米国、ヨーロッパ、シンガポールにデータセンターがあり、中国にも追加する予定(日本に追加してくれ😭)
  • NAT越えして直接接続するのは環境により不可能な場合も結構あるが、リレーサーバーを介した接続はほぼ100%成功する。
  • Unity Mutiplayerサービスでは、安定性と開発者の負担軽減のために、およびUnityの開発スケジュールの都合もあり、現状の仕様となっている。今後色々機能は追加されていく

とのことです。でもこれ2015年の投稿なんですよねぇ・・・(遠い目)

Unity社の人の発言ではありませんが、「リレーサーバーを使わずに直接接続したら、Unity社に金が入らない(だから直接接続はサポートしないだろう)」というのもありました(笑)

 

 

目次へ