Quantum 3 とは?
概要
Photon Quantumは、Unity製オンラインマルチプレイヤーゲーム向けの高性能な決定論的ECS(Entity Component System)フレームワークです。
予測/ロールバック方式を採用していて、アクションRPG・スポーツゲーム・格闘ゲーム・FPSなど、遅延の影響を受けやすいオンラインゲームに最適です。
Quantumはクリーンなコードの記述に役立ちます。シミュレーションロジック(Quantum ECS)と、ビュー/プレゼンテーション(Unity)を分離し、ネットワークの実装仕様(内部的な予測/ロールバック・トランスポートレイヤー・ゲームに依存しないサーバーロジック)を管理します。
Quantumは以下の要素で構成される最先端の技術スタックを実装しています。
- 予測/ロールバックを備えたサーバー管理のシミュレーション
- Sparse-set ECSメモリモデルとAPI
- ステートレスな決定論的ライブラリ一式(数学・2D/3D物理・ナビゲーションなど)
- 充実したUnity Editorとの統合ツール
これらはすべて成熟し業界実績がある、既存Photon製品とインフラ(Photon Realtimeのトランスポートレイヤー・サーバーロジックをホストするPhoton Serverプラグインなど)上で構築されています。
ロックステップのない決定論
決定論的システムでは、ゲームクライアントはプレイヤーの入力のみを交換し、シミュレーションは各クライアント上でローカルに実行されます。昔は、これにロックステップのアプローチが使用されており、各ティック/フレームのシミュレーションを更新する前に自分以外プレイヤー全員の入力が揃うまで、ゲームクライアントで待ちが発生していました。
しかしQuantumでは、ゲームクライアントは入力を予測することで自由にローカルのシミュレーションを進めることができます。そして高度なロールバックシステムにより、ゲームの状態を復元し、再シミュレーションで予測ミスを修正します。
ゲームに依存しない権限サーバーコンポーネント(Photon Serverプラグイン)で、入力遅延とクロック同期を管理しているため、クライアントがシミュレーションを進めたりロールバックしたりする際に、最も遅いクライアントに合わせて待つ必要はありません。
Quantum製ゲームの、基本的な構成要素は以下の通りです。- Quantumのサーバープラグイン:入力のタイミングと配信を管理し、クロック同期ソースとして機能します。利用者がホストするバックエンドシステム(マッチメイキング・プレイヤーサービスなど)と統合できるように拡張可能です。
- ゲームクライアントシミュレーター:Quantumサーバーと通信し、ローカルでのシミュレーション実行、すべての入力の予測/ロールバックを実行します。
- 独自のゲームプレイコード:利用者がQuantum ECSを使用して開発する、独立した(Unityと分離した)ピュアC#のシミュレーションです。高性能なコードを作成するためのフレームワークに加えて、QuantumのAPIは、決定論的3Dベクトル数学・2D/3D物理エンジン・ナビメッシュ経路探索など、あらゆるゲームに使用できる広範囲の組み込みコンポーネント(データ)とシステム(ロジック)を提供しています。
伝統的コーディング
すべてのシミュレーションコードは標準で高性能でなければならないという前提の元で、Quantum内部システムはゼロから設計されました。
高性能の鍵は、ポインタを使用したC#コードと、Sparse-set ECSメモリモデルの組み合わせです。(すべての要素が、メモリアライメントされたデータ構造と独自ヒープアロケーターに基づくため、シミュレーションコード実行時にガベージコレクションは発生しません)
これは、ビューのレンダリング(Unity)や、予測/ロールバック固有の予測ミスによって起こる再シミュレーションのために、CPUに十分な余力を残すことが目的です。
(パフォーマンスのために)ポインタベースのC#を使用してはいますが、独自DSLと自動コード生成をうまく使用することで、開発者からは多くの複雑性が隠されます。
コード生成
Quantumでは、すべてのゲームプレイのデータ(ゲーム状態)は、Sparse-set ECSデータ構造(エンティティとコンポーネント)か独自のヒープアロケーター(動的コレクションと独自データ)に置かれ、常にBlittableなメモリアライメントされたC#構造体になります。
開発者は独自DSL(Domain Specific Language)を使用してデータ構造を定義することで、パフォーマンスを重視した制約に縛られずに、ゲーム自体に集中できます。
C#
// components define reusable game state data groups
component Resources
{
Int32 Mana;
FP Health;
}
// structs, c-style unions, enums, flags, etc, can be defined directly from the DSL as well
struct CustomData
{
FP Delay;
Boolean Active;
}
上記のコードスニペットは、対応する型(と明示的メモリアライメント)・シリアライゼーションのコード・(コンポーネントのような)特別な型のロジックを制御するボイラープレートを生成します。
自動生成APIによって、開発者は、エンティティの反復・変更・生成/破棄などの包括的な機能で、ゲーム状態をクエリしたり変更したりできます。
C#
var es = frame.Filter<Transform3D, Resources>();
// Next fills in copies of each of the components + the EntityRef
while (es.NextUnsafe(out var entity, out var transform, out var resources)) {
transform->Position += FPVector3.Forward * frame.DeltaTime;
}
ステートレスシステム
QuantumのDSLは、エンティティ・コンポーネント・補助的なデータ構造(構造体・列挙体・共用体・ビット列・コレクションなど)のような概念で表されるゲームの状態のデータ定義に対応しています。このゲーム状態を更新するためには、独自のゲームロジックを組み込む方法が必要になります。
独自ロジックの記述は、システムで実装します。システムは、Quantumクライアントのシミュレーションループで毎ティック実行される、ステートレスなロジックの一部になります。
C#
public unsafe class LogicSystem : SystemMainThread
{
public override void Update(Frame f)
{
// your game logic here (f is a reference for the generated game state container).
}
}
システムのAPIには、ゲームループの実行順・システム間通信のためのシグナル(独自実装や、組み込みの物理エンジンの衝突判定コールバックなど)・イベント・その他いくつかの拡張用フックがあります。
イベント
シミュレーションはピュアC#で実装され、UnityのAPIを直接参照できません。ゲームプレイコードと描画エンジン間で通信するための二つの重要な機能として、イベントとアセットリンクシステムがあります。
イベントは、シミュレーション中に起こった重要な事を、ゲームコード側から描画エンジン側へ伝える方法です。
この一例は「キャラクターがダメージを受けた時に何らかの結果を伝える」ことです。
前述したゲーム状態を使用して、キャラクターエンティティのResources
コンポーネントのHealth
値が、ダメージを受けて減少することを想像してください。
ここでUnityのスクリプトから確認できるのは最新のHealth
値のみで、何によってダメージを受けたのか・減少前の値はいくつかなどを知る方法はありません。
DSLファイルでのイベントの定義は以下の通りです。
C#
event Damage
{
entity_ref Character;
FP Amount;
}
ゲームプレイコードでは、シンプルなAPI呼び出しでイベントを発生させます。
C#
public void ApplyDamage(Frame f, EntityRef c, FP amount)
{
// logic to apply damage to the character itself
// this sends an event to the "view" (Unity)
f.Events.Damage(amount, c);
}
Quantumのイベント処理機構は、ティック更新完了後に、生成されたすべてのイベントを処理します。ここで、サーバー承認済み入力を必要とするイベント・イベントの繰り返し・シミュレーションでロールバックが発生した際のイベントのキャンセル/承認も管理されます。
シミュレーションコード側から発生したイベントはその後、Unityのスクリプト側で作成されたコールバックによって実行時に消費されます。
C#
public void OnDamage(DamageEvent dmg)
{
// instantiate and show floating damage number above the target character, etc
}
アセットリンク
Unityは、柔軟なエディタと洗練されたアセットパイプラインを備えています。
アセットリンクシステムによって、ゲームデザイナーやレベルデザイナーは、データ駆動シミュレーションのオブジェクトをUnity Editorから編集できます。そしてオブジェクトはその後、シミュレーション側に組み込まれます。
これはプロトタイプ制作や、ゲームバランスの最終調整に必須となる機能です。
開発者はC#のシミュレーション側のプロジェクトから、所望の属性を持つデータ駆動クラスを作成できます。
C#
public partial class CharacterSpec
{
public FP MaxHealth;
public int MaxMana;
}
そして、レベルデザイナーはUnityで、このクラスのアセットインスタンスを必要な数だけ作成できます。各インスタンスには自動的にユニークなGUIDが割り当てられます。
プログラマーは、これらのアセットのデータをシミュレーション中に直接使用できます。
C#
var data = frame.FindAsset<CharacterClass>("character_class_id");
var mana = data.MaxMana;
これらのアセットは、DSL定義のコンポーネントで直接参照することも可能です。
C#
component CharacterAbilities
{
asset_ref<CharacterSpec> CharacterSpec;
}
決定論的ライブラリ
Quantumでは、すべてのクライアント上で、シミュレーションは同じ入力値を与えられたら同じ計算結果になる必要があります。
決定論的でなければならないということは、float
やdouble
の値を使用することも、あらゆるUnity API(ベクトルや物理エンジンなど)を使用することもできないということです。
ゲーム開発者がゲームプレイコードを実装しやすくするため、Quantumには柔軟で拡張可能な決定論的ライブラリが含まれています。クラス/構造体/関数一式に加えて、DSLでエンティティを定義する際に直接使用できるコンポーネントなどもあります。
使用可能なモジュールは以下の通りです。
- 決定論的数学ライブラリ:
float
/double
を置き換えるFP
(固定小数点数)型(Q48.16)・FPVector2
・FPVector3
・FPMatrix
・FPQaternion
・RNGSession
・FPBound2
、安全なキャストやネイティブ型のパーサーを含む数学ユーティリティ。数学ライブラリはパフォーマンスを主目的として実装されていて、可能な限りのインライン化・ルックアップテーブル・高速な演算子が使用されています。 - 2D/3D物理エンジン:高性能でステートレスな2D/3D物理エンジンで、静的/動的オブジェクト・コールバック・ジョイントなどに対応しています。
- ナビメッシュ/経路探索/エージェント:既存のUnityのナビメッシュからのインポートやエディタ上からメッシュの直接操作、業界標準のHRVO衝突回避・Funnelアルゴリズム・さらに多くの機能を含みます。
次のステップ
Quantumを始めるなら、Asteroids Tutorialから始めることを強く推奨します。このチュートリアルは、Quantumを始めるために必要な基礎のすべてが学べます。これはテキストも動画(英語)も用意されています。
Back to top