大家都知道我们可以使用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
动作,并且在回调事件中改变球体的颜色,颜色以随机色来表示
总结
本篇文章主要介绍了
- 认识Cannonjs并且如何导入这个库
- 如何实现一个自由落体的球
- 如何实现球与地面的碰撞以及反弹
- 如果监听碰撞事件
感谢支持~下篇文章见~