three.js cannon.js物理引擎地形生成器和使用指標鎖定控制元件

郭先生的部落格發表於2021-01-25

今天郭先生說一說使用cannon.js物理引擎繪製地形和使用指標鎖定控制元件。效果如下圖。線案例請點選部落格原文

這裡面的生成地形的外掛和指標鎖定控制元件也是cannon.js的作者schteppe封裝的,當然也可以自己寫一個這樣的小外掛。好的我們先說說這兩個外掛的使用方法,然後結合一個小案例應該他們。

1. 地形生成外掛

相信一些同學玩過我的世界這款遊戲,它的地形就是由好多個規格相同的正方體組成。就像下面這樣

那麼VoxelLandscape.js能夠很好的實現這樣的地形生成,我們先來看看生成地形建構函式所需要的引數

voxels = new VoxelLandscape(world,nx,ny,nz,sx,sy,sz);
  • world —cannon.js中的CANNON.World()例項
  • nx — x方向上地形的塊數
  • ny — y方向上地形的塊數
  • nz — z方向上地形的塊數
  • sx — 小矩形x方向上halfSize
  • sy — 小矩形y方向上halfSize
  • sz — 小矩形z方向上halfSize

這樣就能構建出一個地形,由於我們還沒有進行其他操作,它看起來就是一整塊矩形,但是它為我們提供了很多方法,比如說setFilled方法(其他的方法可以自行測試),這個方法可以設定哪些矩形塊需要填充,哪些不需要填充,使用方法如下

voxels.setFilled(i,j,k),filled);
  • i — x方向的第i個
  • j — y方向的第j個
  • k — z方向的第k個
  • filled — 是否需要填充

i,j,k可以確定具體是那一塊,filled是一個布林值,true表示填充,false表示不填充,下面是隨機挖空5000次方塊的例子。

voxels = new VoxelLandscape(world,58,58,58,0.5,0.5,0.5);
for(let i=0; i<5000; i++) {
    var filled = false;
    voxels.setFilled(Math.floor(Math.random() * 58),Math.floor(Math.random() * 8),Math.floor(Math.random() * 58),filled);
}

這樣就可以隨心所欲的設定地形了。

2. 指標鎖定控制元件

對於指標鎖定控制元件,three.js已經為我們提供了PointerLockControls方法,它是基於Pointer Lock API實現的,主要用到的方法有請求指標鎖定requestPointerLock()、退出指標鎖定exitPointerLock()等,下面來說一說這個用於cannon.js的PointerLockControls,他和three的版本又些許區別,看下面程式碼。

controls = new PointerLockControls( camera , sphereBody );//第一個引數是camera,第二個引數是剛體物件
scene.add( controls.getObject() );//將控制器加入到場景
if(controls) {//如果controls初始化成功
    if(document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element) {//如果element處於鎖定中,則開啟controls
        controls.enabled = true;//因為預設enabled為false,所以一般都需要手動開啟
        this.$refs.info.style.display = 'none';
    } else {
        controls.enabled = false;
        this.$refs.info.style.display = 'inline-block';
    }
}
controls.update(clock.getDelta() * 1000);//在每次更新物理世界的時候呼叫controls.update()方法

指標鎖定控制元件使用一個剛體作為camera的載體,可以往camera的移動看起來更加生動(擁有物理特性)。

3. 案例

下面直接上主要案例程式碼

initCannon() {
    world = new CANNON.World();
    world.quatNormalizeSkip = 0;
    world.quatNormalizeFast = false;
    world.gravity.set(0, -9.8, 0);
    world.broadphase = new CANNON.NaiveBroadphase();
    world.solver.iterations = 10;

    physicsMaterial = new CANNON.Material("slipperyMaterial");
    var physicsContactMaterial = new CANNON.ContactMaterial(physicsMaterial, physicsMaterial, {friction: 0.3, restitution: 0.5});//規定相同的physicsMaterial材質之間的發生接觸時的物理引數。
    world.addContactMaterial(physicsContactMaterial);

    var nx = 58, ny = 8, nz = 58, sx = 0.5, sy = 0.5, sz = 0.5;

    const radius = 1.6;

    sphereBody = new CANNON.Body({ //使用球形剛體作為相機載體,讓相機擁有質量屬性
        mass: 5,
        position: new CANNON.Vec3(nx*sx*0.5,ny*sy+radius*2,nz*sz*0.5),
        shape: new CANNON.Sphere(radius),
        material: physicsMaterial
    });
    sphereBody.linearDamping = 0.9;
    world.addBody(sphereBody);

    groundBody = new CANNON.Body({//地面
        mass: 0,
        position: new CANNON.Vec3(0, -0.1, 0),
        shape: new CANNON.Box(new CANNON.Vec3(200, 0.1, 200)),
        material: physicsMaterial
    });
    world.addBody(groundBody);

    voxels = new VoxelLandscape(world,nx,ny,nz,sx,sy,sz);
    
    for(var i=0; i<nx; i++){
        for(var j=0; j<ny; j++){
            for(var k=0; k<nz; k++){
                var filled = true;
                if(Math.abs(Math.sin(i / 6)) < j / 5) {
                    filled = false;
                }
                if(Math.abs(Math.sin(k / 6)) < j / 5) {
                    filled = false;
                }

                voxels.setFilled(i,j,k,filled);

            }
        }
    }
    voxels.update();
},

for(var i=0; i<voxels.boxes.length; i++){//建立地形相應的網格,該資料儲存在voxels.boxes陣列中
    var b = voxels.boxes[i];
    var voxelGeometry = new THREE.BoxBufferGeometry( voxels.sx*b.nx, voxels.sy*b.ny, voxels.sz*b.nz );
    var voxelMesh = new THREE.Mesh( voxelGeometry, new THREE.MeshPhysicalMaterial({color: 0x666666}) );
    voxelMesh.castShadow = true;
    voxelMesh.receiveShadow = true;
    scene.add( voxelMesh );
    boxMeshes.push( voxelMesh );
}

以上就是地形生成器和使用指標鎖定控制元件的使用方法

 

轉載請註明地址:郭先生的部落格

相關文章