github地址:https://github.com/hua1995116...大家好,我是秋风。继上一篇《Three.js系列: ? 游戏中的第一/三人称视角》今天要跟大家分享的是搭建一个海洋球池。你见过海洋球吗?是商场里很受小朋友欢迎的小球,看到就想跳进去的那种。我就是想做一个海洋球池,然后顺便带大家学习一下Three.js中的物理引擎。那么开始吧,要实现海洋球池,首先要有一个“球”。那么先带大家实现一个小球,在Three.js中定义一个小球非常简单。因为Three.js为我们提供了非常丰富的几何图形API,大概有十几种。提供的几何形状恰好有我们需要的球形,球形API称为SphereGeometry。SphereGeometry(radius:Float,widthSegments:Integer,heightSegments:Integer,phiStart:Float,phiLength:Float,thetaStart:Float,thetaLength:Float)这个API一共有7个参数,不过我们只需要用到前3个参数,后者暂时不需要管理。Radius的含义很简单。它是半径。说白了,就是设置球的大小。首先,我们将球的大小设置为0.5,然后是widthSegments和heightSegments。看起来更精致,但是精细的后果是性能消耗更大。widthSegments默认值为32,heightSegments默认值为16。我们可以设置20,20constsphereGeometry=newTHREE.SphereGeometry(0.5,20,20);这很简单。虽然球有形状,但我们必须为球设置材质。该材料类似于我们现实生活中的材料。它不仅仅是一个球形的东西,比如玻璃弹珠。、橡胶材质的网球等,不同的材质会反射不同的光线,看起来也不同。在Three.js中,我们设置了一个标准物理材质MeshStandardMaterial,可以设置金属度和粗糙度,反射光线,然后设置小球的颜色为红色,constsphereMaterial=newTHREE.MeshStandardMaterial({color:'#ff0000'});constmesh=newTHREE.Mesh(sphereGeometry,sphereMaterial);场景。添加(网格);然后我们把它添加到我们的场景中,emmm,它看起来像一个暗块。“上帝说要有光,于是就有了光”,黑暗是正常的,因为我们场景中没有光,意思很简单,晚上关灯的时候,当然看不到了你的手指。所以我们在场景中添加了两盏灯,一盏环境光和一盏直射光。光照不是本文的重点,就不赘述了。只要记住,“天黑了,开灯”//AmbientlightconstambientLight=newTHREE.AmbientLight(0xffffff,0.5)scene.add(ambientLight)//DirectionallightconstdirectionalLight=newTHREE.DirectionalLight(0xffffff,0.5)directionalLight。position.set(2,2,-1)scene.add(directionalLight)嗯!现在球终于露出了它的样子。一个静止的海洋球肯定没有意思,我们要让它动起来,所以我们需要给它加一个物理引擎。有了物理引擎,球会看起来像真人一样,有重力,在高空时会自由下落。不同材质的物体在撞击地面时会有不同的反应。网球会反弹,然后在撞击地面时落下,铅球落地时是静止的。常用的3d物理引擎有Physijs、Ammo.js、Cannon.js和Oimo.js等。我们这里使用的是Cannon.js。Cannon.js官网有很多关于3d物理的效果。详情请看他的官网https://pmndrs.github.io/cann...IntroduceCannon.jsimport*asCANNONfrom'https://cdn.jsdelivr.net/npm/cannon-es@0.19.0/dist/cannon-es.js';先创建一个物理世界,设置重力系数9.8constworld=newCANNON。世界();world.gravity.set(0,-9.82,0);在物理世界中创建一个与我们的Three.js一一对应的球。唯一的区别是你需要设置质量,也就是球的重量。constshape=newCANNON.Sphere(0.5);constbody=newCANNON.Body({mass:1,position:newCANNON.Vec3(0,3,0),shape:shape,});world.addBody(正文);然后我们修改我们的渲染逻辑,我们需要让每一帧的渲染都对应到物理世界。+constclock=newTHREE.Clock();+让oldElapsedTime=0;consttick=()=>{+constelapsedTime=clock.getElapsedTime()+constdeltaTime=elapsedTime-oldElapsedTime;+oldElapsedTime=elapsedTime;+step(1/60,deltaTime,3);控制.更新();renderer.render(scene,camera)window.requestAnimationFrame(tick)}tick();但是我们发现我们的球没有动是因为我们没有绑定物理世界和Three.js球的关系。consttick=()=>{...+mesh.position.copy(body.position);...}让我们看看它现在的样子。小球已经具备了物理特性,处于自由落体状态~但是由于没有地面,小球掉进了无尽的深渊,我们需要设置一个floor,让小球落在一个平面上。在Three.js中创建地面,这里主要使用的是PlaneGeometry,它有4个参数PlaneGeometry(width:Float,height:Float,widthSegments:Integer,heightSegments:Integer)和之前类似,我们只需要关注前2个参数,就是平面的宽和高。由于平面默认为x-y轴的平面,而Three.js默认使用右手坐标系,相应的旋转也是右手定则,所以逆时针为正,顺时针为负,我们平面需要顺时针旋转90°,所以-PI/2constplaneGeometry=newTHREE.PlaneGeometry(20,20);constplaneMaterial=newTHREE.MeshStandardMaterial({color:'#777777',});constplane=newTHREE.Mesh(planeGeometry,planeMaterial);plane.rotation.x=-Math.PI*0.5;scene.add(plane);然后继续绑定飞机的物理引擎,写法基本和Three.js差不多,只是API名称不一样constfloorShape=newCANNON.Plane();constfloorBody=newCANNON.Body();floorBody.mass=0;floorBody.addShape(floorShape);floorBody.quaternion.setFromAxisAngle(newCANNON.Vec3(-1,0,0),Math.PI*0.5);world.addBody(floorBody);来看看效果:不过这个效果好像是铅球落地的效果,没有任何反弹之类的效果。为了让球不会像铅球一样直接落在地上,我们需要给球加上一个弹性模量。constdefaultMaterial=newCANNON.Material("default");constdefaultContactMaterial=newCANNON.ContactMaterial(defaultMaterial,defaultMaterial,{恢复原状:0.4,});world.addContactMaterial(defaultContactMaterial);world.defaultContact...defaultConactMaterial=body=newCANNON.Body({mass:1,position:newCANNON.Vec3(0,3,0),shape:shape,+material:defaultMaterial,});...查看效果:当然,海洋球池不能只有一个球,我们需要有很多很多球。下面我们来实现多个小球的情况。为了生成多个小球,我们需要写一个随机小球生成器。constobjectsToUpdate=[];constcreateSphere=(radius,position)=>{constsphereMaterial=newTHREE.MeshStandardMaterial({金属度:0.3,粗糙度:0.4,颜色:Math.random()me*new0x=conffff})THREE.Mesh(sphereGeometry,sphereMaterial);mesh.scale.set(半径,半径,半径);mesh.castShadow=true;mesh.position.copy(位置);场景。添加(网格);constshape=新CANNON。球体(半径*0.5);constbody=newCANNON.Body({mass:1,position:newCANNON.Vec3(0,3,0),shape:shape,material:defaultMaterial,});body.position.copy(位置);world.addBody(正文);objectsToUpdate.push({mesh,body,});};上面只是我们之前写的代码的一个函数封装,让小球的颜色随机,我们暴露了小球的位置和小球的大小两个参数。最后,我们需要修改更新逻辑,因为我们需要每帧修改每个球的位置信息。consttick=()=>{...for(constobjectofobjectsToUpdate){object.mesh.position.copy(object.body.position);object.mesh.quaternion.copy(object.body.quaternion);}...}然后我们写一个点击事件,当点击屏幕时可以产生100个海洋球。window.addEventListener('click',()=>{for(leti=0;i<100;i++){createSphere(1,{x:(Math.random()-0.5)*10,y:10,z:(Math.random()-0.5)*10,});}},false);查看效果:初步的效果已经实现了,由于我们的水池底部只有一个平面,没有墙,所以小球散了一地。所以大家很容易认为我们要建4堵墙。由于wall和bottomplane的区别在于厚度,不是简单的面,所以我们需要用到一个新的shape——BoxGeometry,它也有7个参数,但是我们只需要关注前三个即可,对应长宽高。BoxGeometry(width:Float,height:Float,depth:Float,widthSegments:Integer,heightSegments:Integer,depthSegments:Integer)现在让我们建造一堵墙,其长度为20,宽度为5,厚度为0.1。constbox=newTHREE.BoxGeometry(20,5,0.1);constboxMaterial=newTHREE.MeshStandardMaterial({color:'#777777',金属度:0.3,粗糙度:0.4,});constbox=newTHREE.Mesh(box,boxMaterial);box.position.set(0,2.5,-10);scene.add(box)现在看起来是这样的:然后我们“照葫芦画瓢”完成剩下的3walls:Untitledandthen我们还在我们的墙壁上添加了一个物理引擎,这样当球碰到它时,就好像它真的撞到了墙上而不是穿过它。consthalfExtents=newCANNON.Vec3(20,5,0.1)constboxShape=newCANNON.Box(halfExtents)constboxBody1=newCANNON.Body({质量:0,材质:defaultMaterial,形状:boxShape,})boxBody1.position.set(0,2.5,-10);world.addBody(boxBody1);...boxBody2boxBody3boxBody4查看效果收获一盆海洋球,大功告成!让我们总结一下我们在这个问题上学到的东西。我们一起使用了SphereGeometry、PlaneGeometry、BoxGeometry,然后学习了Three.js几何与物理引擎cannon.js的绑定,让小球具有物理特性。主要步骤是定义球并将其引入物理引擎。结合Three.js和物理引擎来生成随机球并定义墙。以上就是本章的全部内容。下一章见。github地址:https://github.com/hua1995116...系列连载起始地址:Three.js系列:搭建海洋球池学习物理引擎Three.js系列:游戏中的第一人称和第三人称视角
