今天郭先生繼續講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); }
轉載請註明地址:郭先生的部落格