physics について
@pmndrs/viverse でインタラクティブなシーンを開発するための、BVH ベースの物理スキルです。静的・キネマティックなジオメトリ、キャラクターコントローラー、センサーを提供し、ゲーム固有のルールは実装側に委ねます。
主な機能
- 01BVH 物理ワールドの作成
- 02静的・キネマティック剛体の追加
- 03キャラクターの速度とインパルス制御
- 04接地状態の確認
- 05センサーへの進入・退出の検出
活用シーン
- 01シーンのジオメトリに衝突判定を追加
- 02キャラクターの移動やジャンプの実装
- 03領域への進入・退出への応答
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ drawcall-ai/ skills --skill physicsインストール後、Claude Code で/physicsを入力して読み込まれたか確認します。
詳細
物理演算
静的/キネマティックジオメトリに BVH(Bounding Volume Hierarchy)を使用する物理演算です。動的ボディ同士はまだ相互作用できません。
物理ワールド
物理ワールドを作成します
import { BvhPhysicsWorld } from '@pmndrs/viverse'
const world = new BvhPhysicsWorld()
addBody(object, kinematic) — 第 2 引数は、ボディがキネマティック(プラットフォームや列車のようにスクリプトで動かすもの)かどうかを指定します。静的かどうかではありません。静的なワールドジオメトリ(地面、壁)には false を、自分で動かすものには true を渡します。
world.addBody(ground, false) // static
world.addBody(train, true) // kinematic (you move it each frame)
フラグの意味が逆なので注意してください。
addBodyのフラグはkinematicですが、以下のaddSensorの第 2 フラグはisStaticであり、ブール値の意味は逆です。
キャラクターコントローラー
BvhCharacterPhysics はキャラクターコントローラーで、キャラクターのボディを移動させ、衝突判定を行います。キャラクターをアニメーションさせるものでも、カメラでもありません。操作可能なヒューマノイドを構成するには、CharacterCameraBehavior(カメラ)と Acta を組み合わせて、アニメーションとそれによって生じる動きをすべて処理してください(acta スキルを参照)。SimpleCharacter は使わないでください。Acta と競合する独自のアニメーションが含まれています。
キャラクターコントローラーを作成します
import { BvhCharacterPhysics, type BvhCharacterPhysicsOptions } from '@pmndrs/viverse'
const characterPhysics = new BvhCharacterPhysics(world)
コントローラーは、キャラクターオブジェクトの原点が足元(カプセルの底)にあることを前提としています。モデルの原点が中心や頭にある場合、キャラクターが浮いたり沈んだりします。その場合は、原点が足元にある Group でモデルを囲んでください。調整可能な項目は、オプションの BvhCharacterPhysicsOptions(update の第 3 引数)にあります。capsuleRadius、capsuleHeight、gravity、maxGroundSlope、updatesPerSecond、linearDamping を設定できます。
操作方法
characterPhysics.inputVelocity.copy(velocity) // continuous (walking) — set each frame
characterPhysics.applyVelocity(jumpImpulse) // one-off impulse (e.g. jumping)
Acta のキャラクターでは、情報の流れは 入力 → Acta → 物理演算 です。Acta には移動の 意図(カメラ相対の
moveDirection、長さ 0..1)を渡します。Acta は有効なアニメーションを望ましい速度に変換し、motionコールバックでその速度を渡します。そこでinputVelocityにその速度を設定し、コントローラーを更新してください。生の入力からinputVelocityを設定してはいけません。また、ほかの場所でキャラクターを移動させないでください。acta スキルを参照してください。
コントローラーの更新
速度を設定した後、毎フレーム呼び出します。
characterPhysics.update(character, delta) // pass a BvhCharacterPhysicsOptions third arg to tune
接地状態
characterPhysics.isGrounded // true when character is on the ground
コントローラーが行わないこと — これらは自分で実装してください
コントローラーが提供するプリミティブは、inputVelocity(連続的な速度)と applyVelocity(インパルス)、および isGrounded の 2 つだけです。特定のゲームで必要となるルールは、自分で記述してください。
- 複数回ジャンプ/二段ジャンプ、コヨーテタイム、ジャンプ入力のバッファリング: ジャンプ回数のカウンターはありません。最初の
applyVelocityはisGroundedのときだけ実行し、空中ジャンプの回数は独自に管理して、接地時にリセットしてください。 - 壁ジャンプ/壁滑り:
isGroundedが検知するのは地面だけで、壁は対象外です。カプセルから水平方向にworld.raycast(ray, far)を使って壁を検知してください。返される交差情報には、衝突距離と面の法線が含まれます。その後、壁から離れる方向と上方向にapplyVelocityを適用してください。 - 動くプラットフォームへの追従: キネマティックプラットフォームの上に立っても、キャラクターは運ばれません。プラットフォームに乗っている間は、毎フレームの移動量を自分でキャラクターに加算してください。
- ワールドの端から落ちる/範囲外に出る場合:
isGroundedが true になるのは、カプセルから下向きに行うシェイプキャストが衝突ジオメトリに当たった場合だけです。有限の地面メッシュの端を越えると、衝突対象がなくなるため、キャラクターは無限に落下し、空中/落下ポーズのままになります(意図しない「飛行」や滑空状態のように見えることがよくあります)。コントローラーはこれを防ぎません。ワールドが無限であると想定せず、衝突可能なエリア内にプレイヤーを留めてください。位置を制限する、範囲外用の床面を追加する、または落下時にリスポーンさせるキルバリアを設置するといった方法があります。プレイヤーを、表示上のプレイエリアより広い場合もある物理メッシュの範囲内に留めてください。
これらはゲームプレイ上のルールであり、機能の不足ではありません。コントローラーは、ゲームごとに操作感を調整できるよう、意図的に最小限の構成になっています。
センサー
センサーは、キャラクターがボリュームに入ったとき、または出たときに検知します。
センサーを追加します
world.addSensor(object, isStatic, (intersected) => {
if (intersected) {
// character entered the sensor
} else {
// character exited the sensor
}
})
センサーを削除します
world.removeSensor(object)
キャラクターが入ったセンサーオブジェクトを渡す単一のコールバックを使用する場合は、以下のようにします。
function onEnter(object: Object3D) {}
world.addSensor(obj1, isStatic, (intersected) => intersected && onEnter(obj1))
world.addSensor(obj2, isStatic, (intersected) => intersected && onEnter(obj2))