three.js cannon.js物理引擎之約束(二)

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

今天郭先生繼續講cannon.js的物理約束,之前的一篇文章曾簡單的提及過PointToPointConstraint約束,那麼今天詳細的說一說cannon.js的約束和使用方法。線上案例請點選部落格原文

1. cannon.js約束的種類

1. PointToPointConstraint點對點約束

它的建構函式如下(之前可能介紹過了,這次統一說)

PointToPointConstraint ( bodyA  pivotA  bodyB  pivotB  maxForce )

 

  • bodyA — 剛體A。
  • pivotA — 相對於剛體A的質心的點,剛體A被約束到該點。
  • bodyB — 剛體B。
  • pivotB — 相對於剛體B的質心的點,剛體B被約束到該點。
  • maxForce — 約束物體應施加的最大力。

點對點約束顧名思義就是A剛體的某一點和B剛體的某一點形成約束,剛體之間僅通過約束點相連,如下圖。

2. LockConstraint鎖定約束

它的建構函式如下

LockConstraint ( bodyA  bodyB { maxForce } )
  • bodyA — 剛體A。
  • bodyB — 剛體B。
  • maxForce — 約束物體應施加的最大力。

為什麼不需要設定約束點的位置呢,因為鎖定約束其實就是點對點約束的簡化版本,他們pivotA和pivotB預設為剛體的中心,如下圖。

3. DistanceConstraint距離約束

它的建構函式如下

DistanceConstraint ( bodyA bodyB distance maxForce )
  • bodyA — 剛體A。
  • bodyB — 剛體B。
  • distance — 要保持的距離。如果未定義,它將被設定為剛體A和剛體B之間的當前距離。
  • maxForce — 約束物體應施加的最大力。

距離約束將兩個物體約束為彼此重心的距離恆定,如下圖是相鄰小球保持恆定距離。



4. HingeConstraint鉸鏈約束

它的建構函式如下

HingeConstraint ( bodyA  bodyB  { pivotA axisA pivotB axisB maxForce } )
  • bodyA — 剛體A。
  • bodyB — 剛體B。
  • pivotA — 相對於剛體A的質心的點,剛體A被約束到該點。
  • axisA — 在剛體A中區域性定義的剛體A可以繞其旋轉的軸。
  • pivotB — 相對於剛體B的質心的點,剛體B被約束到該點。
  • axisB — 在剛體B中區域性定義的剛體B可以繞其旋轉的軸。
  • maxForce — 約束物體應施加的最大力。

鉸鏈又稱合頁,這個約束就像門的鉸鏈一樣,讓兩個物理只能在各自的點沿著固定的軸旋轉。如下圖。

2. 案例的主要程式碼

下面是案例的主要程式碼

var bodies = [], meshes = [];

initPoint() {
    var size = 0.5;
    var boxShape = new CANNON.Box(new CANNON.Vec3(size,size,size));
    var mass = 0;

    var N=10, last;

    for(var i=0; i<N; i++){
        // Create a box
        var boxbody = new CANNON.Body({
            mass: mass,
            shape: boxShape,
            position: new CANNON.Vec3(i * 0.6, (N - i) * Math.sqrt(size * size * 3) * 2, 0),
            quaternion: new CANNON.Quaternion().setFromEuler(-Math.PI / 4, -Math.PI / 4, 0),
            material: pubMaterial
        });
        boxbody.angularDamping = 0.3;
        bodies.push(boxbody);
        world.addBody(boxbody);

        if(i == 0) {
            mass = 1;
        } else {
            var c = new CANNON.PointToPointConstraint(boxbody, new CANNON.Vec3(size, size ,size), last, new CANNON.Vec3(-size, -size ,-size));
            world.addConstraint(c);
        }

        last = boxbody;
    }

    for(let i=0; i<10; i++) {
        let mesh = new THREE.Mesh(new THREE.BoxBufferGeometry(1), new THREE.MeshNormalMaterial());
        meshes.push(mesh);
        scene.add(mesh);
    }
    console.log(world)
}
initLock() {
    var size = 0.5;
    var boxShape = new CANNON.Box(new CANNON.Vec3(size,size,size));
    var mass = 1;
    var space = 0.1*size;

    var N=10, last;

    for(var i=0; i<N; i++){
        // Create a box
        var boxbody = new CANNON.Body({
            mass: mass,
            shape: boxShape,
            position: new CANNON.Vec3((N-i-N/2)*(size*2+2*space), size*6+space, 0),
            sleepSpeedLimit: 0,
            material: pubMaterial
        });
        bodies.push(boxbody);
        world.addBody(boxbody);


        if(last){
        // Connect the current body to the last one
            var c = new CANNON.LockConstraint(boxbody, last);
            world.addConstraint(c);
        }

        // To keep track of which body was added last
        last = boxbody;
    }
    var bodyA = new CANNON.Body({
        mass: 0,
        shape: boxShape,
        position: new CANNON.Vec3((-N/2+1)*(size*2+2*space), size*3-1, 0),
        material: pubMaterial
    });
    bodies.push(bodyA);
    world.addBody(bodyA);

    var bodyB = new CANNON.Body({
        mass: 0,
        shape: boxShape,
        position: new CANNON.Vec3((N/2)*(size*2+2*space), size*3-1, 0),
        material: pubMaterial
    });
    bodies.push(bodyB);
    world.addBody(bodyB);

    for(let i=0; i<12; i++) {
        let mesh = new THREE.Mesh(new THREE.BoxBufferGeometry(1), new THREE.MeshNormalMaterial());
        meshes.push(mesh);
        scene.add(mesh);
    }
}
initCloth() {
    var size = 0.2;
    var dis = 0.5;
    var sphereShape = new CANNON.Sphere(size);
    var mass = 1;

    var Nrows = 15, Ncols = 15;
    for(let i=0; i<Nrows; i++) {
        for(let j=0; j<Ncols; j++) {
            let body = new CANNON.Body({
                mass: mass,
                shape: sphereShape,
                position: new CANNON.Vec3((i - 0.5 * Nrows + 0.5) * dis, 9, (j - 0.5 * Ncols + 0.5) * dis),
                material: pubMaterial
            })
            bodies.push(body);
            world.addBody(body);
        }
    }

    let spherebody = new CANNON.Body({
        mass: 0,
        shape: new CANNON.Sphere(4),
        position: new CANNON.Vec3(0,4,0),
        material: pubMaterial
    })

    bodies.push(spherebody);
    world.add(spherebody);
    

    for(let i=0; i<Nrows * Ncols; i++) {
        let r = Math.floor(i / Nrows);
        let c = i % Nrows;
        if(r < Nrows - 1) {
            world.addConstraint(new CANNON.DistanceConstraint(bodies[r * Nrows + c], b2, dis, 5));
        }
        if(c < Ncols - 1) {
            world.addConstraint(new CANNON.DistanceConstraint(bodies[r * Nrows + c], bodies[r * Nrows + c + 1], dis, 5));
        }
    }

    for(let i=0; i<Nrows * Ncols; i++) {
        let mesh = new THREE.Mesh(new THREE.SphereBufferGeometry(size), new THREE.MeshNormalMaterial());
        meshes.push(mesh);
        scene.add(mesh);
    }
    
    let mesh = new THREE.Mesh(new THREE.SphereBufferGeometry(4, 32, 16), new THREE.MeshNormalMaterial());
    meshes.push(mesh);
    scene.add(mesh);
}
initHinge() {
    let bodyA = new CANNON.Body({
        mass: 0,
        shape: new CANNON.Box(new CANNON.Vec3(0.2, 4, 0.2)),
        position: new CANNON.Vec3(-3.2, 5, 0),
        material: pubMaterial
    })
    let bodyB = new CANNON.Body({
        mass: 1,
        shape: new CANNON.Box(new CANNON.Vec3(3, 4, 0.2)),
        position: new CANNON.Vec3(0, 5, 0),
        // material: pubMaterial
    })

    bodyB.velocity.set(0, 0, -10);

    bodies.push(bodyA);
    bodies.push(bodyB);
    world.add(bodyA);
    world.add(bodyB);

    var c = new CANNON.HingeConstraint(bodyA, bodyB, { 
        pivotA: new CANNON.Vec3(0.3, 0, 0),
        axisA: new CANNON.Vec3(0, 1, 0),
        pivotB: new CANNON.Vec3(-3.1, 0, 0),
        axisB: new CANNON.Vec3(0, 1, 0),
        maxForce: 2
    });
    world.addConstraint(c);

    let meshA = new THREE.Mesh(new THREE.BoxBufferGeometry(0.4, 8, 0.4), new THREE.MeshNormalMaterial());
    let meshB = new THREE.Mesh(new THREE.BoxBufferGeometry(6, 8, 0.4), new THREE.MeshNormalMaterial());
    meshes.push(meshA);
    meshes.push(meshB);
    scene.add(meshA);
    scene.add(meshB);
}

 

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

相關文章