😂

Photon Quantumの何がうれしいのか

2024/12/25に公開

これは「Unity Advent Calender 2024」の25日目の記事になります。

はじめに

Photon Quantum」は、世界初かつ唯一(執筆時点)のUnity向けオンラインマルチプレイヤーゲーム開発用の高性能な決定論的ECS(Entity Component System)フレームワークです。

https://www.photonengine.com/ja-jp/quantum

昔までのQuantumは、利用するために別途サブスクリプション契約が必要だったことなどもあり、SDKを触ることすら難しい面がありましたが、

などを経て、大規模な開発スタジオはもちろん、個人~小規模インディーチームからでも、eスポーツ品質のオンラインゲーム開発が手軽に始められる時代が到来しました!

……と手放しに言いたいものの、やはりオンラインゲーム開発の難易度は高めで、ある程度の前提知識がないと「Quantumの何が良いのか?」「従来のネットワークライブラリとは何が違うのか?」などが掴みづらい所もあるかと思います。ということで、この記事ではQuantumの魅力を理解するために必要となる基本的な話を順を追って紹介していきたいと思います。

オンラインゲーム開発で避けられない問題とは?

オンラインゲームの同期は、ネットワークを通して様々なデータを通信することで成立します。そのため、特にネットワークの「帯域」や「遅延」などは、制作できるオンラインゲームのジャンルや要素、ゲームプレイ体験に大きく影響を与えます。他のプレイヤーのデータが届くのが大きく遅れたり、ゲームを同期するのに必要なデータが適切なタイミングまでに届かなければ、ラグや同期ズレが発生し、快適にゲームが遊べなくなってしまうからです。

帯域

帯域幅」「回線速度」と呼ばれたり、実際に通信したデータの合計量は「データ通信量」「転送量」と呼ばれたりします。

ネットワーク上で一度に送受信できるデータ量の上限は、帯域によって決まります。例えば、大規模なバトルロイヤルゲームを何も最適化せずに同期するとして、プレイヤー100人がそれぞれ1キロバイトのデータを毎秒60回送信するとしたら、1秒間で6メガバイト(100 x 1000 x 60 = 6000000)のダウンロード速度が必要になります。回線速度が100Mbpsなら1秒間に約12メガバイトのデータを通信できるので普通にゲームが遊べるかもしれませんが、逆に回線速度が約50Mbps以下のプレイヤーは全員ゲームを遊ぶことすらできない状況になってしまうでしょう。

大容量の動画やゲームアプリ本体をダウンロードする時なら、通信にいくら時間がかかっても問題にはなりませんが、リアルタイムで同期が必要なデータを送受信する時には、時間をかけて通信するわけにはいかないので、オンラインゲームを同期するためのデータ量は、想定するメインターゲットのプレイヤーの回線速度に合わせて極力少なく抑えることが望ましいです。

遅延

レイテンシ」や「RTT(Round Trip Time:ラウンドトリップタイム)」と呼ばれたり、応答速度を「Ping(ピン、ピング)」の値で表したりします。

ネットワーク上でデータを送受信するには、必ず時間がかかります。自分自身がホスト(クライアント兼サーバー)なら例外的に遅延はゼロですが、日本国内のプレイヤー同士なら数ミリ秒~数十ミリ秒、異なる国のプレイヤー同士なら数十ミリ秒~数百ミリ秒の遅延は避けられません。フレームレートが60fpsのゲームなら、1フレームで処理できる時間は約16ミリ秒(1/60秒)なので、大抵の場合、他のプレイヤーのデータは数フレーム遅れて届くことになります。

そして、光の速度には限界があるため、現代の通信技術では(人類が光の速度を超えて情報を送る方法でも発明しない限り)この遅延をゼロにすることはできません。これが、主に格闘ゲーム界隈などで「光の速度は遅すぎる」と言われたりする理由でもあります。


グランドジャンプ編集部(@GrandJump)のポストより引用

帯域を削減するには?

オンラインゲームを同期するために必要なデータ量が多すぎると、回線速度が遅いプレイヤーはラグすぎてまともにゲームが遊べなくなります。さらに転送量の面から見ると、スマホゲームならプレイヤー側でギガの減りの早さが問題になったり、ゲーム開発者側で多額のサーバー費用が発生したりする可能性もあります。これらの問題を回避するためには、主に以下のような方法を組み合わせて、ゲームで使用される帯域を削減していく調整が必要になってきます。

  • 不要なデータは通信しない
  • 通信する頻度を落とす
  • 通信するデータ自体のサイズを小さくする

インタレストマネジメント

例えば、大規模なバトルロイヤルゲームやメタバースなどは、一度に参加できるプレイヤー数こそ多いものの、それ以上に広大なマップが用意されていることがほとんどです。こういったケースでは、「関心領域(AoI:Area of Interest)」と呼ばれるプレイヤーを中心とした特定の境界で区切られた範囲を定義して、遠くにいるプレイヤーとの通信頻度を落とし、さらに遠くの見えない場所にいるプレイヤーとは最低限の情報だけを通信するような仕組みを作ることで、帯域を劇的に削減することができます。この技術は「インタレストマネジメント(Interest Management)」などと呼ばれます。

インタレストマネジメントは帯域削減に効果的な技術の一つですが、その効果はゲームのジャンルや仕様などによって大きく変わってきます。同じ100人対戦のゲームでも、狭いマップの中で100人が密集して大乱闘するようなゲームを制作するとしたら、通信をスキップして済ませられるプレイヤーはほぼ居なくなり、無理に通信頻度を落としてしまうとラグが目立つようになってしまうこともあり、インタレストマネジメントはほとんど役に立たなくなります。それでも大人数が乱戦するようなオンラインゲームを作りたいんだ!というのなら、愚直にデータ自体のサイズを小さくしていく方法を取ることが必須になってくるでしょう。

差分圧縮

データ自体のサイズを小さくするなら、データを圧縮するのが効率的で、様々な圧縮アルゴリズムを使うことができます。オンラインゲームの通信データの圧縮でよく使われる「差分圧縮(Delta Compression)」は、データを送信する際に前回にデータを送信した時からの差分を取って、値が変わっていないデータの通信をスキップする(または、「前回から値が変わっていない」という小さなフラグのみを送信する)ことで、通信データのサイズを削減します。

ただし差分圧縮は、値が変更される頻度が少なかったり稀によくあったりするようなデータに対しては効果が大きいですが、プレイヤーが操作するキャラクターの位置など、高頻度で更新されたり値が常に変化し続けるようなデータに対してはほとんど効果がないことに注意が必要です。

「状態」と「入力」

オンラインゲームを同期するために通信するデータの内容は、ゲームの「状態(State)」と、コントローラーの「入力(Input)」の2つに大きく分類できます。例えば、プレイヤーが操作するキャラクターの位置や向きを同期する時には、以下のような方法のどちらかを選択できます。

  • キャラクターの位置と向きの値(ゲーム内の要素の状態)を直接送信する
  • コントローラーの方向キーの入力方向だけを送信する、そしてその入力を受信したプレイヤー側でキャラクターを移動(位置と向きを更新)する

以下の表は、FPS(一人称視点シューティング)ゲームにおける状態と入力の例です。

状態 入力
プレイヤーの名前
プレイヤーのキル数 / デス数
キャラクターの位置
キャラクターの向き
キャラクターの残り体力
キャラクターが持つ銃の種類
キャラクターが持つ銃の残弾数
発射された弾の位置
発射された弾の方向
マップに落ちているアイテムの種類
マップに落ちているアイテムの位置
試合の残り時間
など...
コントローラーの方向キー(アナログスティックの傾き)
コントローラーのボタンの押下

状態を通信する場合は、ゲーム内で同期が必要な要素が増えれば増えるほど、データサイズも比例して大きくなってしまいます。一方、入力を通信する場合は、ゲームの規模にはまったく影響を受けず、コントローラーのボタンが押されているかどうか?という非常に小さなデータだけを通信すれば済むので、通信するデータのサイズを大きく削減できます。

ここで「じゃあ、入力を通信するようにすれば、帯域の問題は大体解決するんじゃないの?」と思うかもしれません。実際それは間違いでもないのですが、それが従来はまったく簡単な話ではありませんでした。入力だけを通信してゲームを正確に同期するためには、非常に重要な条件を満たしている必要があって、その条件とは「同じ入力を与えたら、必ず同じ出力が得られる(必ず同じゲームの状態になる)」ことです。そして、この性質を専門的な用語で「決定論的(Deterministic)」であるといいます。

Unityは決定論的なのか?

Unityは決定論的ではありません。そのため、オンラインゲームのロジックを実装する時に、Unity標準で用意されている機能を1つでも使用した時点で、そのロジックが決定論的に動作する保証がなくなります。これが、入力だけを通信してゲームを正確に同期しようとする際の最大の問題点になります。

ゲームロジックが決定論的ではないのに、入力だけを通信してしまうと、致命的な同期ズレが発生する可能性があります。キャラクターの位置や向きを直接送信するかわりに、コントローラーの入力を送信しても、その入力を適用して移動した結果(キャラクターの位置や向き)がまったく違う値になってしまったら意味がありません。もし格闘ゲームで、同じコマンドを入力したのに必殺技が出たり出なかったり、入力毎に違う必殺技が出るとしたら、オンライン対戦の同期は成立しなくなってしまうでしょう。

可変フレームレート

Unityのフレームレートは可変です。設定(Application.targetFrameRate)で60fpsに指定したとしても、処理落ちが発生すればフレームレートは下がります。また、フレーム間の経過時間(Time.deltaTime)にも揺らぎがあります。プレイヤーの端末ごとにフレームレートが異なっているなら、そもそも「同じ入力を与える」こと自体が不可能で、フレームレートが同じでもフレーム間の経過時間が異なっているなら、「同じ入力を与える」ことはできても「同じ出力を得る」ことはできません。つまり、ゲームロジックを決定論的に動作させるには、ロジックを更新する頻度と間隔を固定することが必須になります。

浮動小数点数の誤差

一般的にコンピューターは「浮動小数点数(floating-point number)」で実数を扱います。これはUnityやC#でも同じで、小数には主にfloat型やdouble型が使われます。しかし浮動小数点数は「限られたデータサイズの中で広範囲の数値を表現しコンピューターで処理できるようにするかわりに、値を近似値で扱う」方式のため、様々な理由で誤差が発生します。そして、誤差が発生すると当然「同じ出力を得る」ことができなくなります。

浮動小数点数の誤差についての参考リンク

「でも、ほんのわずかにズレる程度だったら、問題にならないのでは?」と思うかもしれませんが、これが大問題になります。それを説明する有名な言葉が「バタフライエフェクト」です。「蝶1匹の羽ばたきによって生まれた小さな空気の乱れが、めぐりめぐって地球の反対側で大嵐を引き起こす」ように、「ほんのわずかでもゲームの状態がズレたままで入力の適用を繰り返していくと、ゲームの状態には非常に大きな違いが生まれる」ようになってしまうわけです。

以下はUnity Physics関連の動画ですが、決定論的(deterministic)であることの重要性が一目でわかる動画なのでここで紹介します。物理エンジンの動作が決定論的ではない動画前半と、決定論的に動作している動画後半とで、どの程度ズレに違いがあるのかを見比べてみてください。

https://www.youtube.com/watch?v=7hK1g8CvbgY

ここまでの話をまとめると、

  • オンラインゲームで使用される帯域を大きく削減するには、入力だけを通信するのが効果的な方法の一つになる
  • しかし、入力だけを通信してゲームを正確に同期するには、ゲームロジックが決定論的に動作している必要がある
  • しかし、ゲームロジックを決定論的に動作させるには、決定論的に動作するコードをUnityの機能を一切使わずに独自実装する必要がある
  • しかし、決定論的に動作するコードをUnityの機能を一切使わずに独自実装するのは、非常に難易度が高く大変な手間がかかるので、実際に採用できる開発プロジェクトは限られていた

ということになります。

Photon Quantumの特徴とは?

そしてここからようやくPhoton Quantumの話になるのですが、Quantumは「決定論的に動作するコードを実装するために必要となる機能一式を備えたフレームワーク」になります。従来は非常に難易度が高く大変な手間がかかっていた決定論的に動作するコードが、Quantumを使用することで誰でも手軽に実装できるようになったということです!

  • Unityとは独立して動作する、固定フレームレートのシミュレーション
  • 浮動小数点数を使わない「固定小数点数(fixed-point number)」型
  • 決定論的な数学ライブラリ
  • 決定論的な2D/3D物理エンジン
  • 決定論的なナビメッシュ/経路探索
  • 決定論的な「RNG(Random Number Generator:乱数生成器)」

などの機能を使用することで、Unity製ゲームのゲームロジックを決定論的に動作させられるようになり、その結果、入力だけでオンラインゲームを正確に同期できるようになります。

ネットコードの実装不要

入力だけを通信してゲームを同期するということは、入力だけを通信してゲームを同期するということです。つまり、従来のネットワークライブラリなどを使用してオンラインゲームを開発する際には、ゲームの状態を同期するためのコード(ネットワークを通して実際にどのようなデータを送信するか?受信したデータをどのように処理してゲームを同期するか?など)の実装が不可欠でしたが、それらの作業がすべて不要になります。

「複数のプレイヤーの入力が与えられて、その入力に基づいてゲームの状態を更新する」という処理の流れは、実はローカルマルチプレイヤーゲーム(ゲーム機にコントローラーを複数繋げて、1画面で遊ぶようなゲーム)とほぼ同じです。オンラインゲームはオフラインゲームに比べて数倍の工数(開発時間)がかかると言われますが、Quantumを使用してオンラインゲームを開発する場合は、オフラインのローカルマルチプレイヤーゲームと同じようにゲームロジックを実装できるため、(オフラインゲームと全く同じとまではいかないものの)実装工数の削減も期待できます。

チート耐性

入力だけを通信してゲームを同期するということは、状態を一切通信せずにゲームを同期するということです。そのため、ゲームの状態を不正に書き換えるようなメモリ改竄系のチートは、他のプレイヤーにまったく影響を与えません。例えば、チーターがゲームプレイ中に自分自身のライフ・攻撃力・移動速度などを10倍にしたとしても、他のプレイヤーにその書き換えた値は送信されないので、チーター以外のプレイヤーの画面上では正常なパラメーターのままゲームが進行することになります。チーター以外のプレイヤーからチーターを見ると、何もない壁に向かって全力ダッシュしながら銃を撃ちまくる変な人のように見えるかもしれませんが、ゲームの結果には何の影響もありません。

これは、アンチチートソフトウェアの動作とは違うことに注意してください。「チートを検出するプログラムを動かして、他のプレイヤーに悪影響を与えることを未然に防ぐ」のではなく「チートは自由に制限なく可能だが、その影響を他のプレイヤーに与える方法がない」だけです。チートを防止するための処理が裏側で走っているわけではないので、アンチチートソフトウェアのようにゲームが不安定になったり重くなったりするトラブルは起こりませんが、不正にスコアを書き換えて外部サーバーのハイスコアランキングに登録するようなことは普通にできるので、そういったケースでは別のちゃんとしたチート対策が必要になってきます。

予測とロールバック

決定論的なオンラインゲームでは「同じ入力を与えたら、必ず同じ出力が得られる」ようになっているわけですが、言い換えれば「同じ入力を与えられるようになるまで、同じ出力を得ることができない」ということでもあります。ゲームを進行するためには、自分以外のプレイヤー全員の入力を受信する必要があるわけですが、ネットワークの遅延によって全員の入力を受信するまでには時間がかかるので、その間はゲームが進行できずに停止してしまう可能性があるということです。

Quantumは、この問題を回避する方法として「予測(Prediction)」と「ロールバック(Rollback)」機能を標準で備えています。これらがどういった機能なのかは、まず以下の動画を見てもらうのがオススメです。(解説は英語ですが、日本語の字幕があります)

https://www.youtube.com/watch?v=0NLe4IpdS1w

自分以外のプレイヤー全員の入力を受信するまで待つことなく、未来を予測してゲームを進行した上で、全員の入力が揃ったら、入力が行われた時点まで過去にロールバック(巻き戻し)して正しい出力を得て、巻き戻る前の未来までもう一度予測を行って時間を進めます。これによって、実際にはネットワークの遅延が発生しているにもかかわらず、オンラインゲームを遊ぶプレイヤーには遅延をほとんど感じさせずに快適なゲーム体験を与えることができます。

Quantumに適したゲームジャンルとは?

Quantumは様々なジャンルのオンラインマルチプレイヤーゲームに適していて、特にネットワークの遅延の影響が大きいハイペースな対戦ゲームなどには最適です。

  • 格闘ゲーム
  • アクションゲーム
  • スポーツゲーム
  • FPS(一人称視点シューティング)
  • RTS(リアルタイムストラテジー)

本格的な対戦ゲーム以外に向かないのか?というとまったくそういうことはなく、カジュアルなオンラインゲームでも様々な恩恵を受けることができるでしょう。ここで手前味噌ですが、Quantumの技術デモを兼ねて自分が制作したフリーゲームを2つ紹介します。

PCブラウザで最大100人同時に遊べるかくれんぼゲーム「SALUTO -ソルト-」

Quantumは入力だけを通信してゲームを正確に同期できるので、狭いマップの中で100人近い大人数がリアルタイムで動き回るようなゲームでも、ネットワークの帯域が問題になることはありません。また、このゲームを公開した時点では日本サーバーが動いていなかったため、プレイヤーは全員Ping100を超えている(最低でも0.1秒以上の遅延が発生している)ような状況でしたが、ラグや同期ズレの問題はほとんど確認できませんでした。

https://x.com/o8que/status/1792581446789386333
https://unityroom.com/games/saluto
https://automaton-media.com/articles/newsjp/20240520-294005/
https://www.famitsu.com/article/202405/5930

大量の物理オブジェクトを同期するゲームがスマホで遊べる「フルーツまぜゲームオンライン」

従来のネットワークライブラリなどでゲームの状態を通信している場合は、リアルタイムで動き回る大量の物理オブジェクトを正確に同期することは非常に困難でした。Quantumには決定論的な物理エンジンが搭載されているので、最低限の入力を通信するだけで、大量の物理オブジェクトでも正確に同期できます。また、Quantumは通信するデータのサイズが非常に小さいため、スマホや携帯ゲーム機などの通信環境に制限があるモバイルゲームでも快適に動作します。

https://x.com/o8que/status/1825127175152091268
https://unityroom.com/games/fruit-mix-game-online

決定論的なゲームの欠点

ここまで、オンラインゲームを決定論的に動作させるメリットを色々話してきましたが、もちろん決定論的な動作にはデメリットも存在します。

  • 決定論的なオンラインゲームでは、「同じ入力を与えたら、必ず同じ出力が得られる」ようにする必要があります。たとえその時点で不要な処理だったとしても処理を省略することはできないため、(この記事の前半で紹介した)インタレストマネジメントのような技術は併用できません。仮に、遠くにいるプレイヤーだからといってそのプレイヤーの計算を省略してしまうと、出力が変わってしまうので決定論的ではなくなり、ゲームの状態は一切通信していないので計算を省略したことで生じる同期ズレを後から修正する方法はありません。決定論的に動作するゲームは、ゲームの規模(ゲーム内の要素の多さ)が各プレイヤー端末のCPUやメモリの負荷に大きく影響するので、大規模なバトルロイヤルゲームやメタバースなどを動作させるのは困難になるかもしれません。
  • 「同じ入力を与えたら、必ず同じ出力が得られる」ようにするには、各プレイヤーがゲーム内のすべての要素の情報を知っている必要があります。プレイヤー毎に持っているゲームの情報や状態が異なっていたら、それは決定論的ではなくなってしまうからです。遠くにいるプレイヤーの正確な位置情報や、カードゲームなら本来知りえない相手の手札の情報なども、各プレイヤー端末のメモリ上に保存されることになるため、ウォールハックなどの秘密の情報を盗み見るようなチートには弱くなってしまいます。

https://automaton-media.com/articles/newsjp/liars-bar-cheat-20241111-317561/

まとめ

Photon Quantumを使用して、オンラインゲームを決定論的に動作させることで、入力だけでゲームが正確に同期できるようになります。これによって、オンラインゲーム開発上の問題となっていた「帯域」は大きく削減され、ラグがほぼ感じられないほどに「遅延」が隠蔽されます。もちろん欠点もあり、あらゆるオンラインゲームのジャンルに適しているとは言えないものの、従来までは開発することが困難だったジャンルのオンラインゲームが簡単に実現できるようになるなど、非常に大きな利点がいくつもあるので、この記事を読んで少し興味が出てきた方は、ぜひPhoton Quantumを触ってみてください!

オススメ記事

https://blog.kyubuns.dev/entry/2024/02/24/204120
https://soysoftware.sakura.ne.jp/archives/2277

Photon運営事務局TechBlog

Discussion