Chapter 02

🔰 PUN2の予備知識

o8que
o8que
2021.04.03に更新

オンラインゲームを開発する上で、実装が必要になる(オフラインのゲーム開発にはない)処理は、大雑把に分けると接続(🤝)と同期(🔄)の2つです。

  • ネットワークに接続して、他プレイヤーと通信できるようにする
  • 他プレイヤーとデータを送受信して、ゲームの状態を同期する

Unityにはこれらの実装を楽にする機能が揃っているアセット(ネットワークライブラリ)がいくつか存在していますが、その中でも利用者が多く使いやすいオンラインゲーム開発の入門に最適なアセットが、PUN2(Photon Unity Networking 2)になります。

https://blogs.unity3d.com/jp/2020/09/08/choosing-the-right-netcode-for-your-game/

🤝 接続

Photonのサーバーには、ネームサーバーとマスターサーバーとゲームサーバーの3種類があります。まずネームサーバーへ接続し、ネームサーバーから適切なリージョン(国・地域)のマスターサーバーへ転送、そしてマスターサーバーでプレイヤーのマッチメイキングを行い、ルームが作成されているゲームサーバーへ転送するのが基本的な流れです。プレイヤーは、同じルームへ参加している他プレイヤーとのみデータを送受信して同期処理を行うことができるので、いかにスムーズに各プレイヤーを適切なルームへ参加させていくかが、接続処理の主な目的になります。

プレイヤーが同時に2つ以上のサーバーに接続できないことに注意してください。

  • リージョンを選択 : ネームサーバーを切断してマスターサーバーへ接続
  • ルームへ参加 : マスターサーバーを切断してゲームサーバーへ接続
  • ルームから退出 : ゲームサーバーを切断してマスターサーバーへ接続

マスターサーバーへ接続している間に他プレイヤーとデータを送受信することはできませんし、ゲームサーバーへ接続している間にマッチメイキングを行うことはできません。

https://doc.photonengine.com/ja-jp/pun/current/connection-and-authentication/regions

ネームサーバー

Photon Cloudは世界中の様々なリージョンにサーバーが設置されています。日本在住のプレイヤーは、日本に設置されているサーバーに接続すれば快適に通信ができますが、もし遠い国に設置されているサーバーに接続してしまうと非常に不安定な通信になってしまうでしょう。ネームサーバーでは、多数のリージョンの中から適切なリージョンを選択して、そのリージョンのマスターサーバーへの転送が行われます。これは全て自動的に行われるため、ゲーム開発者側で意識する必要はほとんどありません。そのため本書ではこれ以降、最初にマスターサーバーへ接続するものとして説明を行っていきます。

チュートリアルより抜粋
private void Start() {
    // PhotonServerSettingsの設定内容を使ってマスターサーバーへ接続する
    PhotonNetwork.ConnectUsingSettings();
}

マスターサーバー

マスターサーバーは、現在ゲームを遊んでいるプレイヤーの総数や、ゲームサーバーに作成されている全てのルームの情報を監視しています。ここで新しいルームを作成したり、既に存在するルームへランダムで参加したり、またはロビーに参加して、既に存在するルーム一覧から参加するルームを選んだりすることができます。プレイヤーはルームへ参加する時に、そのルームが作成されているゲームサーバーへ転送されます。

チュートリアルより抜粋
// マスターサーバーへの接続が成功した時に呼ばれるコールバック
public override void OnConnectedToMaster() {
    // "Room"という名前のルームに参加する(ルームが存在しなければ作成して参加する)
    PhotonNetwork.JoinOrCreateRoom("Room", new RoomOptions(), TypedLobby.Default);
}

ゲームサーバー

ルームは全てゲームサーバーに作成されます。ルームへ参加したプレイヤーは、ゲームサーバーを通して同じルームへ参加している他プレイヤーとデータを送受信して、ゲームの状態を同期させていきます。ここでプレイヤーがルームから退出した時は、元のマスターサーバーへ再び転送されます。

チュートリアルより抜粋
// ゲームサーバーへの接続が成功した時に呼ばれるコールバック
public override void OnJoinedRoom() {
    // ランダムな座標に自身のアバター(ネットワークオブジェクト)を生成する
    var position = new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f));
    PhotonNetwork.Instantiate("Avatar", position, Quaternion.identity);
}

🔄 同期

ルームへ参加したプレイヤーは、ゲームサーバーを通して同じルームへ参加している他プレイヤーとデータを送受信できるようになります。ここからルームやプレイヤーの情報を取得して更新したり、プレイヤーが生成したネットワークオブジェクトの機能を活用したりして、ゲームの状態を同期させる処理を行っていきます。

プレイヤー

ルームに参加したプレイヤーは、ルーム内でユニークなIDが割り当てられます。プレイヤーはプレイヤー名の他に、例えば、プレイヤーの獲得したスコアや、チーム戦のゲームであればプレイヤーが所属するチームのIDなど、各プレイヤーごとに持たせる値の同期に使うことができます。

チュートリアルより抜粋
private void Start() {
    var nameLabel = GetComponent<TextMeshPro>();
    // プレイヤー名とプレイヤーIDを表示する
    nameLabel.text = $"{photonView.Owner.NickName}({photonView.OwnerActorNr})";
}

また各ルーム内では、プレイヤー1人にマスタークライアントが割り当てられていて、ルーム内のプレイヤー1人だけに実行させたい同期処理を行う場合に活用されます。

ネットワークオブジェクト

ネットワーク上で同期させるゲームオブジェクトはネットワークオブジェクトと呼ばれます。自身側でインスタンスを生成すると他プレイヤー側でもインスタンスが生成されて、逆に他プレイヤー側でインスタンスを生成すると自身側でもインスタンスが生成されます。ネットワークオブジェクトは、各プレイヤーのアバターの他に、例えば、ゲームのマップ上に存在するアイテムや、NPCの敵キャラクターなど、様々なゲームオブジェクトの同期に使うことができます。

チュートリアルより抜粋
// ゲームサーバーへの接続が成功した時に呼ばれるコールバック
public override void OnJoinedRoom() {
    // ランダムな座標に自身のアバター(ネットワークオブジェクト)を生成する
    var position = new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f));
    PhotonNetwork.Instantiate("Avatar", position, Quaternion.identity);
}

ネットワークオブジェクトには所有権があり、ネットワークオブジェクトを生成したプレイヤーがデフォルトでは所有者として紐づいています。ルームに紐づく(所有者を持たない)ルームオブジェクトと呼ばれるネットワークオブジェクトを生成することもできます。

🌶 Photon Cloudでバトルロイヤルゲームは作れるか?

Photon Cloudの無料プランは最大20人まで同時接続できますが、1ルームに10~20人以上が集まって対戦するようなオンラインゲームの開発は(不可能ではありませんが)かなり難易度が高くなります。

メッセージ数の制限

Photon Cloudには、1ルームの秒間メッセージ数500という制限があります。ここでカウントされるメッセージ数は、サーバーが送受信したメッセージ数の合計になります。例えば、参加プレイヤー数が4人のゲームで、あるプレイヤーが他3人のプレイヤーにデータを送信する場合、サーバーは1メッセージを受信して3メッセージを送信するので、4メッセージがカウントされます。

1ルームの秒間メッセージ数Mは、各プレイヤーの秒間送信メッセージ数Sと、1ルームの参加プレイヤー数Nから、

M=SN^2

で表せます。これは1ルームの参加プレイヤー数が増えると、ルーム内のメッセージ数が指数関数的に増加することを意味します。そして、この秒間メッセージ数を500以下に抑えるとすると、

S \leqq \frac{500}{N^2}

を満たすように、各プレイヤーの秒間送信メッセージ数を調整する必要があります。参加プレイヤー数が4人なら、約30回と高速な対戦アクションを作れるだけの十分な余裕がありますが、参加プレイヤー数が10人になると、最大5回でうまく同期する仕組みを考えなければなりません。参加プレイヤー数が20人にもなると、2回で制限をオーバーしてしまうため、様々な通信量削減のテクニックやトリックを駆使しなければ、まともに同期することすら困難になるでしょう。

これは見方を変えれば、各プレイヤーの秒間送信メッセージ数が1回前後で同期できるゲームなら、参加プレイヤー数が20人以上でも動かせるということでもあります。例えば、ターンベースのゲームや、セミターンベース(行動ゲージが溜まるとコマンドが選択できるようなもの)のゲームなどで、大人数オンラインゲーム開発にチャレンジしてみても良いかもしれません。

https://www.photonengine.com/ja-JP/pun/pricing

転送量超過料金

では、実際に1ルームの秒間メッセージ数が500を超えたらどうなるのか?という話が、以下の公式フォーラムで議論されています。

https://forum.photonengine.com/discussion/12597/message-limits-per-room-enforced
  • 1ルームの秒間メッセージ数500は、弱い制限(soft limit)である
  • メッセージ数が500を超えても、Photon Cloudのサーバーに悪影響がなければOK
  • サーバーに悪影響があれば、Photon運営側から修正依頼の連絡をする
  • (無料プランでも)転送量超過料金がかかる可能性があるので、細心の注意を払うこと

とのことなので、自己責任において、1ルームの秒間メッセージ数が500を超えるようなオンラインゲームを開発することは可能です。ただし、もしサーバーに悪影響があるほどの負荷をかけてしまった場合に、問題を修正できるだけの知識やスキルがなければ、ゲームがリリースできなくなったり、公開中止にせざるを得なくなることになるかもしれません。さらに、1ルームの参加プレイヤー数が増えると、もちろん転送量も指数関数的に増加するため、多額の転送量超過料金が発生してしまう可能性もあります。もし大人数オンラインゲーム開発にチャレンジするつもりであれば、これらのリスクは十分に理解しておく必要があるでしょう。

バトルロイヤルゲームの実例

手前味噌ですが、PUN2で開発したカジュアルなバトルロイヤルゲームをunityroomに無料で公開していますので、良かったら遊んでみてください。

https://unityroom.com/games/last-man-skating2