コライダーと剛体
はじめに
コリジョンと物理挙動について、Quantumは独自のコンポーネントを持っています。
- エンティティに
PhysicsCollider2D/PhysicsCollider3Dを追加すると、そのエンティティは動的な障害物やトリガーとなり、transformから動かせるようになります。 PhysicsBody2D/PhysicsBody3Dを追加すると、そのエンティティは物理ソルバーによって制御されるようになります。
要件
Transform2D/Transform3D・PhysicsCollider2D/PhysicsCollider3D・PhysicsBody2D/PhysicsBody3Dは密接に関連しているため、あるコンポーネントは他のコンポーネントが機能するための要件になっています。完全な依存関係のリストを以下に示します。
| コンポーネント / 要件 | Transform | PhysicsCollider | PhysicsBody |
|---|---|---|---|
| Transform | ✓ | ✗ | ✗ |
| PhysicsCollider | ✓ | ✓ | ✗ |
| PhysicsBody | ✓ | ✓ | ✓ |
これらの依存関係は相互に関連しています。PhysicsBodyを有効にするには、次の順序でエンティティにコンポーネントを追加する必要があります。
- Transform
- PhysicsCollider
- PhysicsBody
コリジョンのコールバックの詳細はこちらをご覧ください。
PhysicsBodyコンポーネント
エンティティにPhysicsBodyコンポーネントを追加すると、そのエンティティは物理エンジンの考慮対象になります。注意: PhysicsBodyを使用するには、エンティティにTransformとPhysicsColliderが既に追加されている必要があります。
このコンポーネントの作成や初期化は、コードから手動で行うことも、UnityのQuantumEntityPrototypeコンポーネントから行うことも可能です。
C#
var entity = f.Create();
var transform = new Transform2D();
var collider = PhysicsCollider2D.Create(f, Shape2D.CreateCircle(1));
var body = PhysicsBody2D.CreateDynamic(1);
f.Set(entity, transform);
f.Set(entity, collider);
f.Set(entity, body);
3D物理でも同様に適用できます。
C#
var entity = f.Create();
var transform = Transform3D.Create();
var shape = Shape3D.CreateSphere(FP._1);
var collider = PhysicsCollider3D.Create(shape);
var body = PhysicsBody3D.CreateDynamic(FP._1);
f.Set(entity, transform);
f.Set(entity, collider);
f.Set(entity, body);
QuantumEntityPrototypeを使用する場合、コンポーネントはUnityのインスペクター上で定義された値で初期化されます。
対応するシェイプ
PhysicsCollider3Dは、動的なエンティティに対して、以下の3Dシェイプのみをサポートしています。
- Sphere
- Box
- Capsule
- Compound(複数のシェイプの組み合わせ)
エディター上では、Unityのカプセルと同じプロパティ(RadiusとHeight)を使用して、カプセルシェイプのプロパティを設定できます。ただしQuantum内部では、カプセルはRadiusとExtentで定義され、HeightとDiameterプロパティを持っています。
PhysicsCollider2Dは、動的なエンティティに対して、以下の2Dシェイプのみをサポートしています。
- Circle
- Box
- Polygon
- Edge
- Capsule
- Compound(複数のシェイプの組み合わせ)
使用可能なカプセルコライダーに関連するコードスニペットを以下に示します。
C#
FP radius = FP._0_50;
FP extent = FP._1;
Shape2D shape = Shape2D.CreateCapsule(radius, extent);
// カプセルを描画
Draw.Capsule(FPVector2.Zero, shape.Capsule);
Draw.Capsule(FPVector2.Zero, extent, radius);
以下の図は、QuantumとUnity間のカプセルコライダーの違いを明確に示すものです。
重心
「重心(Center of Mass:CoM)」は、PhysicsBodyコンポーネントで設定できます。重心は、Transformコンポーネントで指定された位置に対するオフセットで表されます。重心の位置を変更すると、PhysicsBodyに作用する力に影響します。
デフォルトでは、重心はPhysicsColliderのシェイプの幾何中心に設定されています。これは、PhysicsBodyのConfigのReset Center of Mass On Addedによって強制されます。
注意:重心の位置をカスタマイズするには、Reset Center of Mass On Addedフラグのチェックを外すことが必須です。これを外さないと、PhysicsBodyコンポーネントがエンティティに追加された際に、重心がコライダーの幾何中心にリセットされてしまいます。
上記の構成は、一様な密度の剛体のように振る舞うエンティティの一般的な設定です。ただし、重心とコライダーのオフセットは別々に設定されています。以下の表にその組み合わせを示します。
| PhysicsCollider Offset | PhysicsBody CoM | Reset Center of Mass On Addedフラグ | 最終的な位置 |
|---|---|---|---|
| デフォルト位置 = 0, 0, 0 カスタム値 = デフォルト位置とは異なる任意の位置 |
|||
| デフォルト位置 | デフォルト位置 | On / Off | コライダーの幾何中心と重心の位置は両方とも、transformの位置と同じです。 |
| カスタム値 | デフォルトの位置 | On | コライダーの幾何中心はtransformからオフセットされ、重心はコライダーの幾何中心と同じ位置になります。 |
| カスタム値 | デフォルトの位置 | Off | コライダーの幾何中心はtransformの位置からオフセットされます。 重心はtransformの同じ位置になります。 |
| カスタム値 | カスタム位置 | On | コライダーの幾何中心はtransformの位置からオフセットされます。 重心はコライダーの幾何中心と同じ位置になります。 |
| カスタム値 | カスタム位置 | Off | コライダーの幾何中心はtransformの位置からオフセットされます。 重心はtransformの位置からオフセットされます。 |
Compoundコライダーの重心
Coumpoundシェイプの重心は、すべてのシェイプ要素の幾何中心の組み合わせで、それぞれの面積(2D)または体積(3D)の加重平均に基づきます。
重要なポイント
まとめると、重心の設定に関する重要なポイントは次の通りです。
PhysicsColliderのオフセットとPhysicsBodyの重心位置は互いに異なります。- デフォルトでは、
PhysicsBodyのConfigでReset Center of Mass On AddedとReset Inertia on Addedフラグが設定されています。 - カスタムの重心を設定するには、
PhysicsBodyのConfigのReset Center of Mass On Addedフラグを外します。 PhysicsBodyのConfigでReset Center of Mass On Addedフラグが設定されている場合、エンティティに追加される際に、重心は(エディター上で指定した重心の位置に関わらず)PhysicsColliderの幾何中心に自動的に設定されます。
外力の適用
PhysicsBodyAPIによって、手動で剛体に外力を適用できます。
C#
// これは3DのAPIですが、2Dも同様です。
public void AddTorque(FPVector3 amount)
public void AddAngularImpulse(FPVector3 amount)
public void AddForce(FPVector3 amount, FPVector3? relativePoint = null)
public void AddLinearImpulse(FPVector3 amount, FPVector3? relativePoint = null)
// relativePointは、剛体の重心から力が作用する点までのベクトルで、どちらもワールド座標系です。
// relativePointが与えられると、結果のトルクが計算され適用されます。
public void AddForceAtPosition(FPVector3 force, FPVector3 position, Transform3D* transform)
public void AddImpulseAtPosition(FPVector3 force, FPVector3 position, Transform3D* transform)
// 重心を考慮しながら、指定した位置にforce/imulseを適用する。
PhysicsBodyの角運動量と線形運動量は、以下の適用時に影響を受けます。
- forces
- impulses
これらは似ていますが、重要な違いがあります。forcesは一定期間にわたって適用されるのに対して、impulsesは即時に適用されます。次のように考えることもできます。
- Force =
deltatimeごとの力 - Impulse = フレームごとの力
備考:Quantumのdeltatimeは固定で、Simulation Configアセットで設定されたシミュレーションレートに依存します。
Impulseは、シミュレーションレートによらず同じ効果をもたらしますが、Forceはシミュレーションレートに依存します。シミュレーションレートが30Hz時に剛体に力を適用するとして、シミュレーションレートを60Hzにするとdeltatimeは半分になるため、積分された力も半分になります。
一般的に、Impulseは瞬間的かつ即時の変化を与えたい場合に使用し、Forceは長い期間にわたって常時適用されるものに使用することが望ましいです。
コンポーネントの初期化
PhysicsBodyを動的/キネマティックな剛体として初期化するには、対応する生成関数を使用します。これらのメソッドはPhysicsBody2D/PhysicsBody3Dクラスから利用可能で、以下がその例です。
- PhysicsBody3D.CreateDynamic
- PhysicsBody3D.CreateKinematic
ShapeConfig
データ駆動設計によってPhysicsCollider/PhysicsBodyを初期化するには、ShapeConfig型(Shape2DConfig/Shape3DConfig)を使用します。これら構造体は、任意のQuantumデータアセットにプロパティ(シェイプやサイズなど)として追加可能で、Unityから編集できます。
C#
// ShapeConfigプロパティを含むデータアセット
partial class CharacterSpec {
// これはUnityから編集できる
public Shape2DConfig Shape2D;
public Shape3DConfig Shape3D;
public FP Mass;
}
剛体を初期化する際には、シェイプを直接指定せずにShapeConfigを使用します。
C#
// Frameオブジェクトからプレイヤーのエンティティを生成する
var playerPrototype = f.FindAsset<EntityPrototype>(PLAYER_PROTOTYPE_PATH);
var playerEntity = playerPrototype.Container.CreateEntity(f);
var playerSpec = f.FindAsset<CharacterSpec>("PlayerSpec");
var transform = Transform2D.Create();
var collider = PhysicsCollider2D.Create(playerSpec.Shape2D.CreateShape(f));
var body = PhysicsBody2D.CreateKinematic(playerSpec.Mass);
// 3Dならこちら
var transform = Transform3D.Create();
var collider = PhysicsCollider3D.Create(playerSpec.Shape3D.CreateShape())
var body = PhysicsBody3D.CreateKinematic(playerSpec.Mass);
// コンポーネントを設定する
f.Set(playerEntity, transform);
f.Set(playerEntity, collider);
f.Set(playerEntity, body);
物理コールバックの有効化
エンティティには複数の物理コールバックを関連付けることができます。これらは、コードまたはQuantum Entity PrototypeのPhysicsColliderコンポーネントから有効にすることができます。
コードから物理コールバックを設定し、対応するシグナルを実装する方法については、物理のマニュアルの「コールバック」項目をご覧ください。
キネマティック
物理エンティティにキネマティックな振る舞いをさせる方法は4つあります。
PhysicsColliderコンポーネントのみを持つ:エンティティはPhysicsBodyコンポーネントを持たない(Mass, Drag, Force/Torqueなどがない)ため、エンティティのtransformを自由に操作することが可能ですが、動的な剛体への衝突はエンティティが静止しているもの(速度と角速度が0)として解決されます。PhysicsBodyコンポーネントを無効にする:PhysicsBodyのIsEnabledプロパティをfalseにすると、物理エンジンはエンティティを1と同じように(コライダーコンポーネントのみを持つように)扱い、ForceやVelocityがなくなります。一時的に静的なエンティティとして振る舞い、後で再有効化した際に設定(Mass, Dragなど)を保持するのに適しています。PhysicsBodyコンポーネントのIsKinematicプロパティをtrueに設定する:物理エンジンはPhysicsBody自体に影響を与えませんが、衝突時にその速度と角速度が他の剛体に影響を与えます。物理エンジンには任せずに、エンティティの移動と速度を手動で制御しつつ、他の動的な剛体に反応させることができます。CreateKinematicからPhysicsBodyを初期化する:剛体が常にキネマティックに振る舞うことを期待する場合は、単にキネマティックな剛体を生成します。これによって、PhysicsBodyは最初から3のように振る舞います。剛体が最終的に動的になる必要がある場合は、CreateDynamicメソッドから新しい剛体を作成してIsKinematic = trueに設定してください。IsKinematicをtrue/falseに設定すると、いつでもPhysicsBodyコンポーネントを動的/キネマティックとして再初期化できます。
PhysicsColliderコンポーネント
コンポーネントの無効化/有効化
PhysicsColliderコンポーネントにはEnabledプロパティがあります。このプロパティをfalseに設定すると、PhysicsColliderを持つエンティティはPhysicsSystemから無視されます。
PhysicsBodyはアクティブなPhysicsColliderが必要とするため、効率的に無効化できます。
実行時にシェイプを変更する
PhysicsColliderのシェイプは、初期化された後からでも変更することが可能です。
C#
var collider = f.Get<PhysicsCollider3D>(entity);
collider.Shape = myNewShape;
f.Set(entity, collider);
最初にPhysicsBodyが追加されると、PhysicsColliderのシェイプに基づいて慣性と重心が計算されます。そのため、コライダーのシェイプを変更した後に、ResetInertiaとResetCenterOfMassを呼び出すことを推奨します。
C#
// 上記のスニペットは次の通り
var body = f.Get<PhysicsBody3D>(entity);
body.ResetCenterOfMass(f, entity); // こちらを最初に呼び出す必要がある
body.ResetInertia(f, entity); // こちらをその後に呼び出す必要がある
f.Set(entity, body);
変更前/変更後のシェイプが以下の条件に当てはまる場合は、ResetCenterOfMassを必ず呼び出す必要があります。
- シェイプに位置オフセットがある
- シェイプがCompoundである
- 重心にオフセットがある