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): voidstep
按照设置时间,计算物理世界
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
刚体类型,有如下类型
- DYNAMIC = 1: 受到物理力学影响,可以改变运动,可参与交互
- KINEMATIC = 4:不受物理学影响,可以改变运动,可参与交互
- 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时,即为禁止旋转休眠
休眠的条件
- 世界允许休眠:
world.allowSleep = true; - 刚体允许休眠:
body.allowSleep = true;
body.sleepSpeedLimit = 1.0; // 设置多少速度内休眠
body.sleepState; // 当前休眠状态
body.sleep(); // 主动休眠body.wakeup(); // 唤醒休眠事件
- sleepy ——> sleepyEvent
- sleep ——> sleepEvent
- 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): CylinderConvexPolyhedron
凸多面体
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;
- 刚体类型可碰撞:
body.type = CANNON.Body.KINEMATIC - 设置碰撞组:
body.collisionFilterGroup = G1; - 设置可以和哪一组碰撞:
body.collisionFilterMask = G2 | G3;
监听碰撞事件
body.addEventListener("collide", (ev: ContactEquation) => {});如何忽略碰撞
- 静态物体或刚体质量为 0
- 设置不可碰撞组
- 启用休眠,
body.sleep()
Ray
创建一条射线,且可以获取和场景内物体的交点
const ray = new CANNON.Ray(startPoint, direction);
// ray.intersectBody// ray.intersectBodies// ray.intersectWorldray.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; // 从射线起点到碰撞点的距离,未碰撞则返回-1raycastResult.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);}