Three.jsの物理エンジンはどれを選ぶ?cannon-es・Rapier・Jolt比較

Three.jsに物理エンジンを入れようと調べると、cannon.js、cannon-es、Ammo.js、Rapier、Jolt…と選択肢が多くて、最初にここで足が止まります。私は一通り実際に試したので、その結果を比較としてまとめておきます。

結論から言うと基本はRapierをおすすめしますが、cannon-esとJoltにもそれぞれ選ぶ理由があります。

3つのエンジンをひと目で比較

cannon-esRapierJolt
実装純JavaScriptRust製 → WASMC++製 → 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で始めて、困ったら特性に合わせて乗り換える、で問題ないと思います。