今天郭先生说的是一个物理引擎,它十分小巧并且操作简单,没错他就是cannon.js。这些优点都源自于他是基于js编写的,对于js使用者来说cannon.js拥有其他物理引擎没有的纯粹性。从学习成本来看,cannon.js的学习成本比较低,对于新手来说比较友好,因为它有相对完善的api,学习cannon.js之前我们不妨来看看cannon.js的官方网站以及他的API,对于js学习者来说这是十分必要的。官网上面有一些example,他们十分典型并囊括了大多数的知识点,配合api一起学习是个不错的选择。在线案例请点击博客原文。效果如下图,接下来以一个小案例,简单的介绍一下cannon.js。
1. 初始化three场景
创建three场景(或者说three世界)来作为物理世界的载体,这一步很简单,主要就是添加渲染器、场景、相机和网格等three元素,没必要多说。
scene = new THREE.Scene();//step 1 创建场景 camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 ); camera.position.y = 30; camera.position.z = 20; camera.lookAt(0,5,0); scene.add( camera ); //step 2 场景中添加相机 scene.add(new THREE.AmbientLight(0x888888)); const light = new THREE.DirectionalLight(0xbbbbbb, 1); light.position.set(6, 30, 6); scene.add(light); //step 3 场景中添加另种光源 renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.shadowMap.enabled = true; renderer.setClearColor(0xbfd1e5); this.$refs.box.appendChild(renderer.domElement); //step 4 dom中添加渲染器 let groundGeom = new THREE.BoxBufferGeometry(40, 0.2, 40); let groundMate = new THREE.MeshPhongMaterial({color: 0xdddddd, map: texture}) ground = new THREE.Mesh(groundGeom, groundMate); ground.position.y = -0.1; ground.receiveShadow = true; scene.add(ground); //step 5 添加地面网格
2. 初始化物理世界
这里是初始化物理世界,我们详细的讲一下他们的用法。
initCannon() { world = new CANNON.World(); //该方法初始化物理世界,里面包含着物理世界的相关数据(如刚体数据,世界中所受外力等等) world.gravity.set(0,-9.8,0); //设置物理世界的重力为沿y轴向上-9.8米每二次方秒 world.broadphase = new CANNON.NaiveBroadphase();//NaiveBroadphase是默认的碰撞检测方式,该碰撞检测速度比较高 world.solver.iterations = 5;//解算器的迭代次数,更高的迭代次数意味着更加精确同时性能将会降低 bodyGround = new CANNON.Body({ //创建一个刚体(物理世界的刚体数据) mass: 0, //刚体的质量,这里单位为kg position: new CANNON.Vec3(0, -0.1, 0), //刚体的位置,单位是米 shape: new CANNON.Box(new CANNON.Vec3(20, 0.1, 20)), //刚体的形状(这里是立方体,立方体的参数是一个包含半长、半宽、半高的三维向量,具体我们以后会说) material: new CANNON.Material({friction: 0.05, restitution: 0}) //材质数据,里面规定了摩擦系数和弹性系数 }); ground.userData = bodyGround; //将刚体的数据赋值给地面网格的userData属性 world.addBody(bodyGround); //物理世界添加地面刚体 },
3. 向场景中添加网格并向物理世界中添加刚体数据
这里我们通过setInterval函数我们定时向场景中添加网格并向物理世界中添加刚体数据,
interval = setInterval(() => { this.createBox(); //创建网格和刚体的方法 }, 200);
下面是具体的方法
createBox() { let x = Math.random() * 10 - 5; let z = Math.random() * 10 - 5; let box = new THREE.Mesh( geometry, this.createRandomMaterial() ); //createRandomMaterial创建随机颜色的材质 box.position.set(x, 20, z); scene.add( box ); //创建box,并添加到场景 let bodyBox = new CANNON.Body({ mass: 1, position: new CANNON.Vec3(x, 20, z), shape: new CANNON.Box(new CANNON.Vec3(1,1,1)), material: new CANNON.Material({friction: 0.1, restitution: 0}) });//创建一个质量为1kg,位置为(x,20,z),形状为halfSize为1,1,1的正方形刚体,材质中摩擦系数为0.1,弹性系数为0。 box.userData = bodyBox;//给box的userData属性添加刚体数据 world.addBody(bodyBox);//在物理世界中添加该刚体 setTimeout(() => { //10秒钟之后在场景中移除box,并在物理世界中移除该刚体 scene.remove(box); box.material.dispose(); box.geometry.dispose(); world.removeBody(bodyBox); }, 10000) },
4. 根据物理引擎的数据更新three网格数据
这一步我们逐帧根据物理引擎的数据渲染three场景
animation() { //requestAnimationFrame动画中调用render方法 this.globalID = requestAnimationFrame(this.animation); this.render(); }, render() { //更新性能插件,根据物理引擎数据更新网格数据,最后渲染场景 stats.update(); this.updatePhysics(); renderer.render( scene, camera ); }, updatePhysics() { // world.step world.step(timeStep); //第一个参数是以固定步长更新物理世界参数(详情请看api) scene.children.forEach(d => {//遍历场景中的子对象,如果对象的isMesh属性为true,我们就将更新改对象的position和quaternion属性(他们对应的刚体数据存在对应的userData中)。 if(d.isMesh == true) { d.position.copy(d.userData.position); d.quaternion.copy(d.userData.quaternion); } }) }
这样我们就将cannon.js物理引擎应用到了three中。不出意外的话,接下来我会讲解一下官方的examples。
转载请注明地址:郭先生的博客