Quantum イントロ
概要
Photon Quantumは、Unityで作られたオンラインマルチプレイヤーゲームのための高性能決定論的ECS(エンティティコンポーネントシステム)フレームワークです。
アクションRPG、スポーツゲーム、戦闘ゲームなどのレイテンシに敏感なオンラインゲーム用に設計されています。
Quantumは既存のPhoton製品とインフラストラクチャの上に構築された最先端の予測/ロールバックシミュレーションAPIを実装しています。
次の図はこれを示しています。
Quantum は、以下の部分で構成される最先端の技術スタックを実装しています。
- サーバー管理型の予測/ロールバックシミュレーションコア。
- DSLベースのコード生成により、ゲームステートデータをメモリアラインメントされたC#のパーシャル構造体として生成 (高性能APIによりデータへの直接ポインタアクセスが可能)
- ステートレスな決定論的ライブラリの完全なセット(数学、2Dおよび3D物理学、ナビゲーションなど
- リッチUnityエディタ統合とツール
すべては、業界で実績のある既存のPhoton製品とインフラストラクチャ(Photonリアルタイムトランスポート層、ホストサーバロジックへのPhotonサーバプラグインなど)上に構築されています。
また、Quantumは、シミュレーションロジック(Quantum ECS)をビュー/プレゼンテーション(Unity)から完全にデカップリングして、クリーンなコードを書くのに役立ちますが、ネットワーク実装の仕様(内部予測/ロールバック+トランスポート層+ゲーム不可知のサーバーロジック)も考慮しています。
ロックステップなしの決定論
決定論的なシステムでは、ゲームクライアントは、すべてのクライアント上でローカルに実行されているシミュレーションでプレーヤの入力を交換するだけです。過去には、ゲームクライアントがシミュレーションの各ティック/フレームを更新する前に、他のすべてのプレイヤーからの入力を待つロックステップアプローチを使用していました。
Quantumでは、ゲームクライアントは入力予測を使用してシミュレーションをローカルに自由に進めることができ、高度なロールバックシステムがゲームの状態を復元し、誤予測を再シミュレーションします。
Quantumは入力レイテンシとクロック同期を管理するためにゲームに依存しない信頼できるサーバコンポーネント(Photonサーバプラグイン)に依存するため、クライアントは最遅のものをロールバックして前方を確認する必要はありません。
これらがQuantumゲームの基本のビルディングブロックです。
- Quantum Server Plugin:ゲームクライアント間の入力タイミングと配信を管理し、クロック同期ソースとして機能します。
顧客がホスティングするバックエンドシステム(マッチメイキング、プレイヤーサービスなど)と統合するために拡張することができます。 - Game Client Simulator: Quantumサーバと通信し、ローカルシミュレーションを実行し、すべての入力予測とロールバックを実行する。
- Custom Gameplay Code: Quantum ECSを使用して、孤立した純粋なC#シミュレーション(Unityから切り離された)として開発されました。
QuantumのAPIは、高性能なコードの編成の枠組みを提供するだけでなく、決定論的な3Dベクトル計算、2D物理エンジン、navmeshパスファインダー、アニメーターなどのあらゆるゲームで再利用できる、あらかじめ構築されたコンポーネント(データ)とシステム(ロジック)を提供します。
従来のコーディング
すべてのシミュレーションコードが高性能でなければならないという前提に基づいて、Quantum内部システムは最初からそれを念頭に置いて設計されています。
Quantumの高性能の鍵は、ECSデータモデルと組み合わせたダイレクトポインタの使用にあります(すべてメモリアラインメントされたデータ構造に基づいており、ランタイム時にシミュレーションコードから直接割り当て/ガベージコレクションを行わないようになっています)。
目標は、CPU予算の大部分をビュー/レンダリングコード(Unity)のために残すことです。これには、予測/ロールバックアプローチに固有の入力の誤予測によって引き起こされる再シミュレーションも含まれます。
ポインタベースのC#の使用は(パフォーマンスのために)露呈していますが、カスタムDSLの巧妙な使用とコードの自動生成によって、開発者の作業が簡単になります。
コード生成
Quantumでは、すべてのゲームプレイデータ(ゲーム状態)は、メモリ配列のC#構造体として単一のブロックに構成されています。
開発者は、すべてのデータ構造を定義するために、パフォーマンス指向の制約の代わりにゲームの概念に専念できるカスタムDSL(ドメイン固有の言語)を使用します。
C#
// components define reusable game state data groups
component Resources
{
Int32 mana;
FP health;
}
// entities are component containers (custom and pre-built ones)
entity character[32]
{
use Resources;
use Transform2D;
}
上記のコードスニペットは、最大32文字のゲーム状態データ構造を生成し、それぞれに変換とリソースの両方のコンポーネントが含まれています。
自動生成APIを使用すると、エンティティの読み込み、変更、作成、破棄を行う包括的な機能を使用して、ゲームの状態を照会および変更することができます。
C#
// Auto-generated function to create a new character, returning a pointer (Frame f is the game state container passed at every init or update function)
var c = f.CreateCharacter();
var position = c->Transform2D.Position;
// if you want to update all characters...
var all = f.GetAllCharacters();
while (all.Next())
{
Character* c = all.Current;
}
// destroying an entity is also straightforward...
f.DestroyCaracter(c);
ステートレスシステム
QuantumのDSLは、エンティティ、コンポーネント、補助構造体(構造体、列挙体、連合体、ビットセット、コレクションなど)などの概念によるゲーム状態データ定義を対象としていますが、ゲーム状態を更新するカスタムゲームロジックを整理する方法が必要です。
Quantumのクライアントシミュレーションループによって毎回更新されるステートレスなロジックであるシステムを実装することで、カスタムロジックを作成します。
C#
public unsafe class LogicSystem : SystemBase
{
public override void Update(Frame f)
{
// your game logic here (f is a reference for the generated game state container).
}
}
Systems APIゲームループの呼び出し順序、システム間通信(物理エンジンコリジョンコールバックなどのカスタムおよびプリビルドの両方)、イベントおよびその他の拡張フック。
イベント
シミュレーションは、UnityのAPIを直接参照することなく純粋なC#で実装されていますが、ゲームプレイコードとレンダリングエンジンとの通信を可能にする重要な2つの機能としてイベントとアセットリンキングシステムが使用されます。
イベントは、ゲームコードがシミュレーション中に重要なことが発生したことをレンダリングエンジンに知らせるための方法です。
例えば、キャラクターにダメージを与える出来事です。
前のセクションの状態を基にして、ダメージがキャラクタエンティティのリソースコンポーネントからHP値を減少させるのを想像してください。
Unityのレンダリングスクリプトで唯一明確になっているデータは、新しいHP値そのものです。ダメージの原因や以前のHP値などを知ることはできません。
ファイルDSL内のイベント定義:
C#
event Damage
{
entity_ref<Character> Character;
FP Amount;
}
ゲームプレイコードは、単純なAPIコール(生成された)としてイベントを発生させます。
C#
public void ApplyDamage(Frame f, character* c, FP amount)
{
f.Events.Damage(amount, c->EntityRef);
}
Quantumのイベントプロセッサは、チックの更新が完了した後に生成されたすべてのイベントを処理し、サーバが確認した入力、イベントの繰り返しなどを必要とするイベントを処理します。
シミュレーションコードから生成されたイベントは、Unityスクリプトで作成されたコールバックから実行時に消費することができます。
C#
public void OnDamage(DamageEvent dmg)
{
var target = QuantumGame.Frame.GetCharacter(dmg.Character);
// instantiate and show floating damage number above the target character, etc
}
アセットリンキング
Unityは柔軟なエディタとスムーズなアセットパイプラインを用意されています。
ゲームデザイナーやレベルデザイナーはアセットリンキングシステムによりUnity Editorからデータ駆動型シミュレーションオブジェクトを作成して編集し、シミュレーションに入力することができます。
これは、最終的なタッチをゲームプレイに追加するためのプロトタイプに不可欠です。
開発者は、C#シミュレーションプロジェクトから、望ましい属性を公開するデータ駆動型クラスを作成します。
C#
public partial class CharacterClass
{
public Int32 MaxMana;
public FP MaxHealth;
}
Unityのレベルデザイナーは、必要に応じてこのアセットのインスタンスをいくつでも作成することができます。それぞれのインスタンスには、一意のGUIDが自動的に割り当てられます。
プログラマは、これらのアセットのデータをシミュレーション内から直接使用することができます。
C#
var data = DB.FindAsset<CharacterClass>("character_class_id");
var mana = data.MaxMana;
状態定義DSLからエンティティとコンポーネントにこれらのアセットを直接割り当てることもできます。
C#
entity Character[32]
{
use Resources;
use Transform2D;
fields
{
asset_ref<CharacterClass> CharacterData;
}
}
決定論的ライブラリ
Quantumでは、同じ入力値を使用する場合、シミュレーションはすべてのクライアントで同じ結果を計算する必要があります。
つまり決定論的であり、フロート変数や倍精度変数、ベクトル、物理エンジンなどのUnity APIのいずれも使用していないことを意味します。
ゲーム開発者がこのスタイルのゲームプレイを実現するために、Quantumには、クラス/構造体/関数のセットと、DSLでエンティティを定義するときに直接使用できるいくつかのコンポーネントの両方を含む、柔軟で拡張可能な決定論的ライブラリがバンドルされています。
以下のモジュールが利用可能です:
- 決定論的な数学ライブラリ:float/doubleの代わりとなるFP (Fixed Point) 型 (Q48.16)、FPVector2、FPVector3、FPMatrix、FPQuaternion、FPRandom、FPBounds2、および安全なキャストを含む余分な数学ユーティリティ、ネイティブタイプのパーサー。
数学ライブラリはパフォーマンスを第一の目標として実装されているため、可能な限りインライン展開、ルックアップテーブル、高速演算子を使用します。 - 2D 物理エンジン:静的および動的オブジェクト(円、ボックス、ポリゴンのコライダ)に対応する高性能剛体2D物理エンジン。動的ボディは、エンティティの内部で直接使用できる事前構築されたコンポーネントであり、コリジョンとトリガの両方(物理エンジンシステムから生成される信号として)のコールバックがあります。
- NavMesh + A* PathFinder:既存のUnity navmeshからのエクスポータまたはメッシュを直接操作するエディタの両方を含みます。経路探索の他に、視線検査を行うことができます。
- 決定論的アニメーションコントローラ:データをメカニックアセットから決定論的フォーマットにエクスポートし、決定論的シミュレーションからアニメーションステートマシンを実行します。また、mecanimアニメータを制御して同期させます。ルートモーションなどの対応を含みます。
完全なマニュアル(リンク)とサンプルゲームの詳細な説明(リンク)で詳細を確認することをおすすめします。
Back to top