07-监听碰撞事件和获取碰撞信息

28 阅读2分钟

监听碰撞事件和获取碰撞信息

在物理仿真中,了解刚体之间的碰撞信息是非常关键的。Cannon.js 提供了 collide 事件,让我们能够在碰撞发生时捕获相关信息,比如碰撞对象、冲击速度等。


一、监听碰撞事件

body.addEventListener('collide', (event) => {
  console.log('发生碰撞!')
  console.log('碰撞到的刚体:', event.body)
  console.log('碰撞强度:', event.contact.getImpactVelocityAlongNormal())
})
字段含义
event.body与当前刚体发生碰撞的刚体
event.contact碰撞联系信息
getImpactVelocityAlongNormal()获取法向方向上的冲击速度(衡量碰撞强度)

二、完整示例

球体从空中落下与地面发生碰撞,并在控制台打印碰撞信息:

<!-- App.vue -->
<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(() => {
  // 创建 Three.js 场景与相机
  const scene = new THREE.Scene()
  const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100)
  camera.position.set(0, 5, 10)

  // 渲染器和轨道控制器
  const renderer = new THREE.WebGLRenderer({ canvas: canvasRef.value })
  renderer.setSize(window.innerWidth, window.innerHeight)
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true

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

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

  // 创建地面刚体
  const groundBody = new CANNON.Body({
    mass: 0,
    shape: new CANNON.Plane()
  })
  groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
  world.addBody(groundBody)

  // 创建地面网格
  const groundGeo = new THREE.PlaneGeometry(10, 10)
  const groundMat = new THREE.MeshStandardMaterial({ color: 0x999999, side: THREE.DoubleSide })
  const groundMesh = new THREE.Mesh(groundGeo, groundMat)
  groundMesh.rotation.x = -Math.PI / 2
  scene.add(groundMesh)

  // 创建小球
  const radius = 0.5
  const sphereGeo = new THREE.SphereGeometry(radius, 32, 32)
  const sphereMat = new THREE.MeshStandardMaterial({ color: 0x0077ff })
  const sphereMesh = new THREE.Mesh(sphereGeo, sphereMat)
  sphereMesh.position.set(0, 5, 0)
  scene.add(sphereMesh)

  const sphereBody = new CANNON.Body({
    mass: 1,
    shape: new CANNON.Sphere(radius),
    position: new CANNON.Vec3(0, 5, 0)
  })
  world.addBody(sphereBody)

  // 🧩 碰撞事件监听
  sphereBody.addEventListener('collide', (event) => {
    console.log('🎯 碰撞发生!')
    console.log('与谁发生碰撞:', event.body)
    console.log('冲击速度:', event.contact.getImpactVelocityAlongNormal().toFixed(2))
  })

  // 动画更新
  const clock = new THREE.Clock()
  const timeStep = 1 / 60

  function animate() {
    requestAnimationFrame(animate)
    const delta = clock.getDelta()
    world.step(timeStep, delta)

    sphereMesh.position.copy(sphereBody.position)
    sphereMesh.quaternion.copy(sphereBody.quaternion)

    controls.update()
    renderer.render(scene, camera)
  }

  animate()
})
</script>

<template>
  <canvas ref="canvasRef"></canvas>
</template>

2025-07-23T13_47_28.405Z-885294.gif

三、碰撞信息可以做什么?

用途描述
播放音效根据碰撞强度播放不同音效
触发逻辑如“命中目标”后触发销毁或得分
记录日志用于物理调试或分析仿真结果
可视化反应冲击越强,颜色变化越剧烈

小技巧

  • 若想检测多个刚体的碰撞,分别为它们绑定监听器。
  • 想获取更详细的碰撞点信息,可查看 event.contact.rievent.contact.rj(相对接触点)。