03 - Cannon 材质与摩擦系数设置

公開日: 2025-07-24 00:20 750文字 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-页面生命周期
この投稿は「日本語」では表示できません。元の投稿を表示しています。
03 Cannon 材质与摩擦系数设置 作者: 前端AC | 原文: https://juejin.cn/post/7530105407846563890 03 Cannon 材质与摩擦系数设置 本节目标 理解什么是物理材质(Material) 学会设置摩擦系数 frictio...

03 - Cannon 材质与摩擦系数设置

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

03 - Cannon 材质与摩擦系数设置

本节目标

  • 理解什么是物理材质(Material)
  • 学会设置摩擦系数 friction
  • 使用 ContactMaterial 控制材质间交互
  • 观察摩擦力对运动的影响

材质与摩擦介绍

在 Cannon.js 中:

  • Material 是一个刚体的物理属性载体,包括摩擦、弹性等。
  • 只有在 ContactMaterial 中组合两个 Material 才能生效。
  • ContactMaterial 可定义:
    • friction 摩擦系数
    • restitution 弹性系数(可反弹)

举个例子:

const wood = new CANNON.Material('wood')
const ice = new CANNON.Material('ice')

const contactMat = new CANNON.ContactMaterial(wood, ice, {
  friction: 0.05,
  restitution: 0.1
})
world.addContactMaterial(contactMat)

示例:三个立方体摩擦不同的滑动对比

我们创建一个倾斜的平面,然后放上三个摩擦系数不同的立方体,观察它们滑动速度的不同。


完整 Vue3 示例(使用 cannon-es)

<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

  const world = new CANNON.World()
  world.gravity.set(0, -9.82, 0)

  // 创建基础材质
  const groundMaterial = new CANNON.Material('ground')
  const matLow = new CANNON.Material('low')
  const matMid = new CANNON.Material('mid')
  const matHigh = new CANNON.Material('high')

  // 设置接触材质(摩擦系数不同)
  world.addContactMaterial(new CANNON.ContactMaterial(groundMaterial, matLow, { friction: 0.0 }))
  world.addContactMaterial(new CANNON.ContactMaterial(groundMaterial, matMid, { friction: 0.3 }))
  world.addContactMaterial(new CANNON.ContactMaterial(groundMaterial, matHigh, { friction: 1.0 }))

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

  const groundBody = new CANNON.Body({
    mass: 0,
    shape: new CANNON.Box(new CANNON.Vec3(10, 0.5, 5)),
    position: new CANNON.Vec3(0, -0.5, 0),
    material: groundMaterial
  })
  groundBody.quaternion.setFromEuler(0, 0, -0.3)
  world.addBody(groundBody)

  // 立方体基础设置
  const cubeGeo = new THREE.BoxGeometry(1, 1, 1)
  const cubeShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))

  const cubes = []
  const bodies = []

  const settings = [
    { x: -4, color: 0xff0000, material: matLow },
    { x: 0, color: 0xffff00, material: matMid },
    { x: 4, color: 0x00ff00, material: matHigh }
  ]

  for (let i = 0; i < 3; i++) {
    const { x, color, material } = settings[i]

    const mesh = new THREE.Mesh(
      cubeGeo,
      new THREE.MeshStandardMaterial({ color })
    )
    mesh.position.set(x, 2, 0)
    scene.add(mesh)

    const body = new CANNON.Body({
      mass: 1,
      shape: cubeShape,
      position: new CANNON.Vec3(x, 2, 0),
      material
    })
    world.addBody(body)

    cubes.push(mesh)
    bodies.push(body)
  }

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

  const clock = new THREE.Clock()
  const timeStep = 1 / 60

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

    for (let i = 0; i < cubes.length; i++) {
      cubes[i].position.copy(bodies[i].position)
      cubes[i].quaternion.copy(bodies[i].quaternion)
    }

    groundMesh.quaternion.copy(groundBody.quaternion)

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

  animate()
})
</script>

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

2025-07-23T12_42_22.550Z-557681.gif
2025-07-23T12_42_22.550Z-557681.gif

观察结果

  • 绿色立方体:friction = 0,滑得最远
  • 黄色立方体:friction = 0.3,滑行距离适中
  • 红色立方体:friction = 1.0,几乎立即停止

这就是摩擦系数对运动的控制作用。


小结

  • 使用 Material 可以控制物体的物理属性
  • 必须用 ContactMaterial 来定义两个材质之间的行为
  • friction 越大,滑动越慢甚至停止
  • 可以使用多组材质,设置多种不同摩擦情况
  • 倾斜地面是检验摩擦的最佳手段之一