Threejs:我想找个老伴儿.....Cannonjs:哈咯~

大家都知道我们可以使用Threejs提供的几何体,材质,灯光,轨道等元素构建出很棒的3D世界,但是有些场景单独使用Threejs就很难实现,这个时候就需要借助其他库一起组合使用,比如Cannonjs,本篇文章就一起来认识学习下加入Cannonjs的3D世界会有什么不一样

Cannonjs是啥

用一句话概括Cannonjs就是一个轻量级的3D物理引擎,可以与Threejs结合来实现一些比如弹跳,碰撞,重力等效果,在Cannonjs官网中可以看到很多用Cannonjs实现的例子

随便点进去一个就会出现该例子的效果图,看着这些效果有没有一点心动呢?想不想亲自尝试下,那么现在就一起来使用Cannonjs来搭建一个简单的3D物理世界

导入Cannonjs

安装导入Cannonjs的方式如下

js 复制代码
npm install cannon --save
js 复制代码
import * as CANNON from "cannon-es"

创建一个正在下落的球

一切准备工作就绪,先来第一步,创建一个正在下落的球,如果只是一个简单的球,每个人都会,但是如果要让这个球处于下落的过程,就需要让它像在真实的世界一样受重力作用,Cannon就提供了这样一个世界

js 复制代码
const world = new CANNON.World()
world.gravity.set(0,-9.82,0)

这两行代码就表示现在就有一个重力加速度为9.82的世界,有了世界,然后给这个世界添加第一个物体,来创建一个球

js 复制代码
const radius = 5;
const ballMaterial = new CANNON.Material()
const sphereBody = new CANNON.Body({
    mass: 10,
    shape: new CANNON.Sphere(radius),
    position: new CANNON.Vec3(0, 30, 0),
    material:ballMaterial
});
world.addBody(sphereBody);

给世界创建物体的时候,会使用Cannon.Body来创造,其中所使用到的参数意义如下

  • mass:物体的质量,只有当物体的质量大于0的时候,物体才会受重力的影响,一个没有质量的物体是会处于静止状态的,这是个简单的物理知识
  • shape:物体的形状,基本我们在Threejs中认识到的几何体都可以被创建出来
  • position:物体所处于的位置,如代码中所示,目前处理x,z都为0,但y坐标为30的位置
  • material:物体的材质

现在有了物体,也必须要让这个物体可视化,可视化的方式就是要用到Threejs,让它也创建出一个球,并且给予它对应的颜色与材质

js 复制代码
const sphereGeometry = new THREE.SphereGeometry(radius);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0x69eeff,metalness:0.8,roughness:0.2 });
const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphereMesh);

代码到了这里,一个完整的球体已经可以在世界中看到了

但是目前这个球体还只是静止的,虽然它现在的确是受到所处世界的重力作用,但我们也要去更新这个世界才能看到它受重力后的效果,更新这个世界就要用到step函数

js 复制代码
const update = () => {
    requestAnimationFrame(update);
    world.step(1/60);
    sphereMesh.position.copy(sphereBody.position);
    sphereMesh.quaternion.copy(sphereBody.quaternion);
    render.render(scene, camera);
};
update();

我们在update函数中调用了step函数,表示每次刷新的时候就去更新世界,做前端的都知道屏幕刷新的频率基本都为60HZ,因此传入1/60才能保证物理世界更新与屏幕渲染保持同步,但是我们光更新世界还不够,还必须要让更新后的物体同步到可视化物体上,所以这里还将物体的位置以及旋转角度复制给了可视化物体,这样才能保证让物体动起来

现在我们已经创建出来了一个正在自由落体的球了,接下来看看如何模拟球落地后弹起的效果

弹跳

先来创建一个地面,同样先在物理世界出添加一个作为地面的物体

js 复制代码
const planeMaterial = new CANNON.Material()
const planeBody = new CANNON.Body({
    mass:0,
    shape:new CANNON.Plane(),
    material:planeMaterial
})
planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
world.addBody(planeBody)

地面的shape对应几何体就是Threejs中的PLaneGeometry,因此将shape设置为Cannon.Plane,由于地面不需要让它动,所以质量mass就设置为0,再新建一个材质planeMaterial给到material属性,另外还调整了一下planeBody的方向,让它能够水平朝上,这样球才能弹到平面上,现在物体有了,也要创建一个与它对应的可视化物体

js 复制代码
const groundGeometry = new THREE.PlaneGeometry(100, 100);
const groundMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff, side: THREE.DoubleSide,metalness:0.8,roughness:0.2 });
const groundMesh = new THREE.Mesh(groundGeometry, groundMaterial);
groundMesh.rotation.x = -Math.PI / 2; 
scene.add(groundMesh);

现在有了球跟平面,来看下当球在下落过程中遇到平面会怎么样

球在下落过程中遇到平面后就跟真实的物理世界一样停止在了平面上,现在让我们让它变得更加真实一些,让它可以在平面上弹起来,需要让物体碰撞后弹起来就需要用到碰撞材质ContactMaterial,在这个世界中加入它才能让物体弹起来,可以看到ContactMaterial的构造函数中有这么几个参数

前两个就是需要碰撞的物体材质,在可选参数中,列出来了这么几个属性

  • friction:摩擦系数,通常表示物体滑动时候的阻力
  • restitution:弹性系数,表示物体碰撞反弹后的能量损失,数值越小,损失的能量越多
  • contactEquationStiffness:接触刚度,数值越大,物体越难穿透
  • contactEquationRelaxation:接触松弛系数,数值越大,碰撞反应越慢
  • frictionEquationStiffness:摩擦刚度,与contactEquationStiffness类似,针对摩擦
  • frictionEquationRelaxation:摩擦松弛系数,与contactEquationRelaxation类似,针对摩擦

了解了这些属性,我们给这个世界添加一个弹性系数为0.6的碰撞材质,这样球在接触到地面后就会弹起一段距离

js 复制代码
const contactMaterial = new CANNON.ContactMaterial(ballMaterial,planeMaterial,{restitution: 0.6})
world.addContactMaterial(contactMaterial) 

而如果我们将弹性系数降低,那么球弹起的距离也会减少,这里将restitution降低到0.1再看看效果

js 复制代码
const contactMaterial = new CANNON.ContactMaterial(ballMaterial,planeMaterial,{restitution: 0.1})
world.addContactMaterial(contactMaterial) 

restitution降低到0.1的效果就跟前面没有添加碰撞材质一样,区别就是这里消耗了不少弹性能量

阻尼系数

linearDamping相当于是阻力一样,之前我们依靠弹性系数来决定球体与地面碰撞后的能量损失,而linearDamping则是球体在运动过程中的能量损失,类似于空气阻力一样,举个例子,正常情况下当restitution设置为0.9的时候,球体弹起的高度是这样的

基本没有什么能量损耗,第一次反弹回的高度基本就跟初始位置一样,而当在这种场景下加入阻尼系数,效果就不一样了

js 复制代码
sphereBody.linearDamping = 0.5

这里给球体加上了0.5的阻尼系数,最终得到的效果如下

球在整体运动中就像是受到一股外力,无论是下落的速度还是弹起的距离,都比之前少了许多

碰撞监听

如果我们需要监听球与平面碰撞的动作,就需要在某个物体上添加上监听器,比如我们在球上加上碰撞监听

js 复制代码
sphereBody.addEventListener("collide", (event:any) => {
    const collidedBody = event.body;
    if (collidedBody === planeBody) {
      sphereMesh.material.color.set(0xffffff*Math.random());
    } 
});

监听了collide动作,并且在回调事件中改变球体的颜色,颜色以随机色来表示

总结

本篇文章主要介绍了

  1. 认识Cannonjs并且如何导入这个库
  2. 如何实现一个自由落体的球
  3. 如何实现球与地面的碰撞以及反弹
  4. 如果监听碰撞事件

感谢支持~下篇文章见~

相关推荐
OpenTiny社区5 分钟前
把 SearchBox 塞进项目,搜索转化率怒涨 400%?
前端·vue.js·github
编程猪猪侠35 分钟前
Tailwind CSS 自定义工具类与主题配置指南
前端·css
qhd吴飞39 分钟前
mybatis 差异更新法
java·前端·mybatis
YGY Webgis糕手之路1 小时前
OpenLayers 快速入门(九)Extent 介绍
前端·经验分享·笔记·vue·web
患得患失9491 小时前
【前端】【vueDevTools】使用 vueDevTools 插件并修改默认打开编辑器
前端·编辑器
ReturnTrue8681 小时前
Vue路由状态持久化方案,优雅实现记住表单历史搜索记录!
前端·vue.js
UncleKyrie1 小时前
一个浏览器插件帮你查看Figma设计稿代码图片和转码
前端
遂心_1 小时前
深入解析前后端分离中的 /api 设计:从路由到代理的完整指南
前端·javascript·api
你听得到111 小时前
Flutter - 手搓一个日历组件,集成单日选择、日期范围选择、国际化、农历和节气显示
前端·flutter·架构
风清云淡_A1 小时前
【REACT18.x】CRA+TS+ANTD5.X封装自定义的hooks复用业务功能
前端·react.js