Skip to content

API

World

gravity

重力设置

World.gravity: Vec3;
const world = new CANNON.World();
world.gravity.set(0, -9.8, 0);

addBody

添加刚体

World.addBody(body: Body): void
world.addBody(body);

addConstraint

添加约束,例如 PointToPointConstraint(点对点约束)

World.addConstraint(c: Constraint): void

step

按照设置时间,计算物理世界

World.step(dt: number, timeSinceLastCalled?: number, maxSubSteps?: number): void
function animate() {
world.step(1 / 60);
requestAnimationFrame(animate);
}

Body

mass

质量

World.mass: number
world.mass = 10;

position

位置

World.position: Vec3;
World.position = new CANNON.Vec3(0, 2, -5), // 刚体位置

type

刚体类型,有如下类型

  1. DYNAMIC = 1: 受到物理力学影响,可以改变运动,可参与交互
  2. KINEMATIC = 4:不受物理学影响,可以改变运动,可参与交互
  3. STATIC = 2: 不受物理学影响,不可以改变运动,可参与交互

当创建 Body 时,如果未指定 type,则如果刚体的质量大于 0,则默认为 DYNAMIC,否则为 STATIC

World.type: BodyType;

shape

刚体形状

World.shape: Shape;

velocity

设置初始速度

body.velocity = new CANNON.Vec3(0, 10, 0);

angularVelocity

角速率

body.angularVelocity.set(0, 0, 0); // 设置刚体的角速率,设置为0时,即为禁止旋转

休眠

休眠的条件

  1. 世界允许休眠:world.allowSleep = true;
  2. 刚体允许休眠:body.allowSleep = true;
body.sleepSpeedLimit = 1.0; // 设置多少速度内休眠
body.sleepState; // 当前休眠状态
body.sleep(); // 主动休眠
body.wakeup(); // 唤醒

休眠事件

  1. sleepy ——> sleepyEvent
  2. sleep ——> sleepEvent
  3. wakeup ——> wakeupEvent

施加力

body.applyForce(); // 施加力的时候,如果不设置施加点,则视为重心施加
body.applyLocalForce();

施加脉冲

在一帧内施加力

body.applyImpulse();
body.applyLocalImpulse();

施加扭矩

body.applyTorque();
body.torque = new CANNON.Vec3(0, 10, 0);

Shape

Box

盒子(长方体),参数中的长宽高均为实际的一半

new CANNON.Box(halfExtents: Vec3);

Sphere

球形

const sphere = new CANNON.Sphere(1);

Plane

平面,可认为平面无限长

new CANNON.Plane();

Cylinder

圆柱

new Cylinder(radiusTop?: number, radiusBottom?: number, height?: number, numSegments?: number): Cylinder

ConvexPolyhedron

凸多面体

Particle

粒子

Heightfield

高度场

Trimesh

复杂模型

Material

设置材质后,就可以计算作用力。例如摩擦力,弹性

const physicalSphereMat = new CANNON.Material("sphereMat");
physicalSphereMat.friction = 1; // 摩擦系数
physicalSphereMat.restitution = 0.2; // 弹性系数
// 给刚体增加材质,可以计算摩擦和弹性
this.physicalSphereBody.material = physicalSphereMat;
// 设置阻尼
this.physicalSphereBody.linearDamping = 0.1;

ContactMaterial

定义接触材质(2 个材质在接触时候的材质)

const contactMaterial new CANNON.ContactMaterial(
mat1,
mat2,
{
}
);

RaycastVehicle

射线投射车辆,可组成汽车

// 车身刚体
const chassisBody = new CANNON.Body({
mass: 10,
shape: new CANNON.Box(new CANNON.Vec3(1, 0.5, 2)),
position: new CANNON.Vec3(0, 2, -5),
});
// 整车
const vehicle = new CANNON.RaycastVehicle({
chassisBody: chassisBody, // 底盘刚体
// 0表示X,1表示Y,2表示Z
// indexRightAxis: 2,
// indexUpAxis: 1,
// indexForwardAxis: 0,
});
// 轮子参数
const wheelOptions = {
radius: 0.8, // 车轮半径
directionLocal: new CANNON.Vec3(0, -1, 0), // 车轮向下方向
axleLocal: new CANNON.Vec3(0, 0, 1), // 车轴方向(轴心)
chassisConnectionPointLocal: new CANNON.Vec3(1, 1, 0),
suspensionStiffness: 30,
suspensionRestLength: 1,
frictionSlip: 5,
dampingRelaxation: 2.3,
dampingCompression: 4.4,
maxSuspensionForce: 100000,
rollInfluence: 0.01,
maxSuspensionTravel: 0.3,
customSlidingRotationalSpeed: -30,
useCustomSlidingRotationalSpeed: true,
};
// 添加四个车轮
vehicle.addWheel({
...wheelOptions,
chassisConnectionPointLocal: new CANNON.Vec3(-1, 1, 1.5), // 左前轮
});
vehicle.addWheel({
...wheelOptions,
chassisConnectionPointLocal: new CANNON.Vec3(1, 1, 1.5), // 右前轮
});
vehicle.addWheel({
...wheelOptions,
chassisConnectionPointLocal: new CANNON.Vec3(-1, 1, -1.5), // 左后轮
});
vehicle.addWheel({
...wheelOptions,
chassisConnectionPointLocal: new CANNON.Vec3(1, 1, -1.5), // 右后轮
});
for (let i = 0; i < 4; i++) {
// 车轮形状
const cylinderShape = new CANNON.Cylinder(
wheelOptions.radius,
wheelOptions.radius,
wheelOptions.radius / 2,
20
);
// 车轮刚体
const wheelBody = new CANNON.Body({
type: CANNON.Body.KINEMATIC, // 防止车身和车轮相互计算
mass: 5,
});
// 旋转车轮和车身适配
const q = new CANNON.Quaternion();
q.setFromAxisAngle(new CANNON.Vec3(0, 0, 1), -Math.PI / 2);
const q1 = new CANNON.Quaternion();
q1.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), -Math.PI / 2);
const total = q1.clone();
total.mult(q, total);
total.normalize();
wheelBody.addShape(cylinderShape, new CANNON.Vec3(), total);
}
vehicle.addToWorld(world);

碰撞

碰撞需要具有如下 3 个条件

const G1 = 1, G2 = 2, G3 = 4;

  1. 刚体类型可碰撞:body.type = CANNON.Body.KINEMATIC
  2. 设置碰撞组: body.collisionFilterGroup = G1;
  3. 设置可以和哪一组碰撞: body.collisionFilterMask = G2 | G3;

监听碰撞事件

body.addEventListener("collide", (ev: ContactEquation) => {});

如何忽略碰撞

  1. 静态物体或刚体质量为 0
  2. 设置不可碰撞组
  3. 启用休眠,body.sleep()

Ray

创建一条射线,且可以获取和场景内物体的交点

const ray = new CANNON.Ray(startPoint, direction);
// ray.intersectBody
// ray.intersectBodies
// ray.intersectWorld
ray.intersectWorld(
world,
{
collisionFilterGroup: -1,
collisionFilterMask: -1,
},
function () {
if (rayResult.hasHit) {
console.log(
"相交点坐标:",
rayResult.point.x,
rayResult.point.y,
rayResult.point.z
);
console.log(
"相交点法线:",
rayResult.normal.x,
rayResult.normal.y,
rayResult.normal.z
);
console.log("相交物体:", rayResult.body);
console.log("相交距离:", rayResult.distance);
} else {
console.log("射线未与物体相交");
}
}
);

RaycastResult

raycastResult.distance; // 从射线起点到碰撞点的距离,未碰撞则返回-1
raycastResult.hasHit; // 是否碰撞
raycastResult.hitFaceIndex; //
raycastResult.hitNormalWorld; // 碰撞面的法线
raycastResult.hitPointWorld; // 碰撞点的世界坐标
raycastResult.rayFromWorld;
raycastResult.rayToWorld;
raycastResult.shape;
raycastResult.shouldStop;

CANNON 同步 THREE

function animate() {
requestAnimationFrame(animate);
// 更新物理世界
world.step(1 / 60);
// 计算车辆状态 vehicle.updateVehicle(1 / 60);
// 同步刚体的位置和旋转,chassisMesh 为 THREE 的 Mesh
chassisMesh.position.copy(chassisBody.position);
chassisMesh.quaternion.copy(chassisBody.quaternion);
// 渲染场景
renderer.render(scene, camera);
}