RigidBody
RigidBodyは、物理シミュレーションに参加するエンティティです。2つのコンポーネントが必要です。物理エンジンがエンティティをどう扱うかを決めるRigidBodyコンポーネントと、形状を与えるCollisionコンポーネントです。Collisionコンポーネントのないrigidbodyコンポーネントには衝突する形がなく、何もしません。形状はコリジョン形状のページで扱います。このページはボディそのものについてです。
ボディのタイプ
RigidBodyのtypeは、誰がそれを動かすかを決めます。
- Static - 決して動きません。地面や壁など、環境の一部となるものに使います。Staticなボディが低コストなのは、エンジンがそれらは動かないものと見なしているためです。作成時に配置した後、エンジンはそのエンティティを二度と確認しません。
- Dynamic - 完全にシミュレートされます。重力で落下し、衝突や力とインパルスに応答し、位置と回転は物理エンジンが所有します。
- Kinematic - エンティティのトランスフォームを通して開発者が動かします。Dynamicなボディを押しのけますが、それらや重力の影響は受けません。動くプラットフォーム、アニメーションで駆動されるドアなど、物理法則ではなくスクリプトに従うものに使います。
下にあるものの上に落ちる、1メートルのDynamicな箱の例です。
- Engine
- Editor
- React
- Web Components
const box = new pc.Entity('box');
box.addComponent('render', { type: 'box' });
box.addComponent('collision', { type: 'box' });
box.addComponent('rigidbody', { type: pc.BODYTYPE_DYNAMIC, mass: 2 });
box.setPosition(0, 5, 0);
app.root.addChild(box);
エンティティを選択し、Add ComponentをクリックしてPhysicsグループからRigid BodyとCollisionの両方を追加します。Rigid BodyのTypeをDynamicに設定すると、インスペクターに後述の質量やマテリアルのプロパティが表示されます。デフォルトのボックスコリジョン形状(Half Extentsが0.5)は、1ユニットのレンダーボックスに一致します。
import { Entity } from '@playcanvas/react';
import { Render, Collision, RigidBody } from '@playcanvas/react/components';
<Entity name="box" position={[0, 5, 0]}>
<Render type="box" />
<Collision type="box" />
<RigidBody type="dynamic" mass={2} />
</Entity>
<pc-entity name="box" position="0 5 0">
<pc-render type="box"></pc-render>
<pc-collision type="box"></pc-collision>
<pc-rigid-body type="dynamic" mass="2"></pc-rigid-body>
</pc-entity>
質量とマテリアル
4つのプロパティが、ボディの重さと接触時の表面の振る舞いを決めます。
| プロパティ | 説明 |
|---|---|
| Mass | Dynamicなボディの質量(キログラム)。重いボディは加速により大きな力を必要とし、軽いボディを押しのけます。デフォルトは1で、StaticとKinematicのボディでは無視されます |
| Friction | 表面が滑りにどれだけ強く抵抗するか(0から1)。デフォルトは0.5 |
| Rolling Friction | 転がりに対する抵抗。球や円柱が永遠に転がり続けるのを防ぎます。デフォルトは0 |
| Restitution | 弾力性(0から1)。デフォルトは0。接触する2つのボディの値は掛け合わされるため、反発係数1のボールも反発係数0の床では跳ねません |
- Engine
- Editor
- React
- Web Components
entity.rigidbody.mass = 5;
entity.rigidbody.friction = 0.8;
entity.rigidbody.rollingFriction = 0.1;
entity.rigidbody.restitution = 0.6;
Rigid BodyコンポーネントでMass、Friction、Rolling Friction、Restitutionを設定します。MassはDynamicなボディでのみ表示されます。
<RigidBody type="dynamic" mass={5} friction={0.8} rollingFriction={0.1} restitution={0.6} />
<pc-rigid-body type="dynamic" mass="5" friction="0.8" rolling-friction="0.1" restitution="0.6"></pc-rigid-body>
ダンピングとファクター
2組のプロパティが、動き始めたDynamicなボディの運動を調整します。
- Linear DampingとAngular Dampingは、空気抵抗のように、ボディが毎秒失う速度の割合(0から1)です。デフォルトは0です。
- Linear FactorとAngular Factorは、ワールド軸ごとに移動と回転をスケーリングします。ファクターが0の軸はロックされます。Angular Factorを
0, 0, 0にするとキャラクターが直立を保ち、Linear Factorを1, 1, 0、Angular Factorを0, 0, 1にすると2Dゲーム用にボディをXY平面に閉じ込められます。デフォルトは1, 1, 1です。
- Engine
- Editor
- React
- Web Components
// 空気抵抗のように、時間とともにボディを減速させる
entity.rigidbody.linearDamping = 0.2;
entity.rigidbody.angularDamping = 0.2;
// キャラクターを直立させる:どの軸でも回転しない
entity.rigidbody.angularFactor = new pc.Vec3(0, 0, 0);
// 2DゲームのXY平面にボディを閉じ込める
entity.rigidbody.linearFactor = new pc.Vec3(1, 1, 0);
Rigid BodyコンポーネントでLinear Damping、Angular Damping、Linear Factor、Angular Factorを設定します。これらはDynamicなボディでのみ表示されます。
<RigidBody type="dynamic" linearDamping={0.2} angularDamping={0.2} angularFactor={[0, 0, 0]} />
<pc-rigid-body type="dynamic" linear-damping="0.2" angular-damping="0.2" angular-factor="0 0 0"></pc-rigid-body>
重力スケール
重力スケールは、1つのDynamicなボディに対してワールドの重力に掛ける倍率です。デフォルトは1で、ボディは他のものと同じように落下します。0から1の間の値ではよりゆるやかに落下し、0では重力の影響を受けずに浮かび、負の値では風船のように上昇します。この倍率はその時点のワールドの重力に適用されるため、後からワールドの重力を変更するとボディもそれに従います。StaticとKinematicのボディは重力で動かないため、このプロパティは影響しません。
軸方向の移動をすべてロックするLinear Factorの0とは異なり、重力スケールの0は重力だけを取り除きます。ボディは引き続き衝突、力、インパルスに応答します。小さな惑星の中心に引き寄せる重力のように、ボディに独自の重力を与えるには、スケールを0にして、その力を加える処理を毎フレーム自分で行います。
スケールを変更しても、スリープ中のボディは復帰しません。コード、Reactのプロパティ、Web Componentsの属性のどれから設定しても同じです。床の上で静止している箱は、負のスケールを与えても何かに乱されるまでその場にとどまるため、スリープしている可能性のあるボディのスケールを変更した後はactivate()を呼び出してください(スリープを参照)。
- Engine
- Editor
- React
- Web Components
// ワールドの重力の半分で落下する
entity.rigidbody.gravityScale = 0.5;
// 風船のようにゆっくり上昇する。スリープしている場合に備えてボディを復帰させる
entity.rigidbody.gravityScale = -0.2;
entity.rigidbody.activate();
Rigid BodyコンポーネントでGravity Scaleを設定します。ダンピングやファクターのプロパティと同様に、Dynamicなボディでのみ表示されます。
<RigidBody type="dynamic" gravityScale={-0.2} />
<pc-rigid-body type="dynamic" gravity-scale="-0.2"></pc-rigid-body>
スリープ
静止したDynamicなボディは物理エンジンによってスリープ状態にされ、何かに乱されるまでシミュレーションが止まります。力、インパルス、トルクを加えたり、速度を代入したり、テレポートしたりすると自動的に復帰し、他のボディにぶつかられた場合も同様です。自分で復帰させることもできます。
if (!entity.rigidbody.isActive()) {
entity.rigidbody.activate();
}
移動とテレポート
ボディのタイプによって、その位置と回転を誰が所有するか、つまりどう動かすかが決まります。
| タイプ | 動かし方 | エンティティのトランスフォームを設定したときの結果 |
|---|---|---|
| Dynamic | 力、インパルス、速度。瞬間移動にはteleport | ボディには何も起きません。動いているボディは次のステップで変更を上書きし、スリープ中のボディは取り残されます |
| Kinematic | エンティティのトランスフォーム | 次のステップでボディが追従します |
| Static | 動かすことは想定されていません。位置を変えるにはteleport | ボディには何も起きず、作成された場所に留まります |
以下の呼び出しは、どの方法で構築しても同じです。Reactでは<Entity>の中に配置したコンポーネントからuseParentフックでエンティティを取得し、Web Componentsでは<pc-entity>要素のentityプロパティから取得します。ボディを動かすでは、各環境で毎フレームのコードをどこで実行するかを示しています。
Dynamicなボディ
Dynamicなボディの位置と回転は物理エンジンが所有します。動かすには力や速度を使うか、リスポーン時のようにどこかへ瞬間移動させる必要があるときにrigidbodyコンポーネントのteleportを呼び出します。テレポートはボディの速度をクリアしないため、静止した状態で到着させたい場合は先に速度をリセットしてください。
// 現在の回転を保ったまま位置を移動する
entity.rigidbody.teleport(0, 10, 0);
// 移動と回転。回転はオイラー角(度)で指定
entity.rigidbody.teleport(new pc.Vec3(0, 10, 0), new pc.Vec3(0, 90, 0));
// 静止した状態でリスポーンする:以前の動きをクリアし、クォータニオンで移動と回転を行う
entity.rigidbody.linearVelocity = pc.Vec3.ZERO;
entity.rigidbody.angularVelocity = pc.Vec3.ZERO;
entity.rigidbody.teleport(spawn.getPosition(), spawn.getRotation());
linearVelocityやangularFactorなどのベクトルプロパティは、読み取り専用のスナップショットを返します。返されたベクトルを編集するのではなく、新しいベクトルを代入してください。
Kinematicなボディ
Kinematicなボディは逆の仕組みです。setPosition、setRotation、lookAtなどのトランスフォームAPIでエンティティを動かすと、ボディは次のステップで追従し、その上に乗っているDynamicなボディを運びます。
// X軸に沿って前後に滑るプラットフォーム
platform.setPosition(Math.sin(time) * 2, 0.5, 0);
Staticなボディ
エンジンはStaticなボディの位置と回転をボディの作成時にエンティティから読み取り、その後は二度と確認しません。Staticなボディを追跡しないことが低コストである理由の一つですが、そのためにプレイ中にエンティティを移動または回転させると、ボディは取り残されます。モデルは動いても、他のボディは元の場所にある見えない形状と衝突し続けます。
ドアやエレベーターのようにプレイ中に動くオブジェクトはKinematicにしてください。レベルの配置を変えるときなど、Staticなボディの位置をときどき変える場合はteleportを呼び出します。エンティティとボディが一緒に移動します。
// Staticな壁とそのボディを新しい位置に移動する
wall.rigidbody.teleport(0, 0, 10);
Staticなボディをテレポートしても、その上に乗っているボディはスリープから復帰しません。スリープ中のボディはその場に留まり、宙に浮いたままになることもあります。
スケーリング
RigidBodyには位置と回転はありますが、スケールはありません。そのため、エンティティをスケールしてもボディのサイズは変わりません。ボディのサイズはコリジョン形状で決まります。ボックス、球、カプセル、円柱、円錐は、Half ExtentsやRadiusなどのプロパティでサイズを変えます。ボディは新しいサイズで再構築され、動きを保ちます。例外はメッシュ形状で、エンティティのスケールに従います。理由と形状の種類ごとの詳細は、コリジョン形状ページのスケールで説明しています。
コリジョングループとマスク
すべてのボディはコリジョングループに属し、衝突する相手のグループを表すマスクを持ちます。2つのボディは、それぞれのグループが相手のマスクに含まれているときにだけ相互作用します。エンジンはボディのタイプが設定されたときに両方を割り当てます。Dynamicなボディはすべてと衝突し、StaticとKinematicのボディは互いに衝突しません。例えば弾丸同士が当たらないようにするなど、コードからこれを絞り込むことができます。
// ボディをユーザーグループに入れ、同じグループの他のメンバーと衝突しないようにする。
// タイプを変更するとリセットされるため、タイプの後に設定する。
entity.rigidbody.group = pc.BODYGROUP_USER_1;
entity.rigidbody.mask = pc.BODYMASK_ALL ^ pc.BODYGROUP_USER_1;
pc.BODYGROUP_USER_1からpc.BODYGROUP_USER_8までの8つのユーザーグループを自由に使用できます。同じグループとマスクでレイキャストをフィルタリングすることもできます。
関連情報
- コリジョン形状 - すべてのRigidBodyに必要な形状
- ボディを動かす - 力、インパルス、トルク、速度
- Rigid Bodyコンポーネント - すべてのプロパティのエディターリファレンス
- RigidBodyComponent - APIリファレンス
- コードでRigidBodyを作成する - スクリプトから両方のコンポーネントを追加するチュートリアル