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

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

本节目标

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

材质与摩擦介绍

在 Cannon.js 中:

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

举个例子:

js 复制代码
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)

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(() => {
  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>

观察结果

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

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


小结

  • 使用 Material 可以控制物体的物理属性
  • 必须用 ContactMaterial 来定义两个材质之间的行为
  • friction 越大,滑动越慢甚至停止
  • 可以使用多组材质,设置多种不同摩擦情况
  • 倾斜地面是检验摩擦的最佳手段之一
相关推荐
ywf121530 分钟前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭37 分钟前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf7 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特7 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷7 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian8 小时前
前端node常用配置
前端
华洛8 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq8 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A9 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常9 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端