はじめに
PlayCanvasの物理演算は、オープンソースのBullet物理エンジンをWebAssemblyに移植したammo.jsによって動作します。読み込みが済んだら、エンティティがどのように動くかを決めるRigidBodyコンポーネントと、形状を与えるCollisionコンポーネントを持つエンティティからシミュレーションを組み立てます。このページでは、ライブラリの読み込み、グローバルな設定、そしてこのセクションの他のすべてのページが前提とするルールを扱います。
物理を有効化
物理演算はオプトインです。ammo.jsは数百キロバイトのサイズがあるため、要求しない限り何も読み込みません。配布は3つのファイルで行われます。WebAssemblyモジュールのammo.wasm.wasm、そのJavaScriptグルーであるammo.wasm.js、そしてWebAssemblyをサポートしないブラウザ向けのasm.jsフォールバックであるammo.jsです。ライブラリが存在するまで、物理コンポーネントは何もしないプレースホルダーです。追加しても何も起こらず、エラーも発生しません。
- Engine
- Editor
- React
- Web Components
アプリケーションを作成する前にモジュールを読み込みます。3つのファイルは、エンジンリポジトリのexamples/assets/wasm/ammo/とsync-ammo npmパッケージに同梱されています。
pc.WasmModule.setConfig('Ammo', {
glueUrl: 'ammo/ammo.wasm.js',
wasmUrl: 'ammo/ammo.wasm.wasm',
fallbackUrl: 'ammo/ammo.js'
});
// モジュールを待ってから、通常どおりアプリケーションを作成する
await new Promise((resolve) => {
pc.WasmModule.getInstance('Ammo', () => resolve());
});
const app = new pc.Application(canvas);
アプリケーションの開始時にRigidBodyシステムがAmmoグローバルを見つけ、ammo.jsバックエンドをインストールします。pc.Applicationはすべてのコンポーネントシステムを登録します。代わりにpc.AppBaseとpc.AppOptionsから構築する場合は、pc.CollisionComponentSystemとpc.RigidBodyComponentSystem(ジョイントを使うならpc.JointComponentSystemも)をcomponentSystemsに追加してください。Falling Shapesの例に完全な手順があります。
Scene Settingsパネルを開き、PHYSICSを展開してIMPORT AMMOをクリックします。これによりPlayCanvasストアから3つのファイルがアセットに追加され、エンジンはシーンの開始前にそれらを読み込みます。

ライブラリがインポートされるまで、CollisionとRigidBodyコンポーネントのインスペクターには、同じボタン付きのAmmo module not foundという警告が表示されます。独自のammo.jsビルドを使用するには、ストア版の代わりにWASMモジュールアセットとして追加してください。
ammo.jsがモジュールアセットとして配布される前に作成されたプロジェクトは、ランチャーが自動的に挿入するレガシー版のライブラリを使用していました。プロジェクトにammo.jsのアセットがないのに物理演算が動作する場合は、これが理由です。IMPORT AMMOをクリックするとモジュールアセットが追加され、同時にレガシーライブラリが無効になるため、プロジェクトが両方を読み込むことはありません。インポートされるビルドはより新しく、小さく、高速なので、できるだけ早く切り替えてください。
モジュールをインストールし、ルートの<Application>にusePhysicsプロパティを設定します。
npm install sync-ammo
import { Application } from '@playcanvas/react';
<Application usePhysics>
{/* <RigidBody> と <Collision> コンポーネントを持つエンティティ */}
</Application>
usePhysicsを設定するとammo.jsは遅延読み込みされるため、ライブラリが届く前にシーンが描画され、届いた時点で物理コンポーネントが有効になります。そのタイミングを待つ必要があれば、usePhysicsフックのisPhysicsLoadedを参照してください。詳細はReactの物理演算ガイドを参照してください。
<pc-app>の中で<pc-wasm>タグを使ってモジュールを宣言します。
<pc-app>
<pc-wasm name="Ammo"
glue="https://developer.playcanvas.com/assets/modules/ammo/ammo.wasm.js"
wasm="https://developer.playcanvas.com/assets/modules/ammo/ammo.wasm.wasm"
fallback="https://developer.playcanvas.com/assets/modules/ammo/ammo.js"></pc-wasm>
<pc-scene>
<!-- <pc-rigid-body> と <pc-collision> コンポーネントを持つエンティティ -->
</pc-scene>
</pc-app>
アプリケーションはモジュールを待ってから開始するため、シーン内のすべての物理タグは最初のフレームから機能します。
重力 (Gravity)
重力は、すべてのDynamicなRigidBodyに適用される一定の加速度です。デフォルトのワールドY軸方向(真下)に-9.81という値は地球の重力に近いものです。宇宙を舞台にしたゲームではゼロに、月ならもう少し小さな値に設定します。
- Engine
- Editor
- React
- Web Components
// 月の重力。古い setGravity() メソッドは非推奨で、このプロパティに置き換えられている。
app.systems.rigidbody.gravity = new pc.Vec3(0, -1.62, 0);
Scene SettingsパネルのPHYSICSセクションでGravityを設定します。
重力のプロパティはないため、<Application>の中のコンポーネントからアプリケーションに設定します。
import { useEffect } from 'react';
import { useApp } from '@playcanvas/react/hooks';
function LunarGravity() {
const app = useApp();
useEffect(() => {
app.systems.rigidbody.gravity.set(0, -1.62, 0);
}, [app]);
return null;
}
<pc-scene gravity="0 -1.62 0">
<!-- ... -->
</pc-scene>
測定の単位
物理エンジンは1ユニットを1メートルとして解釈し、質量はキログラムで測ります。オブジェクトが自然な速さで落下するように、シーンのサイズをそれに合わせてください。身長1.8mのキャラクターは1.8ユニットの高さにします。大きく異なるスケールで作られたシーンもシミュレートはされますが、重力が弱すぎたり強すぎたりして見え、非常に小さな形状は互いをすり抜けやすくなります。
シミュレーションの仕組み
エンジンがシミュレーションを進める方法から、いくつかのルールが導かれます。このセクションの残りはこれらを前提としています。
- シミュレーションは固定レートでステップします。 毎フレーム、物理ワールドは1/60秒の固定ステップで進み、フレーム時間が必要とする回数(上限あり)だけステップします。そのため、ボディは30fpsでも144fpsでも同じように振る舞います。60Hzより速いディスプレイではステップが1回も実行されないフレームがあり、その場合はボディのトランスフォームが補間されて動きが滑らかに保たれます。
- Dynamicなボディは物理エンジンが所有します。 各ステップの後、エンジンはすべてのDynamicなボディの位置と回転をエンティティに書き戻します。エンティティのトランスフォームに設定した値は上書きされるため、Dynamicなボディは力、速度、または
teleportで動かします(RigidBodyを参照)。 - StaticとKinematicなボディは開発者が所有します。 それらのトランスフォームは各ステップの開始時にエンティティから読み取られるため、他のエンティティと同じように動かせます。
- イベントはステップごとに発火します。 衝突イベントとトリガーイベントは、フレームごとではなく物理ステップごとに1回通知されます。床の上で静止している箱は、静止している間ずっと毎ステップ
contactイベントを発生させます。