Cannon.js 物理引擎入门(Three.js 结合 Cannon.js)
Cannon.js 是一个基于 JavaScript 的 3D 物理引擎,通常与 Three.js 结合使用,来实现刚体碰撞、重力、弹跳、关节约束等物理效果。
1️⃣ 安装 Cannon.js
如果使用 npm:
npm install cannon-es
如果用 CDN 方式,可以在 HTML 文件中引入:
<script src="https://cdn.jsdelivr.net/npm/cannon-es@0.20.0/dist/cannon-es.js"></script>
2️⃣ 创建 Cannon.js 物理世界
在 Cannon.js 中,物理世界 (CANNON.World
) 负责管理所有物体的物理行为:
import * as CANNON from 'cannon-es';
// 创建物理世界
const world = new CANNON.World();
world.gravity.set(0, -9.81, 0); // 设置重力(Y 轴方向向下)
3️⃣ 创建刚体(Rigid Body)
物理世界中的刚体 ≠ Three.js 的 Mesh
CANNON.Body
负责物理行为THREE.Mesh
负责渲染
🔹 创建一个立方体刚体
// 创建刚体的形状(Cannon 只支持基础形状)
const boxShape = new CANNON.Box(new CANNON.Vec3(1, 1, 1)); // 立方体大小 (1,1,1)
// 创建刚体
const boxBody = new CANNON.Body({
mass: 1, // 质量(0 = 静态,>0 = 可移动)
shape: boxShape
});
// 设置刚体初始位置
boxBody.position.set(0, 5, 0);
// 将刚体添加到物理世界
world.addBody(boxBody);
4️⃣ 创建静态地面
地面是一个Plane
(不可移动的静态物体)
// 创建地面形状
const groundShape = new CANNON.Plane();
// 创建地面刚体
const groundBody = new CANNON.Body({
mass: 0, // 质量为 0,表示不会受重力影响(静态)
shape: groundShape
});
// 旋转地面,使其水平
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0);
// 添加到世界
world.addBody(groundBody);
5️⃣ 连接 Three.js
import * as THREE from 'three';
// 创建 Three.js 场景
const scene = new THREE.Scene();
// 创建 Three.js 立方体
const boxGeometry = new THREE.BoxGeometry(2, 2, 2);
const boxMaterial = new THREE.MeshStandardMaterial({ color: 'blue' });
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(boxMesh);
// 创建 Three.js 地面
const groundGeometry = new THREE.PlaneGeometry(10, 10);
const groundMaterial = new THREE.MeshStandardMaterial({ color: 'green' });
const groundMesh = new THREE.Mesh(groundGeometry, groundMaterial);
groundMesh.rotation.x = -Math.PI / 2;
scene.add(groundMesh);
6️⃣ 物理世界更新
每一帧都需要 更新物理世界 并 同步刚体位置 到 Three.js:
const clock = new THREE.Clock();
const tick = () => {
// 计算时间步长
const deltaTime = clock.getDelta();
// 让物理世界前进一小步
world.step(1 / 60, deltaTime, 3);
// 更新 Three.js 物体位置
boxMesh.position.copy(boxBody.position);
boxMesh.quaternion.copy(boxBody.quaternion);
// 渲染
renderer.render(scene, camera);
requestAnimationFrame(tick);
};
tick();
7️⃣ 添加弹性(Restitution)
让物体具有弹性(反弹效果)
boxBody.material = new CANNON.Material();
groundBody.material = new CANNON.Material();
// 创建接触材质
const contactMaterial = new CANNON.ContactMaterial(boxBody.material, groundBody.material, {
friction: 0.1, // 摩擦力
restitution: 0.7 // 弹性(0 = 无弹性,1 = 完全弹性)
});
// 添加到物理世界
world.addContactMaterial(contactMaterial);
8️⃣ 物理约束(关节、连接)
🔹 让两个刚体连接在一起
const pivotA = new CANNON.Vec3(0, 1, 0);
const pivotB = new CANNON.Vec3(0, -1, 0);
const constraint = new CANNON.PointToPointConstraint(boxBody, pivotA, anotherBody, pivotB);
world.addConstraint(constraint);
9️⃣ 触发事件
可以监听物体的 碰撞事件:
boxBody.addEventListener('collide', (event) => {
console.log('碰撞发生!', event);
});
🔟 完整代码示例
import * as THREE from 'three';
import * as CANNON from 'cannon-es';
// 创建 Three.js 场景
const scene = new THREE.Scene();
// 创建 Three.js 立方体
const boxGeometry = new THREE.BoxGeometry(2, 2, 2);
const boxMaterial = new THREE.MeshStandardMaterial({ color: 'blue' });
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(boxMesh);
// 创建 Three.js 地面
const groundGeometry = new THREE.PlaneGeometry(10, 10);
const groundMaterial = new THREE.MeshStandardMaterial({ color: 'green' });
const groundMesh = new THREE.Mesh(groundGeometry, groundMaterial);
groundMesh.rotation.x = -Math.PI / 2;
scene.add(groundMesh);
// 创建物理世界
const world = new CANNON.World();
world.gravity.set(0, -9.81, 0);
// 创建物理立方体
const boxShape = new CANNON.Box(new CANNON.Vec3(1, 1, 1));
const boxBody = new CANNON.Body({ mass: 1, shape: boxShape });
boxBody.position.set(0, 5, 0);
world.addBody(boxBody);
// 创建物理地面
const groundShape = new CANNON.Plane();
const groundBody = new CANNON.Body({ mass: 0, shape: groundShape });
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0);
world.addBody(groundBody);
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 3, 10);
// 动画循环
const clock = new THREE.Clock();
const tick = () => {
const deltaTime = clock.getDelta();
world.step(1 / 60, deltaTime, 3);
boxMesh.position.copy(boxBody.position);
boxMesh.quaternion.copy(boxBody.quaternion);
renderer.render(scene, camera);
requestAnimationFrame(tick);
};
tick();
💡 总结
功能 | 代码示例 |
创建物理世界 |
|
设置重力 |
|
创建刚体 |
|
添加刚体到世界 |
|
创建地面 |
|
同步到 Three.js |
|
添加碰撞事件 |
|
🚀 Cannon.js 适用于刚体模拟,比如小球弹跳、物体掉落等,适合游戏和交互应用!