three.js cannon.js物理引擎之製作擁有物理特性的汽車

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

今天郭先生說一說使用cannon.js的車輛輔助類讓我們的汽車模型擁有物理特性。效果圖如下,線上案例請點選部落格原文

下面我們說一下今天要使用的兩個類,並簡單的看看他們的物理意義

1. RaycastVehicle類

這是車輛輔助類,將光線從車輪位置投射到地面並施加力。它決定車的位置,角度,質量等資訊。下面是它的建構函式

RaycastVehicle ( [options] )

options一共有四個引數,並且比較好理解,

  • chassisBody – 表示車身的剛體
  • indexForwardAxis – 前軸索引0 = x,1 = y,2 = z
  • indexRightAxis – 前軸索引0 = x,1 = y,2 = z
  • indexUpAxis – 前軸索引0 = x,1 = y,2 = z

後面的三個引數要決定了車輛哪個方向是前面,,哪個是右面,哪個是上面。比如說我們在three.js中一般以y軸作為向上的方向,因此我們就要將indexUpAxis設定成1,indexRightAxis設定成2。如果有特別的要求,three.js的檢視以z軸作為up的方向,這是我們就要將indexUpAxis設定成2,indexRightAxis設定成1(cannon的預設值就是將z軸的方向作為up的方向的)。RaycastVehicle就先說到這,它的一些屬性和方向請檢視文件。

2. WheelInfo類

這個是車輪類,裡面包含了十分豐富的資訊,包括車輪相對於車的位置,車輪半徑,滾動影響,轉彎滑動等等資訊,下面我們看一下建構函式

WheelInfo ( [options] )

這個options就很複雜了,可以在我的例子中手動除錯。下面我們還是通過案例程式碼來學習這兩個類。

3. 案例程式碼

先看看物理引擎的部分

var carBodySize = new THREE.Vector3(4.52, 2.26, 1.08);
var wheelRadius = 0.5;
initCannon() {
        //初始物理裡世界
    world = new CANNON.World();
    world.quatNormalizeSkip = 0;
    world.quatNormalizeFast = false;
    world.gravity.set(0, 0, -9.8);
    world.broadphase = new CANNON.NaiveBroadphase();
    world.solver.iterations = 10;
    world.defaultContactMaterial.friction = 0;
        //定義車體形狀
    var chassisShape;
        //車體為一個矩形
    chassisShape = new CANNON.Box(new CANNON.Vec3(carBodySize.x/2, carBodySize.y/2, carBodySize.z/2));
        //定義車體剛體
    var chassisBody = new CANNON.Body({mass: 150, material: new CANNON.Material({friction: 0, restitution: 0})})
        //剛體中新增形狀
    chassisBody.addShape(chassisShape);
        //初始化剛體的位置
    chassisBody.position.set(0, 0, 5);
        //設定一個初始的角速度
    chassisBody.angularVelocity.set(0, 0, 0.5);
        //初始化車輛引擎
    vehicle = new CANNON.RaycastVehicle({
        chassisBody: chassisBody, indexForwardAxis: 0, indexRightAxis: 1, indexUpAxis: 2
    });

    var options = {
        radius: wheelRadius,
        directionLocal: new CANNON.Vec3(0, 0, -wheelRadius * 2),
        suspensionStiffness: 30,
        suspensionRestLength: 0.3,
        frictionSlip: 5,
        dampingRelaxation: 2.3,
        dampingCompression: 4.4,
        maxSuspensionForce: 100000,
        rollInfluence:  0.01,
        axleLocal: new CANNON.Vec3(0, 1, 0),
        chassisConnectionPointLocal: new CANNON.Vec3(1, 1, 0),
        maxSuspensionTravel: 0.3,
        customSlidingRotationalSpeed: -30,
        useCustomSlidingRotationalSpeed: true
    };
    //設定第一個輪的位置,並將輪子資訊新增到車輛類中
    options.chassisConnectionPointLocal.set(1.13, 0.95, -0.1);
    vehicle.addWheel(options);
        //設定第二個輪的位置,並將輪子資訊新增到車輛類中
    options.chassisConnectionPointLocal.set(1.13, -0.95, -0.1);
    vehicle.addWheel(options);
        //設定第三個輪的位置,並將輪子資訊新增到車輛類中
    options.chassisConnectionPointLocal.set(-1.47, 0.95, -0.05);
    vehicle.addWheel(options);
        //設定第四個輪的位置,並將輪子資訊新增到車輛類中
    options.chassisConnectionPointLocal.set(-1.47, -0.95, -0.05);
    vehicle.addWheel(options);
        //通過addToWorld方法將將車輛及其約束新增到世界上。
    vehicle.addToWorld(world);
        //高度場的方法,上節已經瞭解了,不多說
    var matrix = [];
    for (var i = 0; i < size; i++) {
        matrix.push([]);
        for (var j = 0; j < size; j++) {
            var height = Math.cos(i / size * Math.PI * (size / 10)) * Math.cos(j / size * Math.PI * (size / 10)) / 2;
            matrix[i].push(height)
        }
    }
    var hfShape = new CANNON.Heightfield(matrix, {
        elementSize: 1
    });
    var hfBody = new CANNON.Body({ mass: 0, material: new CANNON.Material({friction: 0.5, restitution: 0})});
    hfBody.addShape(hfShape, new CANNON.Vec3(-size / 2, - size / 2, 0), new CANNON.Quaternion());
    world.addBody(hfBody);
},

接下來是操作小汽車的一些程式碼

document.onkeydown = this.handler;
document.onkeyup = this.handler;
handler(event) {
    var up = (event.type == 'keyup');
    if(!up && event.type !== 'keydown'){
        return;
    }

    vehicle.setBrake(0, 0);
    vehicle.setBrake(0, 1);
    vehicle.setBrake(0, 2);
    vehicle.setBrake(0, 3);

    switch(event.keyCode){
        case 38: // 按下向前鍵時,通過applyEngineForce方法,設定車輪力以在每個步驟中施加在後車輪上
            vehicle.applyEngineForce(up ? 0 : -params.maxForce, 2);
            vehicle.applyEngineForce(up ? 0 : -params.maxForce, 3);
            break;

        case 40: // 同上
            vehicle.applyEngineForce(up ? 0 : params.maxForce, 2);
            vehicle.applyEngineForce(up ? 0 : params.maxForce, 3);
            break;

        case 66: // 剎車鍵b,通過setBrake方法,設定四個車輪的制動力
            vehicle.setBrake(brakeForce, 0);
            vehicle.setBrake(brakeForce, 1);
            vehicle.setBrake(brakeForce, 2);
            vehicle.setBrake(brakeForce, 3);
            break;

        case 39: // 按下右鍵,通過setSteeringValue方法,設定前輪轉向值
            vehicle.setSteeringValue(up ? 0 : -maxSteerVal, 0);
            vehicle.setSteeringValue(up ? 0 : -maxSteerVal, 1);
            break;

        case 37: // 同上
            vehicle.setSteeringValue(up ? 0 : maxSteerVal, 0);
            vehicle.setSteeringValue(up ? 0 : maxSteerVal, 1);
            break;
        }
},

好的這就是主要程式碼,小車是在THREE的官方demo上面下載的。

 

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

相關文章