05-碰撞与碰撞组

Published 2025-07-24 00:24 719 words 4 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-页面生命周期
This post is not yet available in English. Showing the original.
05碰撞与碰撞组 作者: 前端AC | 原文: https://juejin.cn/post/7530141922515795978 碰撞与碰撞组 一、核心知识点讲解 在 Cannon.js 中,我们可以使用 碰撞过滤 来控制哪些物体可以彼此发生碰撞。主要依赖以下两个属性: | 属性 ...

05-碰撞与碰撞组

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

碰撞与碰撞组


一、核心知识点讲解

Cannon.js 中,我们可以使用 碰撞过滤 来控制哪些物体可以彼此发生碰撞。主要依赖以下两个属性:

属性类型说明
collisionFilterGroupNumber表示刚体所属的组(可以多个组按位或组合)
collisionFilterMaskNumber表示刚体可以与哪些组发生碰撞(按位与判断)

类似 bitmask 位运算控制。比如:

  • A 属于组 1,只想和组 2 碰撞,则:
    • A.collisionFilterGroup = 1
    • A.collisionFilterMask = 2

二、示例:三个方块,控制不同的碰撞组

  • 地面:属于组 1,允许与组 2 和组 4 碰撞
  • 红色方块:属于组 2,只碰地面
  • 绿色方块:属于组 4,只碰地面
  • 蓝色方块:属于组 8,不碰任何人

示例代码

<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, 8, 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

  // ------------------- 光照 -------------------
  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 groundShape = new CANNON.Plane()
  const groundBody = new CANNON.Body({
    mass: 0,
    shape: groundShape,
    collisionFilterGroup: 0b0001, // 属于组 1
    collisionFilterMask: 0b1111   // 可与所有组碰撞
  })
  groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
  world.addBody(groundBody)

  const groundGeo = new THREE.PlaneGeometry(20, 20)
  const groundMat = new THREE.MeshStandardMaterial({ color: 0x888888, side: THREE.DoubleSide })
  const groundMesh = new THREE.Mesh(groundGeo, groundMat)
  groundMesh.rotation.x = -Math.PI / 2
  scene.add(groundMesh)

  // ------------------- 创建球体函数 -------------------
  function createBall(color, x, group, mask) {
    const radius = 0.5

    const geometry = new THREE.SphereGeometry(radius, 32, 32)
    const material = new THREE.MeshStandardMaterial({ color })
    const mesh = new THREE.Mesh(geometry, material)
    mesh.position.set(x, 5, 0)
    scene.add(mesh)

    const shape = new CANNON.Sphere(radius)
    const body = new CANNON.Body({
      mass: 1,
      shape,
      position: new CANNON.Vec3(x, 5, 0),
      collisionFilterGroup: group,
      collisionFilterMask: mask
    })
    world.addBody(body)

    return { mesh, body }
  }

  // ------------------- 创建三个球体 -------------------
  const redBall   = createBall(0xff0000, -4, 0b0010, 0b0001) // 红球:只与地面碰撞
  const greenBall = createBall(0x00ff00,  0, 0b0100, 0b1111) // 绿球:与所有碰撞
  const blueBall  = createBall(0x0000ff,  4, 0b1000, 0b0100) // 蓝球:只与绿球碰撞

  const balls = [redBall, greenBall, blueBall] // 避免 undefined

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

  function animate() {
    requestAnimationFrame(animate)

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

    for (const { mesh, body } of balls) {
      if (mesh && body) {
        mesh.position.copy(body.position)
        mesh.quaternion.copy(body.quaternion)
      }
    }

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

  animate()
})
</script>

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

三、效果说明

  • 红色方块和绿色方块都能落在地面上(因为它们的组和掩码与地面对上了)
  • 蓝色方块直接穿透地面(它没有任何碰撞掩码匹配地面)

四、小贴士

  • 碰撞组默认是 1,所有物体都在组 1
  • 掩码为 -1 时表示与所有组碰撞
  • 可用 1 << N 来表示不同的位(如 1 << 3 = 8