02 - Cannon 引擎基础碰撞讲解

发布于 2025-07-24 00:19 1074 字 6 min read

做产品前,先别急着写代码:我是怎么判断一个点子值不值得做的想偷卷?但微信不支持md文档?这个软件助你!新手小白如何开发练手项目?前端常见安全问题 + 防御方法 + 面试回答吉比特(雷霆游戏)前端二面问题总结吉比特27前端实习面试纷享销客前端实习一面08-刚体约束 Constraint07-监听碰撞事件和获取碰撞信息06 - 物体休眠与休眠事件详解(Cannon.js)05-碰撞与碰撞组04 - 弹性与接触材质详解03 - Cannon 材质与摩擦系数设置02 - Cannon 引擎基础碰撞讲解01 - Cannon.js 引擎的用途和基本使用一款减轻前端图片命名工作量的图片转换器总结:一个双非大学生的两年,是怎么过来的?数学公式Input-输入框组件实现详解用HTML5实现实时ASCII艺术摄像头object-fit: contain - CSS 属性详解GSAP (GreenSock Animation Platform) 动画库学习指南Webview通信系统学习指南实现手势操控3D粒子动画?太好玩了!东田数码科技前端面试前端性能优化面试回答技巧(一)React与Vue表单的对比差异融山科技前端面经·React的入门学习(一)九方前端面试JavaScript 核心:数组/字符串 API 精要指南JavaScript的常用数组API原理解析前端练习小网站-新手突破之路前端の骚操作代码合集 (二)| 让你的网页变得有趣前端Threejs入门(五)Vue 3 响应式原理:computed的模板解包机制Vue 监听器 watch 深度解析Axios 深入解析从0到1配置vue项目跟git仓库连接!前端Three.js入门(四)你懂keep-alive吗?见过Vue的小区保安吗?前端Three.js入门(三)前端Three.js入门(二)前端Three.js入门(一)-Three的第一个简单的页面 Git 大冒险:解锁代码管理的秘密武器如何将网站加载速度提升50%?前端性能优化全攻略前端の骚操作代码合集 | 让你的网页充满恶趣味进击蓝桥杯!2025web前端HTML5uniapp-页面生命周期
02 Cannon 引擎基础碰撞讲解 作者: 前端AC | 原文: https://juejin.cn/post/7530105395282952230 02 Cannon 引擎基础碰撞讲解 本节目标 理解 Cannon.js 中的碰撞原理 学习如何让两个刚体发生碰撞 配...

02 - Cannon 引擎基础碰撞讲解

作者: 前端AC | 原文: https://juejin.cn/post/7530105395282952230

02 - Cannon 引擎基础碰撞讲解

本节目标

  • 理解 Cannon.js 中的碰撞原理
  • 学习如何让两个刚体发生碰撞
  • 配置质量、形状、位置使碰撞真实生效
  • 用 Vue3 示例观察碰撞过程

什么是碰撞?

在 Cannon.js 中,碰撞(Collision) 是指两个或多个刚体接触后发生的物理响应。要实现碰撞:

  • 两个物体都必须有 shape
  • 至少一个物体必须有质量(mass > 0
  • 物体必须靠近、发生交错

碰撞最小实现逻辑

我们用一个最基本的例子说明:

  • 一个球从上往下掉
  • 底下是一个静止的平面(地板)
  • 当球体接触地板后停止

示例:球落地碰撞+掉落

<template>
  <canvas ref="canvasRef"></canvas>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import * as THREE from 'three'
import * as CANNON from 'cannon-es'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

const canvasRef = ref()

onMounted(() => {
  // 初始化场景
  const scene = new THREE.Scene()
  const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
  camera.position.set(0, 5, 15)

  const renderer = new THREE.WebGLRenderer({ canvas: canvasRef.value })
  renderer.setSize(window.innerWidth, window.innerHeight)

  // 添加轨道控制器
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true

  // 添加球体 Mesh
  const ballGeo = new THREE.SphereGeometry(1)
  const ballMat = new THREE.MeshStandardMaterial({ color: 0xff5555 })
  const ballMesh = new THREE.Mesh(ballGeo, ballMat)
  scene.add(ballMesh)

  // 添加地面 Mesh
  const groundGeo = new THREE.BoxGeometry(10, 1, 10)
  const groundMat = new THREE.MeshStandardMaterial({ color: 0x88cc88 })
  const groundMesh = new THREE.Mesh(groundGeo, groundMat)
  groundMesh.position.set(0, -0.5, 0)
  scene.add(groundMesh)

  // 添加灯光
  const light = new THREE.DirectionalLight(0xffffff, 1)
  light.position.set(10, 20, 10)
  scene.add(light)

  // 初始化物理世界
  const world = new CANNON.World()
  world.gravity.set(0, -9.82, 0)

  // 创建球体刚体
  const ballBody = new CANNON.Body({
    mass: 1, // 会受到重力影响
    shape: new CANNON.Sphere(1),
    position: new CANNON.Vec3(0, 5, 0)
  })
  world.addBody(ballBody)

  // 创建地面刚体(静止)
  const groundBody = new CANNON.Body({
    type: CANNON.Body.STATIC, // 不会动
    shape: new CANNON.Box(new CANNON.Vec3(5, 0.5, 5)),
    position: new CANNON.Vec3(0, -0.5, 0)
  })
  world.addBody(groundBody)

  // 动画循环
  const clock = new THREE.Clock()
  const timeStep = 1 / 60
  function animate() {
    requestAnimationFrame(animate)

    const delta = clock.getDelta()
    world.step(timeStep, delta)

    // 同步位置
    ballMesh.position.copy(ballBody.position)
    ballMesh.quaternion.copy(ballBody.quaternion)

    controls.update()
    renderer.render(scene, camera)
  }
  animate()
})
</script>

2025-07-23T12_14_56.568Z-506054.gif
2025-07-23T12_14_56.568Z-506054.gif
<script setup>
import { onMounted, ref } from 'vue'
import * as THREE from 'three'
import * as CANNON from 'cannon-es'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

const canvasRef = ref()

onMounted(() => {
  // 初始化场景
  const scene = new THREE.Scene()
  const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
  camera.position.set(0, 5, 15)

  const renderer = new THREE.WebGLRenderer({ canvas: canvasRef.value })
  renderer.setSize(window.innerWidth, window.innerHeight)

  // 添加轨道控制器
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true

  // 添加球体 Mesh
  const ballGeo = new THREE.SphereGeometry(1)
  const ballMat = new THREE.MeshStandardMaterial({ color: 0xff5555 })
  const ballMesh = new THREE.Mesh(ballGeo, ballMat)
  scene.add(ballMesh)

  // 添加倾斜地面 Mesh(略微倾斜)
  const groundGeo = new THREE.BoxGeometry(10, 1, 10)
  const groundMat = new THREE.MeshStandardMaterial({ color: 0x88cc88 })
  const groundMesh = new THREE.Mesh(groundGeo, groundMat)
  groundMesh.position.set(0, -0.5, 0)
  groundMesh.rotation.z = 0.1 // 倾斜 0.1 弧度
  scene.add(groundMesh)

  // 添加灯光
  const light = new THREE.DirectionalLight(0xffffff, 1)
  light.position.set(10, 20, 10)
  scene.add(light)

  // 初始化物理世界
  const world = new CANNON.World()
  world.gravity.set(0, -9.82, 0)

  // 创建球体刚体
  const ballBody = new CANNON.Body({
    mass: 1,
    shape: new CANNON.Sphere(1),
    position: new CANNON.Vec3(0, 5, 0)
  })
  world.addBody(ballBody)

  // 创建倾斜地面刚体
  const groundBody = new CANNON.Body({
    type: CANNON.Body.STATIC,
    shape: new CANNON.Box(new CANNON.Vec3(5, 0.5, 5)),
    position: new CANNON.Vec3(0, -0.5, 0)
  })
  groundBody.quaternion.setFromEuler(0, 0, 0.1) // Z 轴倾斜 0.1 弧度
  world.addBody(groundBody)

  // 动画循环
  const clock = new THREE.Clock()
  const timeStep = 1 / 60
  function animate() {
    requestAnimationFrame(animate)
    const delta = clock.getDelta()
    world.step(timeStep, delta)

    ballMesh.position.copy(ballBody.position)
    ballMesh.quaternion.copy(ballBody.quaternion)

    groundMesh.quaternion.copy(groundBody.quaternion)

    controls.update()
    renderer.render(scene, camera)
  }
  animate()
})
</script>

<template>
  <canvas ref="canvasRef"></canvas>
</template>
2025-07-23T12_14_12.043Z-860045.gif
2025-07-23T12_14_12.043Z-860045.gif

核心知识点说明

1. mass 决定是否受重力影响

  • mass = 0(或 type: STATIC) 表示不受重力、不会移动
  • mass > 0 表示刚体受力、能运动

2. shape 决定碰撞体积

  • CANNON.Sphere(radius) 创建球体
  • CANNON.Box(new Vec3(x,y,z)) 创建立方体(地面)

3. position 设置初始位置

  • 球体在上方 y=5
  • 地面在 y = -0.5,高度为1(底面居中)

4. step() 每帧推进物理计算

  • world.step() 计算物理
  • 然后把位置同步回 Three.js 的 Mesh 上

如何确认碰撞发生了?

在这个例子中,我们还没有使用事件监听,但你可以通过观察:

  • 球体从空中落下
  • 触碰地面后停止下落

下一节将介绍如何监听碰撞事件,获取更详细的碰撞信息。


小结

  • 碰撞需要物体有 shape,并至少一个有质量
  • STATIC 类型物体常用于地面、墙体等不可动物体
  • 每帧调用 step() 推进物理,并同步位置
  • 碰撞响应是 Cannon.js 的核心机制之一

下一节:监听碰撞事件与获取碰撞信息