当前位置: 首页 > article >正文

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();

💡 总结

功能

代码示例

创建物理世界

const world = new CANNON.World();

设置重力

world.gravity.set(0, -9.81, 0);

创建刚体

new CANNON.Body({ mass, shape })

添加刚体到世界

world.addBody(body);

创建地面

new CANNON.Plane()

同步到 Three.js

mesh.position.copy(body.position);

添加碰撞事件

body.addEventListener('collide', callback);

🚀 Cannon.js 适用于刚体模拟,比如小球弹跳、物体掉落等,适合游戏和交互应用!


http://www.kler.cn/a/596481.html

相关文章:

  • Android10 系统截屏功能异常的处理
  • python每日十题(5)
  • HTML超链接
  • 网络层之IP协议
  • 网络HTTPS协议
  • 从碎片化到标准化:案例详解 MCP 如何重塑 AI Agent 开发生态?
  • WEBUI插件和UE5通讯
  • 爬虫——playwright获取亚马逊数据
  • 2025年3月AI搜索发展动态与趋势分析:从技术革新到生态重构
  • 清洁机器人垃圾物识别与智能分类回收系统研究(大纲)
  • 【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 中的 RESTful API 设计:从上手到骨折
  • MyBatis 中 #{} 和 ${} 的区别详解
  • [思考记录]两则:宏观视角、理想化
  • 高等数学-第七版-上册 选做记录 习题5-2
  • 手机怎么换网络IP有什么用?操作指南与场景应用‌
  • MySQL高频八股——索引
  • Cables Finance 即将发布新的积分奖励计划及代币空投
  • 项目日记 -云备份 -服务器配置信息模块
  • Kafka consumer_offsets 主题深度剖析
  • SSE详解面试常考问题详解