今天郭先生說的是一個物理引擎,它十分小巧並且操作簡單,沒錯他就是cannon.js。這些優點都源自於他是基於js編寫的,對於js使用者來說cannon.js擁有其他物理引擎沒有的純粹性。從學習成本來看,cannon.js的學習成本比較低,對於新手來說比較友好,因為它有相對完善的api,學習cannon.js之前我們不妨來看看cannons.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。
轉載請註明地址:郭先生的部落格