予測カリング
はじめに
予測カリングは、ゲーム世界の一部しかプレイヤーが視認できないゲームで使用されます。これは安全かつ簡単に使用できます。
予測カリングによって、Quantumの予測/ロールバックフェーズのCPU消費を節約できます。これを有効にすると、ローカルプレイヤーにとって重要かつ視認できるエンティティのみで予測が実行されます。視界外のエンティティは確定フレームで1ティックに一度だけシミュレーションが行われるため、可能な限りロールバックを避けることができます。
パフォーマンス向上効果はゲームによって異なります。ゲームが対応するプレイヤー数が多いほど特に重要で、少なくとも1人以上の予測が最終的に不要になるなら、その効果は大きくなる可能性があります。
例えば、30Hzのシミュレーションレートで動作するゲームを考えてみましょう。入力が確定するたびに平均10ティックのロールバックが必要になる場合、ゲームのシミュレーションは(ロールバック含め)300Hz前後で動作できるほど軽量でなければなりません。予測カリングを使用することで、すべてのフレームは常に期待される30/60Hzでシミュレーションされ、予測バッファ内で動作している予測エリアにはカリングが適用されます。
予測カリングの設定
予測カリングを使用するということは、予測シミュレーションが(一部がカリングされるため)フレームの最終結果には決してならず、ゲームステート全体のシミュレーションを進めることもないということです。
予測カリングを設定するには2つのステップが必要で、1つはQuantum、もう1つはUnityで行います。
Quantumでの設定
デフォルトでは、予測カリングシステムはAssets/Photon/Quantum/Samples/SampleScenes/Resources
にあるサンプルのSystemsConfig
アセットで既に有効になっています。

Unityでの設定
Unityでは、予測エリアを設定する必要があります。予測エリアは、どのエンティティを予測からカリングするかを決定するために使用されます。
Unityの更新ごとにSetPredictionArea()
を呼び出して、予測エリアを更新します。
C#
// centerはFPVector2かFPVector3
// radiusはFP
QuantumRunner.Default.Game.SetPredictionArea(center, radius);
想定動作
物理エンジンとナビメッシュエージェント
物理エンジンとナビメッシュ関連のシステムは、予測カリングの影響を受けます。
予測カリングを有効にすると、非確定(つまり予測)フレームの可視エリア内のエンティティのみを考慮し更新します。
物理とナビメッシュエージェントに関連するコンポーネント(PhysicsCollider
・PhysicsBody
・NavMeshPathFinder
・NavMeshSteeringAgent
・NavMeshAvoidanceAgent
)は、ローカル端末の予測エリアの中心点と半径で定義された関心領域から外れたエンティティの更新をスキップして、CPUサイクルを節約します。
イテレーター
ゲームコードも予測カリングの恩恵を受けます。Transform2D
/Transform3D
を含むフィルターは、位置に基づくカリングの対象になります。
予測フレーム実行中、以下のメソッド呼び出しでは、予測半径内のエンティティのみが返されます。一方、検証済み入力受信後の確定フレームでは、すべてのアクティブなインスタンスが返されます。
f.Filter()
f.Unsafe.FilterStruct()
注意: フィルターとは異なり、コンポーネントイテレーターは予測カリングの恩恵を受けません。
f.GetComponentIterator()
f.Unsafe.GetComponentBlockIterator()
手動カリング制御フラグ
フレームAPIから、予測フレームのエンティティをカリングするためのフラグを手動で設定することができます。
メソッド | 説明 | |
---|---|---|
SetCullable(EntityRef entityRef, bool cullable) | エンティティがカリング可能かどうかを設定します。エンティティが存在しない(無効なEntityRefを含む)場合は何も起こりません。 | |
IsCulled(EntityRef entityRef) | フレームの状態(予測か確定か)に関わらず、現在のエンティティがシミュレーションからカリングされているかどうかを返します。
エンティティがカリングされている場合(例:予測エリア内に存在しない場合)や、存在しない場合はTrueになります。 それ以外の場合(エンティティが存在し、カリングされていない場合)はFalseです。 |
|
Culled(EntityRef entityRef) | エンティティが予測カリングされているかどうかを返します。
予測フレーム中かつエンティティがカリングされている場合はTrueになります。 それ以外の場合(確定フレーム中であるか、またはエンティティがカリングされていない場合)はFalseです。 |
|
Cull(EntityRef entityRef) | カリング可能な既存エンティティを、現在のティックでカリングするように手動でマークします。 | エンティティが存在しないか、カリング可能でない場合は何も起こりません。 |
ClearCulledState() | そのフレームのすべてのエンティティのカリング状態をリセットします。 | 毎フレームのシミュレーション開始時、自動的に呼び出されます。 |
一貫した状態を保って同期ズレを避けるには、フラグを設定したシステムの確定フレームで、カリングされたエンティティのフラグを解除するようにしてください。
RNG問題の回避
予測カリングとあわせてRNGSession
インスタンスを使用することは、完全に安全で、決定論性が保証されます。ただし、2つのエンティティがRNGSession
を共有する場合(Quantumのglobal
にあるデフォルトのものなど)、視覚的なちらつきが発生することがあります。これは、確定フレーム実行後の予測されたエンティティで新しい乱数値が生成されることで、エンティティの最終的な位置が変更/修正されてしまうためです。
解決策は、カリング対象の各エンティティに、それぞれRNGSession
構造体を保持させることです。RNGSession
を独立させることで、ロールバックが実際に必要にならない限り、カリングが予測されたエンティティの最終位置に影響しないことが保証されます。
chsarp
struct SomeStruct {
RNGSession MyRNG;
}
各RNGSession
には、好きな方法でシードを注入することができます。