2.5D物理
はじめに
Quantumの2.5D物理は予測エンジンで、Quantum予測ロールバックモデル完全対応です。このドキュメントでは、API機能、設定、パフォーマンスの特徴など現在使用可能な全ての機能について説明しています。
このページでは、2D、3D、2.5D物理の両方のドキュメントについて説明します。
重要: 2D APIと3D APIは非常に似ています。このドキュメントでは、パターンに沿った説明が行われます。まず最初に2D APIが説明され、次に3D APIが説明されます。
静的シーンコライダー
静的コライダーは簡単にシーンに追加できます。UnityのGameObject(球体、ボックス、ポリゴン)にQuantumの静的コライダースクリプトを1つ追加して、追加したスクリプトのプロパティを編集し、シーン上の静的な障害物の配列になるようにします。
The 2D Physics shapes are: Circle, Box and Polygon. Each of these has a Height
field, which is how you create 2.5D shapes. This matter will be explored on the 2.5D Section.
3D物理学の形状は、球体、ボックス、メッシュです。
特定の物理マテリアルとユーザーアセットを添付することもできます(コリジョンコールバックで渡されます)。これらの2つの機能については、このドキュメント次のセクションで説明します: PhysicsMaterial データアセット 、 コリジョンコールバック

Quantumには予測フォーマットでUnityシーンを表すデータアセットがあり、これはMapData Unityスクリプトを経由してシーンにリンクされています。このアセットに静的コライダー(およびNavMeshやその他のもろもろ)をエクスポートするには、Unityインスペクター上のbake mapボタンを押すだけです。(もしくは、カスタムプロセスもしくはツールから同様の機能を直接呼び出してください。)

プレイできるエリアを決めるには、Map asset settingで直接セルのサイズとグリッドのサイズの希望の値を入力してください。これはブロードフェーズの当たり判定のデータ構造体も兼ねています。

Dynamicbody コンポーネント
Quantum DSLファイルで_DynamicBody_ ECSコンポーネントをエンティティタイプ定義に追加すると、物理エンジンでこのエンティティが認識されるようになります。DynamicBody の使用は、Transform2D がエンティティタイプの一部でなければならないことを意味します。
C#
entity Ship[16] {
use Transform2D;
use DynamicBody;
}
同じルールが3D物理に適用されます:
C#
entity Ship[16] {
use Transform3D;
use DynamicBody3D;
}
Dynamicbody を持つ全てのエンティティのインスタンスは、作成後に dynamic もしくは kinematic として初期化される必要があります。どちらの場合でも Quantum.Core.DynamicShape (円形、ボックス、ポリゴン構造体)が適用されますが、Dynamicbodyとして初期化される場合は、下記のように質量の指定も必要です。
// Frameオブジェクトから船のエンティティをインスタンス化
var ship = f.CreateShip();
// 形状と質量でDynamicBodyを初期化
ship->DynamicBody.InitDynamic(Core.DynamicShape.CreateCircle(FP_1), FP_1);
// ship->DynamicBody.InitKinematic(Core.DynamicShape.CreateCircle(FP_1));
3D DynamicBodyは、BoxとSphereのみに対応します。
var ship = f.CreateShip();
ship->DynamicBody3D.InitDynamic(Core.DynamicShape3D.CreateSphere(FP_1), FP_1);
DynamicShapeConfigs
DynamicShapeConfig タイプに基づいた物体の初期化に効率的なデータ駆動型のオプションもあります。この構造体は、すべてのQuantumデータアセットにプロパティとして追加でき、Unityから(形状、サイズなどについて)編集可能です。また、形状を直接使用する代わりに、Dynamicbodyの初期化に使用できます。
C#
// 形状設定プロパティを含むデータアセット
partial class ShipSpec {
// これはUnityから編集されます
public DynamicShapeConfig Shape2D;
public DynamicShape3DConfig Shape3D;
public FP Mass;
}
直接Shapeを使用する代わりにシェイプコンフィグを使用して物体を初期化していきます。
// Frameオブジェクトから船のエンティティをインスタンス化
var ship = f.CreateShip();
var shipSpec = DB.FindAsset<ShipSpec>("millenium_falcon");
ship->DynamicBody.InitDynamic(shipSpec.Shape2D, shipSpec.Mass);
// または3Dの同等:
ship->DynamicBody3D.InitDynamic(shipSpec.Shape3D, shipSpec.Mass);
DynamicBody API
エンティティインスタンスへのポインタを使って、動力や勢いを追加したり速度を直接変更することもできます。
The 2D Physics API
C#
// 上記の例の船のインスタンスを考慮して、動力を追加します(オプションでrelativeToポイントを渡します)
ship->DynamicBody.AddForce(FPVector2.Up * 10);
// インパルスの相対点を使用(相対点に適用されるため、トルクが追加されます):
ship->DynamicBody.AddForce(FPVector2.Up * 10, FPVector2.Right);
// relativeToポイントでも可能な、インパルスの適用(デルタ時間の影響を受けない):
ship->DynamicBody.AddLinearImpulse(FPVector2.Up * 10);
// relativeToポイントに対応していません
ship->DynamicBody.AddAngularImpulse(FPVector2.Up * 10);
// relativeTo ポイントに対応していません
ship->DynamicBody.AddTorque(FP._10);
3D物理 API
C#
ship->DynamicBody3D.AddForce(FPVector3.Up * 10);
ship->DynamicBody3D.AddForce(FPVector3.Up * 10, FPVector3.Right);
ship->DynamicBody3D.AddLinearImpulse(FPVector3.Up * 10);
ship->DynamicBody3D.AddAngularImpulse(FPVector3.Up * 10);
ship->DynamicBody3D.AddTorque(FPVector3.Up * 10);
以下に Dynamicbody APIから直接使用できるものを挙げています。
- 物体の有効化/無効化;
- トリガーとして設定;
- 物理レイヤーの変更;
- WakeUp body (restingの最適化が使用されている場合);
- Physicsmaterial データアセットインスタンスの切り替え(次のセクション);
kinematicキャラクターコントローラー(KCC)
SDKバージョン1.2.4 B1以降、KCCが追加されました。これは、物理オブジェクト(静的および動的)に基づいてキャラクターを移動するための非常に優れたオプションであり、プロトタイピングと製品グレードのコードの両方に非常に適しています。KCCドキュメントで詳細を読む。
Physicsmaterial データアセット
すべての Dynamicbody は、 Physicsmaterial (Quantumデータアセットの1つ)を参照する必要があります。そのPhysicsmaterialには、物理エンジンがコリジョンの解消および動力と速度の統合に使用するプロパティ(restitution、friction、dragなど)が含まれています。
特定の Physicsmaterial アセットが何も使用されていない場合は、デフォルトのPhysicsmaterialが割り当てられます。(SimulationConfig データアセット内のPhysics設定からリンクされます。)

PhysicsMaterial アセットは直接 DynamicBody に割り当てられます。以下を参照してください。
C#
// 上記の例から船エンティティインスタンスを検討
var material = DB.FindAsset<PhysicsMaterial>("steel");
ship->DynamicBody.PhysicsMaterial = material;
// 3Dの同等:
ship->DynamicBody3D.PhysicsMaterial = material;
*Physicsmaterial* には重要な特徴があります。それは、実行時にプロパティの値をシミュレーションから(もしくはUnityから)非予測性へ変更するという点です。値が変更されるのは、これらのプロパティがロールバック可能なゲームステートの一部ではなく、どちらかというとQuantumのアセットデータベースの一部だからです。
これは他のデータアセットと同じルール(このマニュアルのデータアセットの章で説明しています)に従っており、以下のルールが適用されています。
```csharp
// ロールバックされないため、これは安全ではありません。
ship->DynamicBody.PhysicsMaterial.Drag = FP._0;
// 参照の切り替えは問題なく安全です
var newMaterial = DB.FindAsset<PhysicsMaterial>("ice");
ship->DynamicBody.PhysicsMaterial = material;
このルールが適用されない例外もあります。それは、ロックステップモード時(ロールバックに対応していないため)と検証されたフレーム上(ExposeVerifiedStatus 設定オプションと組み合わせた場合)でのみデータベースの変更を行う時です。
ただし、上記の条件下でもランタイム中のデータベースプロパティの変更は非常に良くないことですので、できる限りやらないようにしてください。ほとんどの場合、マテリアルの交換がいい代替案となります。
コリジョンコールバック
Quantumでのコリジョン(およびトリガー)コールバックは シグナル 機能を通して処理されます。基本の ISignalCollisionDynamic and ISignalCollisionStatic インターフェースです。
C#
namespace Quantum {
public interface ISignalOnCollisionDynamic {
Int32 RuntimeIndex { get; }
void OnCollisionDynamic(Frame f, DynamicCollisionInfo info);
}
public interface ISignalOnCollisionStatic {
Int32 RuntimeIndex { get; }
void OnCollisionStatic(Frame f, StaticCollisionInfo info);
}
public interface ISignalOnTriggerDynamic {
Int32 RuntimeIndex { get; }
void OnTriggerDynamic(Frame f, DynamicCollisionInfo info);
}
public interface ISignalOnTriggerStatic {
Int32 RuntimeIndex { get; }
void OnTriggerStatic(Frame f, StaticCollisionInfo info);
}
システムにこれらを実装すると、フレーム内でコリジョンまたはトリガーを見つけるたびに対応するコールバックが実行されます。
C#
public class MySystem : SystemBase, ISignalOnCollisionDynamic {
void OnCollisionDynamic(Frame f, DynamicCollisionInfo info) {
// ここでコリジョンに反応する
// info.EntityAとinfo.EntityBは、互いに衝突する2つのエンティティです
}
}
情報フィールドには、使用されている物理のタイプに応じて、2Dおよび3Dの接触点と法線の両方の情報も含まれます。
Dynamicbody コンポーネント を含む2つの(DSL内で定義されているタイプの)エンティティ間で動的コリジョン(もしくはトリガー)が発生します。コールバックは、全てのクライアントマシンに対してエンティティを同じ順番(情報パラメーター上のエンティティAおよびB)で受け渡し、予測性を保つことが保証されています。
静的コリジョンコールバックはデフォルトでは発生しないことに留意ください。静的コールバックを有効にする場合は、SimulationConfig データアセット内のPhiysicsで対応する設定オプションにチェックを入れます(設定に関する詳細は最終セクションを参照してください。)
タイプセーフコリジョンコールバック
一般的なエンティティ-エンティティおよびエンティティ-静的コリジョンコールバックに加えて、Quantumは実現したいコリジョンコンビネーションについてのタイプセーフ(エンティティタイプ用)コールバックも提供しています。以下のサンプルでは、コード生成されるタイプセーフコリジョンコールバックを3つ定義しています。(2つのShipエンティティ間、ShipとProjectile間、ShipとStatic間)
C#
signal collision (Ship* shipA, Ship* shipB);
signal collision (Ship* ship, Projectile* projectile);
signal collision static (Ship* ship);
重要: パラメータ(エンティティタイプ)の順番は、宣言した順番に関わらずQuantumではアルファベット順に再度ソートされます。つまり、上の例のShip-Projectileはこのシグネチャで生成されます。(他のシグナルと同じようにフレームオブジェクトが追加されることに留意してください。)以下を参照してください。
C#
void ISignalOnCollisionDynamicProjectileShip(Frame f, Projectile* projectile, Ship* ship, DynamicCollisionInfo info) {
}
タイプセーフトリガーコールバックはコリジョンシグナルインターフェースの一部ですので、別々に記述する必要はありません。
コリジョン情報データタイプ
コリジョンコールバックについてのもう1つの重要な側面は、処理の際に便利なデータがあることです。Quantumでは、処理中のコリジョンのタイプに応じて2種類のデータがあり、それぞれ DynamicCollisionInfo と StaticCollisionInfo となっています。
2つのエンティティ間での動的コリジョンでは、常にポインタ(前のセクションで説明したように、任意でタイプセーフ)と Normal/Point/Penetration データ(これらはトリガーに対しては計算されません)を受け取ります。
重要: コールバックの物理エンジンが完全にコリジョンを認識しないようにすることも可能です。info.IgnoreCollision = true; を設定するだけです。
静的コリジョンの場合は2つ目のエンティティはありませんが(1つのエンティティがシーンの静的コライダーに衝突するパターンなので)、Unityでシーンの静的コライダースクリプトに直接Quanrumデータアセットをドラッグし、コリジョンにカスタムデータを受け渡すことは可能です。
Raycast、Linecast、Shapeのオーバーラップ
Raycastは動的コライダー、kinematicコライダー、および静的コライダーを制御します。Ray、Line、ShapeのオーバーラップのAPIは、パラメータに違いがあるものの、ほぼ同じです。結果は常に DynamicHits インスタンスで、これはプール済/再利用可能なオブジェクトであるため using キーワードで制限をかけることが必須です。以下を参照してください。
C#
using (var hits = f.Scene.Linecast(FPVector2.Zero, FPVector2.Up * 10)) {
for (int i = 0; i < hits.Count; i++) {
var hit = hits[i];
}
}
// 3Dの同等:
using (var hits = f.Scene3D.Linecast(FPVector3.Zero, FPVector3.One)){
for (int i = 0; i < hits.Count; i++){
var hit = hits[i];
}
}
結果として生じた DynamicHits オブジェクトには、以下のプロパティが含まれます。
- 個々の DynamicHit オブジェクトアイテムは、エンティティポインタ(非タイプセーフ)もしくは静的コライダー情報を含みます(これら2つはお互いに排他的で、片方が有効であればもう片方は null になります)。;
- 常に Count が使用され、 DynamicHit アイテムは繰り返されます。(hitオブジェクトはプールされ、フレーム間で再利用されるので、配列のサイズが現在のものと異なる可能性があります);
- ヒットは距離ではソートされません。(任意で Sort() 機能に FPVector2 リファレンスを受け渡すことはできます)
Raycastは、Linecastの糖衣構文ですが、異なる点は、Raycastでは start と end のかわりに start、 direction および max-distance がリクエストされる点です。 また、 LayerMask などの検索を制限する任意のパラメータや、normalの計算をスキップしたりstaticをスキップするオプションもあります。
3D物理学でも同じように機能します。
形状のオーバーラップ
形状のオーバーラップもまた DynamicHits を返してインスタンスします。最低限必要なパラメータは以下の通りです: 中心位置(FPVector2)、回転(FP)、形状(DynamicBody からの DynamicShape、もしくは呼び出し時に作成された DynamicShapeの両方)。
C#
using (var hits = f.Scene.OverlapShape(FPVector2.Zero, FP._0, DynamicShape.CreateCircle(FP._1)))
{
for (int i = 0; i < hits.Count; i++)
{
var hit = hits[i];
}
}
// 3Dの同等:
using (var hits = f.Scene3D.OverlapShape(FPVector3.Zero, FPQuaternion.Identity, DynamicShape3D.CreateSphere(1))){
for (int i = 0; i < hits.Count; i++){
var hit = hits[i];
}
}
*Transform2Ds*、*Transform2DVertical* (次のセクションで説明します)、*DynamicShapeConfigs* をとるオーバーロードや、*LayerMask* や *OverlapOptions* といった任意のパラメーターもあります。以下を参照してください。
```csharp
public DynamicHits OverlapShape(Transform2D transform, DynamicShapeConfig shapeConfig, Int32 layermask = -1, OverlapOptions options = default(OverlapOptions));
public DynamicHits OverlapShape(Transform2D transform, DynamicShape shape, Int32 layermask = -1, OverlapOptions options = default(OverlapOptions));
public DynamicHits OverlapShape(Transform2D transform, Transform2DVertical verticalTransform, DynamicShapeConfig shapeConfig, Int32 layermask = -1, OverlapOptions options = default(OverlapOptions));
public DynamicHits OverlapShape(Transform2D transform, Transform2DVertical verticalTransform, DynamicShape shape, Int32 layermask = -1, OverlapOptions options = default(OverlapOptions));
public DynamicHits OverlapShape(FPVector2 center, FP rotation, DynamicShapeConfig shapeConfig, Int32 layermask = -1, OverlapOptions options = default(OverlapOptions));
public DynamicHits OverlapShape(FPVector2 center, FP rotation, DynamicShape shape, Int32 layermask = -1, OverlapOptions options = default(OverlapOptions));
public DynamicHits OverlapShape(FPVector2 center, FP rotation, FP verticalOffset, FP height, DynamicShape shape, Int32 layermask = -1, OverlapOptions options = default(OverlapOptions));
3D Overlap APIには2つのオプションがあります:
C#
public DynamicHits3D OverlapShape(Transform3D transform, DynamicShape3D shape, int layermask = -1, OverlapOptions options = 0);
public DynamicHits3D OverlapShape(FPVector3 center, FPQuaternion rotation, DynamicShape3D shape, int layermask = -1, OverlapOptions options = 0);
重要: Scene.OverlapCircle() は、一般的な Scene.OverlapShape に比べて 格段に速い メソッドです。違いはOverlapCircleではnormalとコンタクトポイントを計算しないことです。パフォーマンスの向上を重視する場合場合には、このオプションを使用してください。
垂直データをもつ2.5D物理
どのタイプの物理を使用するかを選択するとき、オブジェクトにHeightを設定したい場合があります。しかし、3つの軸のすべてを完全に制御するのが好ましくない場合があります。この場合、2.5D Physicsが最良の選択かもしれません。
Quantumの2.5D物理の3次元では、静的コライダーと DynamicBodies に「厚み」と「位置」を持たせられます。静的コライダーには、「高さ」を設定してUnityトランスフォームハンドルに直接配置します。以下を参照してください。

エンティティには、定義に(リクエストされる Transform2D と DynamicBody に加えて)Transform2DVertical コンポーネントを追加し、そこに Height プロパティと Position プロパティを設定します。例えばQuantumのxz-orientedゲームでは、これによって高さと位置がy軸に追加されます。
C#
entity Ship[16] {
use Transform2D;
use Transform2DVertical;
use DynamicBody;
}
エンティティや静的コライダーが3次元のとき、コリジョンはそれを考慮するので「地上ベース」のゲームに「飛んでいる」エンティティを置くことも可能です。これはコリジョン検出段階もしくはQuantumの物理エンジンで自動的に処理されます。
重要: コリジョンが検出されると、コリジョンソルバーは追加のディメンション情報を使用しません。なので、エンティティのバウンスと分離は物理エンジンの基本の2D次元で実行されます。
追加の軸の Transform2DVertical.Position に、手動で直接速度と動力を統合して3次元の重力をシミュレートすることもできます。物理エンジンがこの情報を使用するのは、コリジョン検出のためだけです。
Raycast、Linecast、オーバーラップへの影響:これらの機能は垂直データを考慮するので、オーバーロードバージョンを使用して height と垂直の offset に受け渡していない限り、デフォルトでは2D次元の平面上でフラットな状態です。以下の垂直データを受け入れる形状のオーバーラップオーバーロードを参照してください。
C#
public DynamicHits OverlapShape(FPVector2 center, FP rotation, FP verticalOffset, FP height, DynamicShape shape, Int32 layermask = -1, OverlapOptions options = default(OverlapOptions));
設定と最適化のヒント
SimulationConfig データアセットには、物理エンジンの設定が豊富にあります。
以下を参照してください。

レイヤー と 一致する レイヤーコリジョンマトリックス は、対応するUnityのレイヤーからインポートするか、設定で直接編集できます。コリジョンの有効化を、チェックが必要なレイヤー間のみに設定することが物理エンジンの最適化の1つでしょう。
もう1つの重要な設定は 角速度 (回転を制御する物理)の有効化または無効化です。これを無効化すると、ゲームの物理シミュレーションの速度が上がるだけでなく、より安定しておこなえるようになります。
可能な時には kinematic エンティティを使用するととても便利です。片方がkinematicトリガーでない限り、kinematicがコリジョンのためにチェックされることはないからです。
ある種のゲームでは raycasts を広範囲に及んで使用すると、ボトルネックになる可能性があります。大事なことは、rayに対して妥当な 距離 をとることです。長いものはチェックするコライダーも多くなるからです。小さいものを使用することでraycastはより速くなります。
設定で resting bodies を有効にすると動いていないエンティティが他の動体によって起こされたり、直接コードで起こされない限り、コリジョンでチェックされないようになります。エンティティの数が多い場合は、コリジョン検出システムの負荷を軽減するのにこの方法はとても便利です。
フレームごとにチェックして解消するコリジョンが数多くあるゲームでは マルチスレッディング および コリジョン アイランドを使用するのが便利です。
最後に、パフォーマンスの微調整に入る前にプロファイラの実行(システムまわりも)をお勧めします。実際にボトルネックがどこに存在するのかを見つけることが大事です(物理とは関係ないかもしれませんが、カスタムコードとつながっています。)
プロファイラはまた、ゲーム仕様の負荷のもと、どの設定で一番いい働きをするのかを確かめるのにも便利です。Quantumの バックグラウンドスレッド を使用しているときはUnityのプロファイラは使用できない(Unityプロファイラの制限による)こと、数字はシステム間の比較用のみで、プラットフォームにおけるリリースビルドを表しているわけではないことに留意してください。
Back to top