Cannon.js + Three.jsでオブジェクトの位置を物理演算で算出してみました。
ざっくりとした流れとしては、
❶ Cannon.jsで計算用のworldをつくる
❷ 毎フレーム、Cannon.jsでオブジェクトの位置を計算する
❸ 毎フレーム、Three.jsのオブジェクトの位置と姿勢をCannon.jsの位置と姿勢に合わせる
という感じです。
それに伴い、Cannon.jsをいい感じに使えるようにするカスタムフックを作りました。
ソースコード
useCannon
import * as CANNON from 'cannon-es'; import { useEffect, useRef, useState } from 'react'; export default function useCannon() { const cannonObjectListRef = useRef<{ body: CANNON.Body; mesh: THREE.Mesh; }[]>([]); const [ world ] = useState(new CANNON.World()); const [ cannonObjectList, setCannonObjectList ] = useState<{ body: CANNON.Body; mesh: THREE.Mesh; }[]>([]); useEffect(() => { world.gravity.set(0, -9.81, 0); }, [world]); function addCannonObject(body: CANNON.Body, mesh: THREE.Mesh) { // ❶ cannonObjectListRef.current.push({ body, mesh }); setCannonObjectList(cannonObjectListRef.current); world.addBody(body); } function upDateCannonObject() { // ❷ world.fixedStep(); cannonObjectList.forEach(({ mesh, body }) => { mesh.position.copy(body.position as any); mesh.quaternion.copy(body.quaternion as any); }); } return { CANNON, world, cannonObjectList, addCannonObject, upDateCannonObject }; }
❶ CANNON.BodyとTHREE.Meshをペアで登録する
❷ CANNON.Bodyの位置を更新し、THREE.Meshのposition、quaternionに代入する
という機能を実装しました。
- 登録したオブジェクトを削除する
- 動的にオブジェクトを増やした際の処理を最適化する
を実装していないので、そこは今後に期待です。
つかいかた
index.jsx(抜粋)
const { CANNON, cannonObjectList, addCannonObject, upDateCannonObject } = useCannon(); useEffect(() => { if (!cannonObjectList.length) { const floorBody = new CANNON.Body({ mass: 0, shape: new CANNON.Plane() }); floorBody.position.set(0, 0, 0); floorBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); const floor = new THREE.Mesh( new THREE.PlaneGeometry(8, 8, 1, 1), new THREE.MeshNormalMaterial() ); floor.position.set(0, 0, 0); floor.rotation.x = -Math.PI / 2; addCannonObject(floorBody, floor); // ❶ scene.add(floor); const cubeBody = new CANNON.Body({ mass: 1, shape: new CANNON.Box( new CANNON.Vec3(.5, .5, .5) ) }); cubeBody.position.set(0, 4, 0); cubeBody.angularVelocity.set(1, 1, 1); const cube = new THREE.Mesh( new THREE.BoxGeometry(1, 1, 1), new THREE.MeshNormalMaterial() ); addCannonObject(cubeBody, cube); // ❶ scene.add(cube); return; } camera.position.set(8, 8, 8); camera.lookAt(new THREE.Vector3(0, 0, 0)); renderer.setAnimationLoop(() => { upDateCannonObject(); // ❷ renderer.render(scene, camera); }); }, [cannonObjectList]);
❶ CANNON.BodyとTHREE.Meshをペアで登録する
❷ CANNON.Bodyの位置を更新し、THREE.Meshのposition、quaternionに代入する
という感じで使います。