今天郭先生說的是一個物理引擎,它十分小巧並且操作簡單,沒錯他就是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。
轉載請注明地址:郭先生的博客