Three.jsの物理エンジンはどれを選ぶ?cannon-es・Rapier・Jolt比較
Three.jsに物理エンジンを入れようと調べると、cannon.js、cannon-es、Ammo.js、Rapier、Jolt…と選択肢が多くて、最初にここで足が止まります。私は一通り実際に試したので、その結果を比較としてまとめておきます。
結論から言うと基本はRapierをおすすめしますが、cannon-esとJoltにもそれぞれ選ぶ理由があります。
3つのエンジンをひと目で比較
| cannon-es | Rapier | Jolt | |
|---|---|---|---|
| 実装 | 純JavaScript | Rust製 → WASM | C++製 → WASM |
| 性能 | 控えめ | 高速 | 最高クラス |
| 導入 | 一番楽(importするだけ) | 楽(compat版なら設定不要) | やや面倒 |
| API | 素直で読みやすい | 現代的 | C++移植そのままで硬い |
| ドキュメント | 例が豊富 | 公式ドキュメントが充実 | 情報が少ない |
| 開発状況 | 停滞気味 | 活発 | 活発 |
cannon-es: とにかく手軽、プロトタイプ向き
cannon-esは古参のcannon.jsをコミュニティがメンテナンスしているフォークです。純JavaScriptなのでWASMの初期化待ちが存在せず、importしてすぐ使えます。
import * as CANNON from 'cannon-es';
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
const body = new CANNON.Body({
mass: 1, // 0にすると動かない静的ボディ
shape: new CANNON.Sphere(0.5),
position: new CANNON.Vec3(0, 8, 0),
});
world.addBody(body);
// ループ内: 固定タイムステップ+補間
world.step(1 / 60, deltaTime, 3);
mesh.position.copy(body.position);
mesh.quaternion.copy(body.quaternion);
APIが素直で、mass: 0 なら静的、mass > 0 なら動的というシンプルさです。実際に触れるデモを用意しました。Rapier入門記事のデモとまったく同じ内容をcannon-esで実装したものなので、書き味と挙動を見比べられます。
弱点は性能と将来性です。純JSなのでオブジェクトが数百を超えると厳しくなってきますし、本家cannon.jsの開発は完全に止まっていて、cannon-esの更新も緩やかです。「箱を数個落としたい」「ちょっとした演出に使いたい」程度なら今でも全然ありです。
Rapier: 迷ったらこれ
Rust製・WASM動作のRapierは、性能・ドキュメント・開発の活発さのバランスが現状ベストだと思っています。
import RAPIER from '@dimforge/rapier3d-compat';
await RAPIER.init(); // WASMの初期化を待つ
const world = new RAPIER.World({ x: 0, y: -9.8, z: 0 });
const body = world.createRigidBody(
RAPIER.RigidBodyDesc.dynamic().setTranslation(0, 8, 0)
);
world.createCollider(RAPIER.ColliderDesc.ball(0.5), body);
cannon-esとの一番の思想の違いは、ボディ(位置や速度)とコライダー(当たり判定の形)が分離していることです。1つのボディに複数のコライダーを付ける、といった柔軟な構成ができます。ボディの種類もdynamic / fixed / kinematicが明示的で、キャラクター操作に向いたkinematicの扱いがしっかりしています。
WASMのビルド設定が面倒という評判は、**compat版(WASMがJSに埋め込み済み)**を使えば解決します。CDNでもViteでも設定ゼロです。導入手順と落とし穴はRapier入門記事に詳しく書きました。
Jolt: 性能最優先の本格派
JoltはAAAゲーム(Horizon Forbidden Westなど)で実際に使われているC++物理エンジンのWASM移植です。性能は3つの中で頭ひとつ抜けていて、大量のオブジェクトや複雑なシミュレーションでは最有力です。
import initJolt from 'jolt-physics';
const Jolt = await initJolt(); // こちらもWASMの初期化を待つ
// 以降、Jolt.Vec3 や Jolt.BodyCreationSettings などC++風のAPIが続く
ただし書き味はC++の移植そのままで、レイヤーやフィルターの概念を最初に理解する必要があり、メモリ管理(Jolt.destroy())も自前です。そして何より日本語どころか英語の情報も少ない。公式のサンプルコードを読み解ける人向けです。
用途別のおすすめ
- とにかく手軽に、演出程度の物理 → cannon-es
- ゲームを作る、キャラクターを動かす、長く使う → Rapier(迷ったらこれ)
- 物量で性能の限界を攻める → Jolt
物理エンジンはAPIの概念(ワールド・ボディ・コライダー・ステップ・同期)がどれもほぼ共通なので、1つ覚えれば乗り換えは難しくありません。まずRapierで始めて、困ったら特性に合わせて乗り換える、で問題ないと思います。