简介
Cannon.js 是一个基于 JavaScript 的物理引擎,它可以在浏览器中模拟物理效果。它支持碰撞检测、刚体动力学、约束等物理效果,可以用于创建逼真的物理场景和交互。
原理
Cannon.js 使用了欧拉角来表示物体的旋转,而不是四元数。这使得它在处理旋转时更加直观和易于理解。Cannon.js 还支持多种碰撞检测算法,包括离散碰撞检测和连续碰撞检测。
Cannon.js 还支持多种约束类型,包括固定约束、滑动约束和铰链约束等。这些约束可以用于限制物体的运动,例如将两个物体固定在一起,或者将一个物体限制在一个滑动路径上。这使得 Cannon.js 可以创建逼真的物理场景和交互。
安装
bash
npm install cannon-es
使用
需要要使用 Cannon.js,首先需要创建一个物理世界,然后向其中添加物体和约束。接下来,在每个时间步长中更新物理世界,并使用物理世界中的物体和约束来计算物体的位置和旋转,由于在物理世界中创建的物体是看不到的,所以需要将这些位置和旋转应用到 Three.js 场景中的物体上。
引入
js
import * as CANNON from "cannon-es";
创建物理世界
js
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0); // 设置重力
word.gravity: 属性用于设置物理世界的重力。
- x: 重力的 x 分量。
- y: 重力的 y 分量。
- z: 重力的 z 分量。
创建物体
js
//物理世界物体
const sphereShape = new CANNON.Sphere(1); // 创建一个球体形状
const sphereBody = new CANNON.Body({
mass: 1,
shape: sphereShape,
position: new CANNON.Vec3(0, 5, 0),
}); // 创建一个球体物体
world.addBody(sphereBody); // 将球体添加到物理世界中
//Three.js 场景中的物体
const sphereGeometry = new THREE.SphereGeometry(1, 32, 32); // 创建一个球体几何体
const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // 创建一个球体材质
const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial); // 创建一个球体网格
scene.add(sphereMesh); // 将球体网格添加到场景中
CANNON.Sphere(): 方法用于创建一个球体形状。
- radius: 球体的半径。
CANNON.Body(): 方法用于创建一个物体。
- mass: 物体的质量。
- shape: 物体的形状。
- position: 物体的位置。
world.addBody(): 方法用于将物体添加到物理世界中。
更新物理世界
js
function animate() {
requestAnimationFrame(animate);
world.step(1 / 60); // 更新物理世界
// 将物理世界中的球体位置应用到 Three.js 场景中的球体网格上
sphereMesh.position.copy(sphereBody.position);
// 将物理世界中的球体旋转应用到 Three.js 场景中的球体网格上
sphereMesh.quaternion.copy(sphereBody.quaternion);
renderer.render(scene, camera); // 渲染场景
}
animate();
world.step(): 方法用于更新物理世界中的物体和约束的状态。
- dt: 时间步长,用于计算物体的运动和碰撞。
- time: 当前时间,用于计算物体的运动和碰撞。
- correction: 时间步长修正,用于修正时间步长导致的误差。
copy(): 方法用于将一个向量的值复制到另一个向量中。
- v: 要复制的向量。
碰撞检测
Cannon.js 支持多种碰撞检测算法,包括离散碰撞检测和连续碰撞检测。离散碰撞检测是在每个时间步长中检查物体之间的碰撞,而连续碰撞检测则是在物体之间可能发生碰撞的路径上插入额外的检查点。这使得 Cannon.js 可以处理复杂的碰撞场景,例如物体之间的穿透和重叠。
js
const groundShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5));
const groundBody = new CANNON.Body({
shape: groundShape,
position: new CANNON.Vec3(0, 0, 0),
type: CANNON.Body.STATIC,
});
groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1);
world.addBody(groundBody);
//创建一个平面的几何体
const groundGeometry = new THREE.BoxGeometry(10, 0.2, 10);
//创建一个平面的材质
const groundMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
//创建一个平面的网格
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
//设置平面的位置
ground.position.x = 0.1;
//将平面添加到场景中
scene.add(ground);
CANNON.Plane(): 方法用于创建一个平面形状。
CANNON.Box(): 方法用于创建一个盒子形状。
setFromAxisAngle(): 方法用于设置物体的旋转。
- axis: 旋转轴。
- angle: 旋转角度。
body.type: 物体的类型
- CANNON.Body.KINEMATIC: 物体是动态的,可以移动和旋转。
- CANNON.Body.STATIC: 物体是静态的,不能移动和旋转。
- CANNON.Body.DYNAMIC: 物体是动态的,可以移动和旋转,并且受到物理世界的重力和其他力的作用。
js
const groundBody = new CANNON.Body({
shape: groundShape,
position: new CANNON.Vec3(0, 0, 0),
type: CANNON.Body.STATIC, // 设置物体类型为静态
});
示例
js
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
//引入CannonJS
import * as CANNON from "cannon-es";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
camera.position.y = 5;
camera.position.x = -10;
// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建物理世界
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
// 创建一个球体
const sphereShape = new CANNON.Sphere(1);
const sphereBody = new CANNON.Body({
mass: 1,
shape: sphereShape,
position: new CANNON.Vec3(0, 5, 0),
});
world.addBody(sphereBody);
// 创建一个平面
const groundShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5));
const groundBody = new CANNON.Body({
shape: groundShape,
position: new CANNON.Vec3(0, 0, 0),
type: CANNON.Body.STATIC,
});
groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1);
world.addBody(groundBody);
//创建一个球体的几何体
const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32);
//创建一个球体的材质
const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
//创建一个球体的网格
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
//将球体添加到场景中
scene.add(sphere);
//创建一个平面的几何体
const groundGeometry = new THREE.BoxGeometry(10, 0.2, 10);
//创建一个平面的材质
const groundMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
//创建一个平面的网格
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
//设置平面的位置
ground.position.x = 0.1;
//将平面添加到场景中
scene.add(ground);
//创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 启用阻尼效果
controls.enableDamping = true;
function animate() {
requestAnimationFrame(animate);
world.step(1 / 60);
sphere.position.copy(sphereBody.position);
sphere.quaternion.copy(sphereBody.quaternion);
renderer.render(scene, camera);
}
animate();
//监听窗口大小变化
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});