遊戲中的對象按照物理規律移動,體現重力、引力、副作用力、加速度等物體特性,實現自由落體、搖擺運動、拋物線運動,以及物理碰撞現象的模擬。用於模擬物理碰撞、物理運動的引擎稱爲物理引擎。
來自瑞典斯德哥爾摩大學的Stefan Hedman基於JavaScript,開發了一款面向HTML遊戲的2D物理引擎,P2物理引擎。P2和Box2D物理引擎同樣,集成了各類複雜的物理公式和算法,幫助實現碰撞、加速、自由落體等物理對象的模擬。
P2是一個開源項目,可在GitHub下載,使用build中的p2.min.js文件,就能夠開發物理應用。算法
1. 建立P2物理項目:編程
使用P2物理引擎建立物理應用的過程和Box2D類型,步驟是:建立world、建立shape、建立body剛體、實時調用step()函數更新物理模擬計算;基於形狀、剛體使用Egret或其餘HTML渲染以顯示物理模擬效果。
1)世界world:
world是P2物理引擎入口,對應World類,用於承載全部物理模擬對象。world類的構造函數爲:
function World([options]){options?:{gravity?:number[]=[0,-9.81];}}
其中,gravity是重力加速度,這是一個Vec2類型的向量對象,默認爲垂直向上的向量[0,-9.81]。將gravity設置爲[0,0]能夠取消重力,模擬太空失重狀態。
2)形狀Shape:
形狀是物理模擬計算的基礎。任何物體都要有對應的形狀,才能夠基於P2進行物理碰撞檢測和模擬。全部形狀對象都須要經過addShape()添加到剛體中,才能夠進行碰撞模擬計算:
var body:p2.Body=new p2.Body();
var shape:p2.Shape=new p2.Shape();
body.addShape(shape);
P2中的Shape類是一個抽象的父類,要使用Box、Circle等子類。
3)剛體Body:
剛體是P2物理引擎的核心概念和對象,擁有速度、角度、質量等物理屬性,同時包含了形狀對象,使剛體擁有具體的形狀。將剛體添加到world中,World將以剛體爲單位循環遍歷,進行物理模擬計算,並將模擬的結果保存在剛體屬性中,使剛體成爲碰撞對象的原型。全部的剛體都必須經過addBody()添加到P2的world中,纔會進行物理模擬:
var body:p2.Body=new p2.Body();
var shape:p2.Shape=new p2.Shape();
body.addShape(shape);
world.addBody(body);
4)貼圖Egret:
P2只是一個算法庫,以剛體爲對象模型,模擬並輸出物理碰撞、運動結果。這個過程經過持續調用world中的step()方法來實現:
function step(dt:number, timeSinceLastCalled?:number=0,maxSubSteps?:number=10)
其中,參數dt是step方法執行的時間間隔,單位秒,一般取值爲遊戲幀頻的倒數; 當遊戲幀頻下降時計算兩幀之間的時間差做爲timeSinceLastCalled參數值,此時P2會在一次step()中進行count= timeSinceLastCalled/dt次計算,以保證物理模擬的真實性,默認值爲0;參數maxSubSteps是單次step()進行物理模擬計算的最大次數,當timeSinceLastCalled不等於0時,單次step()中進行計算的次數count最大爲maxSubSteps,默認值爲10。
但P2自己不具有渲染功能,沒法顯示模擬結果,須要藉助JavaScript渲染引擎,如Egret、Cocos2d-js、Pixi、phaser等,經過繪製或貼圖來渲染物理模擬結果。在Egret中:
class SampleP2APP extends egret.DisplayObjectContainer{
public constructor(){
super();
this.createP2App();
}
private world:p2.World;
private factor:number=30;
public createP2App():void{
this.world=new p2.World();
var world=this.world;
world.gracity=[0,0];
var shape:p2.Box=new p2.Box({width:100/this.factor, height:50/this.factor});
var body:p2.Body=new p2.Body({mass:1});
body.position=[275/this.factor, 100/this.factor];
body.addShape(shape);
worldBody(body);
this.addEventListener(egret.Event.ENTER_FRAME, this.loop, this);
}
private loop(e.egret.Event):void{
this.world.step(1/60);
}
}
代碼中建立了一個重力加速度gravity=[0,0]的失重環境world;而後使用Box形狀建立尺寸100x50像素矩形;經過addShape()方法將其添加到位於(275,100)位置的剛體body中;最後經過world的addBody()方法將剛體添加到世界中,完成一個基本的P2物理應用建立。在遊戲的loop更新方法中,每幀持續調用step()方法,實現P2物理模擬計算的持續更新。
P2物理引擎中的座標單位是米,而大部分的遊戲引擎在屏幕上渲染遊戲時,都是基於像素的,因此在建立剛體對象、設置形狀尺寸時,須要將像素轉換成米後再進行賦值。遊戲中,1米一般看做30px,因此代碼中聲明瞭一個值爲30的factor變量,在設置形狀尺寸或剛體時,都是使用像素座標除以factor變量,轉換成米以後再賦值的。數組
2. 用p2DebugDraw實現模擬視圖:app
由於P2只是進行了物理模擬計算,沒有對模擬結果進行渲染,能夠基於Egret引擎編寫渲染繪圖的類,如p2DebugDraw。p2DebugDraw類的構造函數爲:
function p2DebugDraw(world:p2.World, sprite:egret.Sprite)
其中,world是P2中建立的world對象,sprite是Stage中一個Sprite對象。p2DebugDraw將使用基本繪圖的API,在sprite對象中繪製全部剛體、關節等物理對象。修改後的代碼:
class SampleP2APPWithDebugDraw extends egret.DisplayObjectContainer{
public constructor(){
super();
this.createP2App();
}
private world:p2.World;
private factor:number=30;
private debugDraw:p2DebugDraw;
public createP2App():void{
this.world=new p2.World();
var world=this.world;
world.gracity=[0,0];
var shape:p2.Box=new p2.Box({width:100/this.factor, height:50/this.factor});
var body:p2.Body=new p2.Body({mass:1});
body.position=[275/this.factor, 100/this.factor];
body.addShape(shape);
worldBody(body);
var sprite:egret.Sprite=new egret.Sprite();
this.addChild(sprite);
this.debugDraw=new p2DebugDraw(world, sprite);
this.addEventListener(egret.Event.ENTER_FRAME, this.loop, this);
}
private loop(e.egret.Event):void{
this.world.step(1/60);
this.debugDraw.drawDebug();
}
}
p2DebugDraw仿照Box2d中的b2DebugDraw,用不一樣顏色和形狀表示不一樣類型的剛體、關節等對象,粉色爲動態剛體、紫色爲可動剛體、綠色爲靜態剛體、黑色線段爲關節、紅色線段爲彈簧、綠色線段爲剛體與關節點的偏移量、圓的爲關節節點。
p2DebugDraw的屬性和方法:
p2DebugDraw提供了不少屬性和方法:
1)isDrawAABB:boolean:是否繪製剛體的AABB最小包圍框,默認false
2)drawDebug():繪製P2物理引擎中的全部剛體、關節等對象,須要實時調用實時更新
3)drawShape():繪製P2世界中的任意形狀,使用時要確保形狀對應剛體已添加到world
drawShape()方法的結構爲:
function drawShape(shape:p2.Shape, color?:number, fillColor?:boolean):void
其中,shape爲要繪製的Shape對象;color爲要繪製的顏色,缺省時根據類型設置顏色;fillColor指示是否填充顏色,默認true,填充色與邊框色相同。
4)drawConvex():將vertices數組中保存的任意多個頂點座標,逐個使用線段鏈接起來,繪製出多邊形。此方法不受P2剛體約束,可在任意須要狀況下使用。
function drawConvex( vertices:number[][], color:number, alpha:number=1, fillColor:boolean = true ):void
其中,vertices保存了須要繪製的多邊形頂點數組;color爲多邊形的邊框顏色;alpha爲多邊形填充顏色透明度;fillColor爲是否填充顏色,默認true,填充色與邊框色相同。
5)drawCircle():在指定位置以指定的半徑繪製圓形,該方法不受P2剛體約束,可在任意須要狀況下使用。
function drawCircle( pos:number[], radius:number, color:number, alpha:number=1, fillColor?:boolean ):void
其中,pos爲圓形繪製的目標位置,radius爲圓形半徑。
6)drawSegment():在兩點之間繪製線段,結構爲:
function drawSegment( start:number[], end:number[], color:number):void
其中,start爲線段起點,end爲線段終點,color爲線段顏色。
7)drawVecAt():在指定點at處繪製向量v。用來顯示出剛體速度,結構爲:
function drawVecAt( v:number[],at:number[], color:number, markStart:boolean=false ):void
其中,v爲要繪製的向量;at爲開始繪製向量的起點;color爲向量顏色;markStart爲是否用圓點表示出向量的起點,默認false。
8)drawVecTo():繪製向量v,指向指定點to。
function drawVecTo( v:number[],to:number[], color:number, markStart:boolean=false ):void
其中,v爲要繪製的向量;to爲向量的終點;color爲繪製的顏色;markStart爲是否用圓點表示出向量的終點,默認false。ide
3. P2中的形狀:函數
形狀是物理引擎進行碰撞模擬計算的依據,是剛體最基本的屬性。P2中使用Shape類來表示形狀,經過剛體的addShape()方法,將形狀添加到剛體中以後,就能夠隨着剛體的移動、旋轉不斷更新,並進行碰撞檢測了。爲剛體添加形狀的示例代碼爲:
var shape:p2.Shape=new p2.Shape();
var body:p2.Body=new p2.Body();
body.addShape(shape);
Shape類自己並不參與剛體的建立,而是由其幾個子類完成一些常見形狀的模擬。這些形狀包括圓形Circle、矩形Box、膠囊Capsule、粒子Particle、線段Line、平面Plane、海拔形狀HightField、多邊形Convex等。oop
1)形狀:
遊戲中人物形狀各類各樣,但在碰撞檢測時,出於效率考慮,大都被簡化爲簡單的圓形、矩形等形狀。P2預置了包括圓形、矩形在內的一些經常使用的、簡單的形狀。
⑴圓形Circle:
圓形是P2中的基本形狀,構造函數爲:
function Circle({options?:{radius:number})
其中,options是包含全部屬性的集合對象;radius是圓形的半徑,默認值爲1。
⑵矩形Box:
矩形經過width和height屬性來建立,構造函數爲:
function Box(options:{width?:number, height?:number})
其中,width爲矩形寬度,默認爲1;height爲矩形高度,默認值爲1。
⑶膠囊Capsule:
能夠認爲是一種圓角矩形,長度length,高度2*Radius,兩端是半徑爲Radius的半圓形。
function Capsule(options?:{length?:number, radius?:number})
其中,length爲膠囊長度;radius爲膠囊形狀兩端半圓的半徑,同時也是膠囊的高度。
⑷粒子Particle:
粒子就是微小的顆粒,P2物理引擎中的粒子半徑和麪積爲0。粒子參與碰撞檢測,效果與半徑爲1px的圓形同樣。構造函數簡單,不須要任何參數:
function Particle()
⑸線段Line:
長度爲length的線段,看上去與高度爲1的Box形狀同樣,但算法上省去對高度的檢測。
function Line(options?:{length?:number})
⑹平面Plane:
平面沿y軸負方向無限擴展,同時在x軸方向的寬度無限,像地平面。構造函數爲:
function Plane()
建立時沒有任何參數,初始狀況下是一個倒置的穹,實際應用時通常要經過調整剛體的角度angle,使plane平面朝向不一樣的方向,來模擬牆體。示例代碼:
var shape:p2.Plane=new p2.Plane();
var body:p2.Body=new p2.Body({mass:1, position:[274/this.factor, 200/this.factor]});
body.addShape(shape);
body.angle=Math.PI/4;
this.world.addBody(body);
代碼中的平面會隨着剛體的角度angle繞座標點[274,200]順時針旋轉45°。
⑺海拔形狀Heightfield:
這是一個相似於Plane的形狀,但不是平的,而是一組y座標組成的高低不平的山形,這些山丘之間的間隔都是固定的elementWidth。HeightField形狀也是朝y軸負方向無限擴展的,水平方向的寬度是elementWidth與山丘數量的乘積。構造函數爲:
function Heightfield(options?:{heights:number[], minValue?:number,maxValue?:number, elementWidth?:number})
其中,heights是保存每一個山丘高度的數組;minValue是山丘高度的最小值,設置後heights中的小於minValue的值設置爲minValue;maxValue爲heights中高度的最大值,設置此值後heights中大於maxValue的值設置爲maxHeight;elementWidth爲每一個山丘之間的間隔,默認爲1。
定義Heightfield形狀就是定義heights屬性中y座標數組,同時x座標以elementWidth爲步長逐一增長。目前,Heightfield不支持旋轉,始終朝y軸負方向擴展,碰撞檢測也不精確,邊緣位置容易出現穿透現象。
⑻多邊形Convex:
Convex是一個多邊形形狀,能夠根據一組定義好的頂點座標建立對應的多邊形形狀。
function Convex(options?:{vertice?:number[][], axes?:number[][[]})
其中,vertices保存了頂點座標的數組,這是一個二維數組,每一個元素是由x和y座標組成的一維數組,如vertics=[[-1,-1],[1,-1],[1,1],[-1,-1]];axes表示多邊形各個邊的對稱軸,一樣是一個二維數組,且長度應與vertices一致,此參數一般可使用默認值,系統根據vertices中的頂點自動計算得出垂直於各個邊的法向量。使用時首先要將多邊形的頂點座標保存到數組中,而後將其做爲vertices參數傳遞給Convex:
var points=[[-1,-1],[1,-1],[1,1],[-1,-1]];
var shape:p2.Convex=new p2.Convex({vertices:points});
var body:p2.Body=new p2.Body({mass:1});
body.addShape(shape);
this.world.addBody(body);
不推薦使用頂點座標直接建立多邊形,由於可能出現凹多邊形,而P2中的碰撞檢測是基於凸多邊形的,須要將凹多邊形分解成多個小的凸多邊形,還須要從新計算分解後的重心、形狀偏移,這些計算並不在Convex類中。建議使用Body類的fromPolygon()實現。post
2)P2中形狀碰撞關係:
P2中形狀碰撞不完善,一些形狀之間沒法實現碰撞,各個形狀之間的碰撞關係見表:ui
|
Circlethis |
Plane |
Box |
Convex |
Particle |
Line |
Capsule |
Heightfield |
Ray |
Circle |
Yes |
- |
- |
- |
- |
- |
- |
- |
- |
Plane |
Yes |
- |
- |
- |
- |
- |
- |
- |
- |
Box |
Yes |
Yes |
Yes |
- |
- |
- |
- |
- |
- |
Convex |
Yes |
Yes |
Yes |
Yes |
- |
- |
- |
- |
- |
Particle |
Yes |
Yes |
Yes |
Yes |
- |
- |
- |
- |
- |
Line |
Yes |
Yes |
? |
? |
- |
- |
- |
- |
- |
Capsule |
Yes |
Yes |
Yes |
Yes |
Yes |
? |
Yes |
- |
- |
Heightfield |
Yes |
- |
Yes |
Yes |
? |
? |
? |
- |
- |
Ray |
Yes |
Yes |
Yes |
Yes |
- |
Yes |
Yes |
Yes |
- |
其中?部分是有待完善的。
3)形狀屬性:
p2的幾種內置形狀各有本身的屬性,但都繼承自Shape類,因此有共同的屬性,包含在Shape的構造函數內:
function Shape(options?:{
position?:number[],
angle?:number,
collisionGroup?:number,
collisionMask?:number,
sensor?:boolean,
collisionResponse?:boolean,
type?:number,
material?:Material});
其中,position是形狀相對於本地座標中心的偏移量,這個偏移量會影響剛體的重心;angle爲形狀在剛體本地座標系統中傾斜的角度;collisionGroup爲碰撞分組,與collisionMask一塊兒使用,限制當前形狀只與指定條件的形狀發生碰撞;collisionMask爲碰撞篩選,與collisionGroup一塊兒使用,限制當前形狀只與指定條件的形狀發生碰撞;sensor設置形狀是否爲敏感區域,默認false,若是設置爲true,則該形狀不參與碰撞模擬,只做爲感應區域;collisionResponse設置當與其餘剛體發生碰撞時,當前形狀是否進行碰撞模擬,默認true,若是設爲false,碰撞發生時當前形狀會穿過碰撞剛體;type爲剛體類型,取值範圍是Shape. CIRCLE和Shape.BOX等常量之一,不須要設置該參數,系統會自動設置。
使用collisionGroup和collisionMask屬性時,有兩個形狀si和sj,對si的collisionGroup屬性與sj的collisionMask屬性按位與運算,再對sj的collisionGroup屬性與si的collisionMask屬性按位與運算,若是兩個結果均不爲0,則忽略這次si與sj的碰撞檢測。由於要按位運算,因此通常設置成2的倍數。
material爲材質,是一個Material對象,用來設置形狀發生碰撞時表現出的響應特性,如摩擦力、彈性係數等
實際上,Material類中並不包含摩擦力、彈性係數等屬性,它只是一個標誌類,只是一個id,真正實現碰撞響應特性的是ContactMaterial類。ContactMaterial類用來爲添加了merialA和merialB標識的兩個形狀設置獨特的碰撞響應特性,構造函數爲:
function ContectMaterial(
materialA:Material, materialB:Material,
options?:{
friction:number,
restitution:number,
stiffness:number,
relaxation:number,
frictionStuffness:number,
frictionRelaxation:number,
sufaceVelocity:number})
其中,materialA是形狀shapeA的材質標識,materialB是形狀shapeB的材質標識,options是兩個形狀發生碰撞時響應特性的設置;friction是兩個形狀接觸面的摩擦係數,默認0.3;restitution是兩個形狀碰撞時的彈性係數,默認0;stiffness等同於ContactEquation的stiffness屬性,只碰撞時形狀表面的硬度,默認1000000,當stiffness較小時形狀之間能夠重疊並有一個排斥力促使分離,造成海綿或水平的彈性表面效果;relaxation等同於ContactEquation的relaxation,指當形狀之間重疊後,在一次碰撞模擬計算過程當中實施排斥力的次數,能夠想象成粘稠度,取值大時剛體速度衰減快;sufaceVelocity是兩個剛體接觸時,接觸面方向上兩個剛體的相對速度,若是其中一個剛體靜止,則sufaceVelocity表示另外一個剛體的速度,經常使用來模擬傳送帶效果。
4)形狀貼圖:
遊戲中,物理對象須要對應的遊戲圖像素材來呈現,須要使用圖片對剛體形狀貼圖。貼圖的過程,其實是根據物理模擬後剛體的信息,實時更新圖片素材的座標和角度。步驟爲:
⑴保存剛體與素材圖片的對應關係:
Egret遊戲中的圖片素材,一般是經過RES對象加載進來,須要爲剛體添加一個自定義屬性,用來保存對應素材的引用。示例:
body.userData=image;
⑵遍歷world中全部剛體,查找擁有自定義素材屬性的剛體:
world中全部的剛體都保存在bodies數組中,經過數組的foreach()方法,能夠遍歷其中的每個body,若是定義的body.userData屬性不爲null,則進行貼圖:
this.world.bodies.forEach(function(body:p2.Body){
if(body.userData!=null){
//更新素材座標和角度
}
}
⑶根據剛體信息,實時跟新對應圖像素材的座標和角度:
在ENTER+FRAME事件處理函數中,將剛體座標和角度屬性賦值給素材,實時更新》
body.userData.x=body.positon[0]*this.factor;
body.userData.y=body.positon[1]*this.factor;
body.userData.rotation=body.angle*180/Math.PI;
Egret加載的圖片,原點默認爲左上角,而剛體的原點處於其重心,根據形狀不一樣,重心位置也不一樣。在更新素材圖片前,須要根據剛體重心的座標偏移量(offsetX,offsetY)設置圖片的anchorOffsetX和anchorOffsetY屬性。示例代碼:
private bindAsset(body:p2.Body, assetName:string):void{
var offset:number[]=[];
body.updateAABB();
var bodyWidth:number=body.aabb.upperBoumd[0]-body.aabb.lowerBound[0];
var bodyHeight:number=body.aabb.upperBoumd[1]-body.aabb.lowerBound[1];
var asset:egret.Bitmap=new egret.Bitmap();
asset.texture=RES.getRes(assetName);
asset.scaleX=bodyWidth*this.factor/asset.width;
asset.scaleY=bodyHeight*this.factor/asset.height;
this.addChild(asset);
p2.vec2.subtract(offset, body.position, body.aabb.lowerBound);
asset.anchorOffsetX=offset[0]/assets.scaleX*this.factor;
asset.anchorOffsetY=offset[1]/assets.scaleY*this.factor;
body.userData=asset;
}
經過body.updateAABB()更新剛體的aabb屬性,而後才能獲取到正確的upperBoumd和lowerBound數組的值。經過向量的subtract()方法,計算剛體座標position,是相對於剛體最小包圍盒左上角aabb.lowerBound的偏移量,保存在offset變量中。代碼中,按照縮放比例將offset偏移量賦值asset的anchorOffsetX和anchorOffsetY屬性,調整圖片控制點,實現圖片與剛體的徹底貼合。
4. 剛體屬性:
P2能夠建立多種形狀的剛體,剛體除了形狀外還有各類屬性,有數十種,大概分爲4類,速度相關、角度相關、對象相關,還有其餘屬性。
1)速度相關屬性:
⑴position:number[]=[0,0];
表示剛體在全局座標系的位置,是一個二維向量Vec2,默認[0,0]。任何物體都須要經過position屬性進行精肯定位和移動,p2在進行物理模擬時會自動更新剛體位置座標,也能夠在須要時直接修改其屬性值:body.position=[100,100];
⑵velocity:number[]=[0,0];
表示剛體的線性速度,單位像素/秒,是一個二維向量。P2使用包含兩個元素的數組表示速度,第1個元素表示x份量,第2個元素表示y份量。
⑶damping:number=0;
表示線性速度阻尼,也稱速度衰減係數,取值在0~1之間,默認0。假設剛體當前線速度爲v,通過一次模擬計算後,受阻尼影響,剛體線性速度變爲v*(1-damping),也就是剛體的線速度會隨時間的以1-damping爲倍數降低。damping能夠用來模擬剛體與地面之間的摩擦力。
⑷fixedX:boolean=false;
設定是否固定剛體的x座標,默認false。當設置爲true時,剛體只能在y軸方向上下移動。
⑸fixedY:boolean=false;
設定是否固定剛體的y座標,默認false。當設置爲true時,剛體只能在x軸方向左右移動。
設定了fixedX及fixedY屬性後,須要調用updateMassProperties()方法,才能使其發揮做用。
⑹force:number[]=[0,0];
表示剛體當前受到的做用力大小,是一個二維向量。P2經過剛體的force來模擬外力的做用,施加的外力做用於剛體的中心位置,不會引發旋轉。若是模擬施加一個外力到物體頂部而推倒物體,P2經過Body類的applyForce()方法,能夠自定義外力做用的位置,模擬相似旋轉效果。
由於world調用step()方法後,force屬性將被置零,若是須要模擬持續施加做用力,須要在step()方法前持續設置force屬性。
⑺gravityScale:number=1;
設置當前剛體的重力加速度縮放比例,默認爲1。若是當前重力加速度爲gravity=[x,y],那麼設置gravityScale後,重力加速度爲[x*gravityScale, y*gravityScale]。
若是設置gravityScale爲0,當前剛體將不受重力加速度影響;若gravityScale設爲負值,那麼剛體將沿重力加速度反方向移動。
2)角度相關屬性:
⑴angle:number=0;
表示剛體角度,爲弧度值,正值增大爲順時針旋轉。
⑵angularVelocity:number=0;
表示剛體角速度,即旋轉速度,默認爲0。若是angularVelocity>0,剛體順時針旋轉;若是angularVelocity<0,剛體逆時針旋轉。angularVelocity單位是弧度/秒,由於P2更新step()方法是每幀調用,須要轉換成弧度/幀。假設遊戲幀頻爲fps,角速度爲a1,轉換後的每幀弧度爲:a2=a1/fps。
⑶angularDamping:number=0.1;
表示剛體角速度阻尼,取值範圍0~1,默認0.1。假設當前角速度爲r,P2進行一次模擬計算後,受阻尼影響,剛體角速度將變爲r*(1-damping)。
⑷angularForce:number=0;
表示剛體在角速度方向上受到扭力大小,單位N,默認0。angularForce做用的結果是使剛體旋轉,取值越大,剛體角速度變化越大。若是angularForce>0,扭力會促使剛體順時針旋轉;若是angularForce<0,扭力使剛體逆時針旋轉。實際上,在剛體上施加angularForce扭力後,造成一個旋轉加速度a,單位弧度/平方秒,若是剛體質量爲m,它們之間的關係爲:
a=angularForce/m
也即剛體加速度以每秒弧度爲a的變化量遞增。若是使用角度/幀爲單位,公式相應轉換爲:
a=dt*(angularForce/m)*(180/Math.PI)
⑸fixedRotation:boolean=false;
設置是否固定剛體角度,默認false。當fixedRotation爲true時,剛體角度不會因碰撞或運動而發生變化,好比保持人一直處於垂直站立狀態。設置fixedRotation屬性後,也須要調用updateMassProperties()方法才能起做用。
3)對象相關屬性:
⑴shapes;Shape[];
表示剛體中包含的全部形狀,均保存在這個shapes數組中。經過遍歷數組,能夠對剛體中的每一個形狀單獨進行操做。若是要計算剛體總體形狀的面積,就須要遍歷shapes屬性中的全部形狀,經過area屬性,將這些形狀的面積累加起來,獲得總體形狀的面積,代碼爲:
var totalArea=0;
for(var i=0; i<this.shapes.length;i++){
titalArea+=this.shapes[i].area;
}
這個功能實際上已經集成到了Body類的getArea()方法中,能夠直接調用這個方法。
⑵world:World;
表示剛體當前所在的world。一般狀況下,一個P2應用中只有一個world世界,但須要時也能夠建立多個world。不管是一個仍是多個world,均可以經過world屬性獲取剛體所在的world,而後進行addBody()、Raycast()等操做。
4)其餘屬性:
⑴id:number;
表示剛體的惟一標識。P2中的每個剛體都是惟一的,在碰撞檢測等過程當中,能夠經過id屬性來對指定的剛體進行識別。
⑵type:number;
剛體類型。P2中可用的剛體類型有3種,靜態剛體、可動剛體、動態剛體,默認爲靜態剛體。type的屬性值爲3個常量:
·靜態剛體Body.STATIC:靜態剛體在world中始終保持靜止,不受重力影響,其座標、角度不會由於碰撞而發生變化,通常使用綠色表示靜態剛體。
·可動剛體Body.KINEMATIC:這種剛體是可動的,但不受重力影響,其座標、角度不會由於碰撞而發生變化。所謂可動,指可經過設置剛體的velocity或angularVelocity等屬性,使其動起來,常使用紫色表示。
·動態剛體Body.DYNAMIC:是最經常使用的類型,在重力做用下可進行自由落體運動,經過velocity、angularVelocity、force等屬性可以使其動起來;當碰撞發生時,速度和角度相應發生變化,進行物理碰撞模擬,經常使用粉色表示。
⑶mass:number;
表示剛體的質量。P2中的剛體,沒有密度density屬性,mass充當了密度角色,默認爲0,此時剛體爲靜態剛體。當mass>0,剛體類型自動轉換爲動態剛體,數值越大,慣性越大。
⑷ccdIterations:number=10;
當剛體高速運動時,P2會在一次step()中進行連續屢次碰撞檢測,ccdIterations即表示這個檢測次數,默認爲10。ccdIterations越大,碰撞模擬越精確,但計算效率也下降。
⑸ccdSpeedThreshold:number=-1;
只CCD碰撞檢測的最低速度,即速度超過ccdSpeedThreshold門限時,P2將對剛體進行連續碰撞檢測,默認爲-1,此時不進行連續碰撞檢測。
⑹collisionResponse:boolean=true;
指定當與其餘剛體發生碰撞時當前剛體是否會進行碰撞模擬,默認爲true。若是設爲false,則碰撞檢測發生時,當前剛體不進行碰撞模擬,會穿過剛體,但此時仍會觸發碰撞事件,併產生ContactEquation。
⑺allowSleep:boolean=true;
指定是否容許剛體進入睡眠狀態,默認true。正常狀況下,P2會遍歷world中的每一個剛體,對其進行紋理模擬計算。當剛體的速度爲0,處於靜止狀態時,剛體會進入睡眠狀態,P2將再也不對其進行物理模擬計算,以提高效率。這個屬性起做用,world的allowSleeping= World.BODY_SLEEPING。
⑻sleepSpeedLimit:number=0.2;
爲剛體進入睡眠狀態時的最小速度,默認0.2,即當剛體速度小於sleepSpeedLimit時,剛體進入睡眠狀態。其前提是剛體的allowSleep爲true。
⑼sleepState:number=Body.AWAKE;
爲剛體當前睡眠狀態,默認爲喚醒狀態。剛體睡眠狀態有3種:
·Body.AWAKE:剛體處於喚醒狀態,P2對剛體進行正常的物理模擬計算,更新其屬性
·Body.SLEEPY:當剛體的速度爲sleepSpeedLimit,剛體進入SLEEPY瞌睡狀態,該狀態下P2也對剛體進行物理模擬計算
·Body.SLEEPING:當剛體進入SLEEPY狀態超時時間sleepTimeLimit,才進入SLEEPING睡眠狀態。此時,P2在遍歷全部剛體,將經過當前剛體,不對其進行物理模擬計算。
⑽sleepimeLimit:number=1;
爲剛體從瞌睡Body.SLEEPY狀態進入睡眠狀態Body.SLEEPING狀態須要的時間,默認爲1秒。
⑾idleTime:number;
剛體已經進入睡眠Body.SLEEPING狀態的時長。
5. 剛體操做:
1)addBody和removeBody:
World類中的addBody()和removeBody()分別用來上P2世界添加和刪除剛體。全部建立好的剛體,必須經過addBody()添加到P2世界中,才能夠進行碰撞模擬:
var body:p2.Body({mass:1, position:[1,1]});
this.world.addBody(body);
當物體被子彈擊中,或超出屏幕範圍時,須要刪除剛體,能夠經過removeBody()將其從P2世界中刪除,同時還能夠避免沒必要要的計算。示例代碼:
this.world.removeBody(bodyToRemove);
2)addShape和removeShape:
Body類中的addShape()和removeShape()分別用來向剛體中添加和刪除形狀。使用addShape():
var shape:p2.Rectangle=new p2.Rectangle(100,50);
var body:p2.Body=new p2.Body({mass:1, position:[1,1]);
body.addShape(shape);
其實,addShape()中還有其餘參數,構造函數爲:
function addShape(shape:p2.Shape, offset:number, angle:number)
其中參數除了被添加的形狀shape,還有兩個參數,offset爲形狀相對於座標原點的偏移量,angle爲形狀的角度,這兩個參數都是相對於剛體本地座標系而言,即會隨剛體的變化而變化。
能夠經過removeShape()方法將指定的形狀從剛體中刪除。
形狀的添加或刪除,並不會改變剛體的重心座標,所以可用來模擬不倒翁效果。
3)adjustCenterOfMass:
調整重心位置。剛體中增長或移去形狀後,重心並不會自動改變,可使用adjustCenterOf Mass()方法,使剛體重心從新回到中心位置。這個方法不帶任何參數,也沒有返回值。
4)applyForce:
做用力可讓剛體狀態改變,經過applyForce()方法,能夠在指定點worldPoint對剛體施加一個做用力,造成一個加速度或扭力,改變剛體的線速度或角速度。構造函數爲:
function applyForce(force:number[], worldPoint:number[])
其中,force是要施加的做用力,這是一個二維向量; worldPoint是一個全局座標點,表示force在剛體上的做用點,當此點不在剛體重心位置時,剛體角度也會發生變換。
5)applyImpulse:
若是要瞬間改變剛體的狀態,如子彈彈出膛效果,須要使用applyImpulse()對其施加衝量,使剛體的速度和角速度瞬間發生變化。
function applyImpulse(impulse:number[], relativePoint:number[])
其中,impulse爲要施加的衝量,衝量有大小和方向,是一個二維向量; relativePoint是一個本地座標點,表示衝量的做用點,看成用點不在剛體中心時剛體角度也會發生變化。
6)sleep和wakeup:
Body的sleep()方法強制使剛體進入睡眠狀態,此時除了sleepState處於Body.SLEEPING狀態外,剛體的速度、角速度、受到的做用力、扭力等都會所有清零。
function sleep(){
this.sleepState=Body.SLEEPING;
this.angularVelocity=0;
this.angularForce=0;
vec2.set(this.velocity, 0, 0);
vec2.set(this.force, 0, 0);
this.emit(Body.sleepEvent);
};
剛體進入睡眠狀態後,經過調用wakeup()方法,能夠將剛體強制喚醒,但僅僅是將sleepState設置爲Body.AWAKE,睡眠狀態前的速度、角度等屬性不會恢復:
function wakeup(){
var s=this.sleepState;
this.sleepState=Body.AWAKE;
this.idleTime=0;
if(s!==Body.AWAKE){
this.emit(Body.wakeUpEvent);
}
}
可使用sleep()方法實現相似冰凍效果。
7)emit、on、off、has:
P2物理引擎中,經過EventEmitter類實現事件派發機制,並經過emit()、on()、off()、has()方法分別實現派發、監聽、取消監聽,以及檢測是否包含指定事件功能。
⑴emit():派發自定義事件,事件類型使用任意字符串表示:
function emit(event:Object)
其中,event爲要派發的自定義事件,是一個Object類型,用於包含任何須要經過事件傳遞的信息。在自定義事件時,event對象至少要包含名爲type的字符串屬性值,表示事件名稱:
body.emit({type:"movingUp"});
⑵on():監聽對應的自定義函數
監聽到事件對象,將做爲參數傳遞給事件監聽函數,示例代碼爲:
var onMyEvent=function(event){
console.log("onMyEvent is fired, because I am moving up.");
};
body.on("movingUp", onMyEvent);
on()方法的參數是字符串類型的事件名稱,並在事件觸發時調用一個處理函數。
⑶off():取消監聽
當不須要監聽某個事件時,將事件名稱和事件監聽函數做爲參數傳遞給off()方法,就能夠取消監聽。示例代碼:
body.off("movingUp", onMyEvent);
⑷has():檢查對象是否包含指定的事件監聽
Body類由於繼承了EventEmitter類,因此也擁有上面4個方法。經過這些方法,能夠方便地對剛體的某些特定狀態進行監聽,如睡眠或甦醒。Body類內置了睡眠和甦醒事件:
·sleepEvent:當剛體進入睡眠狀態時派發該事件,對應事件名稱爲sleep,監聽使用body. on("sleep", onMyEvent);
·wakeupEvent:當剛體從睡眠狀態恢復至甦醒狀態時,派發wakeupEvent事件,事件名爲wake,監聽使用body.on("wake", onMyEvent);
·sleepyEvent:當剛體進入瞌睡狀態時,派發該事件。Body有一個sleepSpeedLimit屬性,當剛體的速度小於sleepSpeedLimit值時就進入瞌睡狀態;若是該狀態持續時間超過body. sleepTimeLimit,則剛體進入睡眠狀態。sleepyEvent對應的事件名爲sleepy,監聽使用body. on("sleepy", onMyEvent);
除了上述內置事件,還能夠自定義事件,好比在step()方法中持續判斷body.velocity.y是否小於0,來檢測剛體向上運動,派發自定義moveUp事件:
public loop():void{
this.world.step(60/1000);
this.debugDraw.drawDebug();
if(this.bodyRef.velocity[1]<0){
this.bodyRef.emit({type:"myEvent"});
}
}
8)fromPolygon:
fromPolygon用於將多邊形分解成一個個小的形狀,而後組合成完整的多邊形。
function fromPolygon(path:number[][], [options]):boolean
其中,path保存了多邊形頂點數組,options爲可選屬性,定義多邊形分解的相關設置。
options包括的選項有:
·optimalDecomp=false:是否進行最佳分解,默認false,開啓該選項會下降計算速度
·skipSimpleCheck=false:是否進行頂點交叉的判斷,若是肯定不存在交叉點可設爲true
·removeCollinearPoints=false:是否剔除共線頂點,false表示不剔除
若是多邊形建立成功返回true,不然返回false。致使建立失敗有幾種緣由,好比建立的形狀中有空洞、頂點和邊之間有交叉。能夠編程將鼠標光標移動的軌跡中的點做爲多邊形的頂點。也就是隨手建立剛體。
9)hitTest:
用於實現指定座標點與剛體的碰撞檢測,並將檢測到的碰撞剛體保存到數組中返回。
function hitTest(worldPoint:number[][], bodies:Body[], precision:number):Body[]
其中,worldPoint爲要檢測的座標點,是一個全局座標點;bodies爲要檢測的剛體清單,能夠將須要檢測的剛體保存到bodies數組中,實現有針對性的碰撞檢測。若是要對全部的剛體進行檢測,能夠直接設置該參數爲world.bodies,即:hitTest(worldPoint, world.bodies);
參數precision爲檢測精度,默認爲0,取值越大檢測精度越高,計算效率會下降,通常使用默認值,對尺寸極小的物體,如particle和line須要設置。方法的返回值爲保存了與worldPoint發生碰撞全部剛體的數組。
hitTest()最經常使用用於檢測鼠標光標點擊、拖曳效果。P2中沒有鼠標光標事件,能夠經過hitTest()來判斷鼠標點是否與剛體發生碰撞,而後調整剛體的position至鼠標位置,實現對剛體的拖曳。爲了便於拖曳,在鼠標點擊時經過sleep()方法將剛體設爲睡眠狀態,使其不受重力影響,能夠精確地隨鼠標光標移動,當拖曳完成彈起時再使用wakeUp()方法從新喚醒剛體,恢復重力的做用。
10)getAABB:
獲取剛體的最小包圍盒AABB(Axis Align Bounding Box),最小包圍盒AABB是指包圍形狀的最小矩形框。能夠經過設置剛體的isDrawAABB=true,以顯示出剛體的AABB。AABB對象將矩形對象的左下角和右下角座標分別保存在屬性lowerBound和upperBound中。
11)getArea:
獲取剛體的當前全部形狀的面積總和。
12)setDensity:
設置剛體的密度,結構爲:
function setDensity(density:number)
其中,density爲要設置的密度,是取值大於0的數值。
實際上,P2中的剛體並無密度屬性,當調用setDensity()方法後,Body會根據剛體當前的面積area計算出對應的質量並保存在mass屬性中。
13)overlaps:
檢測當前剛體與指定剛體是否有重疊,並返回檢測結果,結構爲:
function overlaps(body:Body):boolean
其中,body爲要檢測的目標剛體。若是剛體之間有重疊則返回true,不然返回false。
目標剛體與被檢測剛體需添加到world中,才能確保overlaps()的正常運行。一般並不須要使用overlaps()進行重疊檢測,由於P2在實施碰撞檢測和模擬時已經完成了這些內容,只有當剛體不進行碰撞模擬時才須要使用overlaps()。好比在界面中放置一個物體,當此位置已經存在物體時不能放置。
14)toWorldFrame和toLocalFrame:
toWorldFrame()是將剛體本地座標系統中的座標點轉換成全局座標點,toLocalFrame()是將全局座標點轉換成剛體本地座標系統中的座標點。經過這兩個方法,能夠實現本地座標和全局座標之間的轉換。
function toWorldFrame(out:number[], localPoint:number[]):void
function toLocalFrame(out:number[],worldPoint:number[]):void
其中,out爲轉換後的本地座標或全局座標,將保存在該屬性對應的變量中; localPoint爲要轉換的本地座標;worldPoint爲要轉換的全局座標。
15)raycast:
射線投射技術用於實現線段與形狀的碰撞檢測,一般用來模擬人物的視野、距離探測等。實現射線投射,要先從起點from到終點to構建一條射線ray,而後檢測與該線段發生碰撞的剛體,並保存在一個RaycastResult對象中。raycast結構爲:
function raycast(result:RaycastResult, ray:Ray)
其中,result是保存了碰撞剛體、碰撞點、碰撞距離等信息的一個RaycastResult對象;ray是用於檢測的射線,是一個Ray類,包含起點from、終點to等屬性。使用射線投射,須要用到Ray類和RaycastResult類。
①Ray類:
Ray類的構造函數爲:
function Ray([options])
其中,options參數是一個Object對象,初始化Ray對象時,能夠保持options缺省,而後再經過Ray屬性進行設置。Options對象中包含一些參數:
·from:number[]:線段的起點,一個二維向量
·to:number[]:線段的終點,一個二維向量
·mode:number:射線的碰撞檢測模式,取值爲3個常量,分別爲Ray.ALL、Ray.CLOSEST、Ray.ANY。Ray.ALL模式下,raycast()函數會檢測全部與射線ray發生碰撞的剛體;Ray.CLOSEST模式下,返回檢測到的碰撞剛體中距離起點from最近的剛體;Ray.ANY模式下,當ray檢測到第1個碰撞剛體時會馬上中止檢測並返回該剛體,這時的第1個剛體與建立時的順序有關。
·callback:Function:回調函數,當raycast()檢測到碰撞剛體後會當即調用回調函數,該屬性只適用於rayCastAll()
·collisionMask:number:與collisionGroup配合使用,對檢測剛體進行篩選
·collisionGroup:number:與collisionMask配合使用,對檢測剛體進行篩選,只有知足條件的剛體才參與碰撞檢測,檢測條件爲:
(this.collisionGroup&body.collisionMask)&&(body.collisionGroup&this.collisionMask)==true
·skipBackface:是否忽略射線ray反方向與剛體的碰撞點,值爲false時只檢測正方向
·checkCollisionResponse:配合Body中的collisionResponse屬性,若是剛體的屬性collisionResponse和checkCollisionResponse同時爲true,則不對該剛體進行檢測。
·direction:射線方向,能夠缺省,取決於from和to屬性
·length:射線從起點from到終點to的間距
②RaycastResult類:
RaycastResult類用於保存射線與剛體的碰撞信息,包括幾個屬性:
·body:當前碰撞檢測中,與射線ray發生碰撞的剛體
·shape:當前碰撞剛體中,與射線ray發生重疊的形狀
·fraction:射線起點from到碰撞點之間的距離distance與射線長度length的比例
·normal:垂直於射線碰撞邊的法向量,只讀屬性,默認-1
RaycastResult類還有一些方法:
·hasHit():是否檢測到與射線ray發生碰撞的剛體,當檢測到時返回true
·getHitDistance():當檢測到與射線發生碰撞的剛體時,碰撞點與射線起點from間的距離
function getHitDistance(ray:Ray):number
·getHitPoint():獲取碰撞點位置,返回結果是一個全局座標點
此方法的結構爲function getHitPoint(out:number[], ray:Ray):void
其中,out爲用於保存碰撞點座標的二維向量,ray爲當前進行碰撞檢測的射線。
·stop():用於當即中止碰撞檢測
由於raycast()方法中的RaycastResult會重複使用,因此回調函數中獲取到的是最後一次碰撞檢測信息。
6. 碰撞處理:
P2能夠實現物體碰撞模擬,同時在碰撞過程當中派發一些事件實現碰撞檢測,將碰撞信息及時反饋,以添加相應的特效。
P2中,當兩個剛體的最小包圍盒AABB發生重疊,碰撞就開始了;而後剛體的形狀發生重疊,同時P2會對重疊進行修復,使剛體朝對方的反方向移動,來消除形狀重疊;當形狀再也不有重疊時,整個碰撞過程結束。能夠把碰撞過程分爲4個階段:
·postBroadphase:AABB開始發生重疊,但形狀並無發生接觸
·beginContact:剛體形狀開始發生重疊,剛體繼續保持原有速度移動
·preSolve:剛體形狀發生了重疊,但P2還未進行碰撞處理
·endContact:P2已經完成了碰撞處理,併爲碰撞剛體從新分配了速度,同時剛體形狀分離,再也不有重疊
1)碰撞事件:
碰撞過程會產生4個事件,postBroadphase、preSolve、beginContact、endContact。在碰撞事件派發後,能夠經過world類的on方法來監聽事件,並在監聽處理函數中進行對應的處理。on方法的結構爲:
function on(type:String, listener:functtion)
其中,type爲監聽事件名,爲一個字符串,取值爲對應的事件名稱;listener爲事件監聽函數,當監聽到碰撞事件後,會自動調用監聽函數,並將碰撞事件對象做爲參數傳遞給監聽處理函數。碰撞監聽函數的參數event是一個Object對象,對應碰撞事件對象,其中保存碰撞信息。
在on事件處理函數中,this指的是派發事件的對象context,也就是world對象,而再也不是主類中的this。所以,須要在on函數以前,將this指針保存到一個局部變量中,而後才能在事件處理函數中經過這個局部變量訪問主類中的變量和方法。
on()方法監聽碰撞事件時,會將對應的碰撞事件對象做爲參數傳遞給監聽處理函數,這些碰撞事件對象爲:postBroadphaseEvent、preSolveEvent、beginContactEvent、endContactEvent。
①postBroadphaseEvent:
當兩個剛體的AABB發生重疊時,不斷派發postBroadphase事件,並將碰撞信息保存到對應的postBroadphaseEvent對象中,該對象包含屬性pairs,即保存碰撞剛體對的數組。由於還沒有進行剛體形狀的碰撞檢測,因此此時對pairs數組中的碰撞進行刪減能夠取消對應碰撞剛體之間的碰撞模擬。但刪除pairs後,將再也不進行beginContact和endContact事件派發。
②preSolveEvent:
在剛體形狀發生重疊時,P2會根據動量守恆定律,對碰撞對象的速度和角度從新計算,實現碰撞模擬,這個過程稱爲solve。preSolve事件在碰撞模擬過程前派發,在preSolve事件處理函數中進行一些處理能夠取消或干預碰撞的模擬。
preSolveEvent對象的屬性有:
·contactEquations:保存當前碰撞產生的全部contactEquation對象的數組
·frictionEquations:保存當前碰撞產生的全部frictionEquation對象的數組
preSolve事件會隨step()方法不斷派發,在剛體形狀之間沒有重疊前,contactEquations屬性中並不包括contactEquation對象,因此代碼中要先進行判斷:contactEquations.length>0。
③beginContactEvent:
當剛體形狀發生重疊時會派發beginContact事件,並將碰撞信息保存在對應的beginContact Event對象中,其中屬性有:
·shapeA:發生碰撞的形狀A
·shapeB:發生碰撞的形狀B
·bodyA:形狀shapeA對應的剛體
·bodyB:形狀shapeB對應的剛體
·contactEquations:保存當前碰撞產生的全部contactEquation對象的數組
④endContactEvent:
當前兩個碰撞剛體的形狀分離而再也不重疊時會派發endContact事件,並將碰撞信息保存在對應的endContactEvent對象中,包含的屬性有:
·shapeA:發生碰撞的形狀A
·shapeB:發生碰撞的形狀B
·bodyA:形狀shapeA對應的剛體
·bodyB:形狀shapeB對應的剛體
2)碰撞信息Equation:
在P2引擎中,Equation類用來保存碰撞發生時的碰撞點、碰撞向量等信息。通常用其兩個子類ContactEquation和FrictionEquation來保存不一樣類型的信息。
①ContactEquation:
碰撞過程當中,因接觸而產生的碰撞信息,如碰撞點、碰撞向量、碰撞剛體等,都保存在ContactEquation對象中。在preSolve和beginContact階段,會產生多個ContactEquation對象,並分別保存在preSolveEvent和beginContactEvent事件對象的ContactEquation屬性中。
·shapeA:發生碰撞的形狀A
·shapeB:發生碰撞的形狀B
·bodyA:形狀shapeA對應的剛體
·bodyB:形狀shapeB對應的剛體
·contactPointA:自bodyA的座標起,到碰撞點的全局向量
·contactPointB:自bodyBA的座標起,到碰撞點的全局向量
·enabled:是否對當前的ContactEquation對象進行碰撞模擬,默認true,若是在preSolve階段將其設爲false能夠取消碰撞模擬
·firstImpact:是否爲第1次碰撞,當第1次碰撞step()完成後,firstImpact當即設爲false
·normalA:垂直於剛體碰撞邊的法向量,這是一個全局的單位向量
·restitution:碰撞剛體之間的碰撞彈性係數,取值0~1。該係數隻影響當前碰撞的模擬,若是對shapeA和shapeB設置contactMaterial,其restitution屬性不受影響。
②FrictionEquation:
碰撞過程當中,因剛體相對運動而產生的摩擦等碰撞信息,保存在FrictionEquation對象中。在碰撞的preSolve階段會產生FrictionEquation對象,並保存在preSolveEvent事件對象的FrictionEquation屬性中。FrictionEquation對象包含的屬性有:
·shapeA:發生碰撞的形狀A
·shapeB:發生碰撞的形狀B
·bodyA:形狀shapeA對應的剛體
·bodyB:形狀shapeB對應的剛體
·frictionCoefficient:碰撞時剛體之間的碰撞係數,取值越大速度衰減越快,隻影響當前碰撞模擬。若是有對shapeA和shapeB設置contactMaterial,其friction屬性不受影響。
·t:碰撞邊的切線方向向量
須要注意,frictionCoefficient屬性須要在world.solver.frictionIterations>0狀況下才起做用,frictionIterations默認是0,須要在建立world時設置其值。
7. 關節:
P2中使用Constraint及其子類表示關節,也就是將兩個剛體按照指定的規則約束在一塊兒,造成有規律的、相互限制的運動模擬。P2關節模擬中,兩個剛體沒有經過任何剛體鏈接,只是經過算法模擬出關節運動軌跡。爲了更加直觀,p2DebugDraw類中使用黑色的線段表示鏈接剛體的連桿,黑點圓的表示關節節點anchor。
P2中關節有5種,每一種都有獨特的約束規則,包括距離關節DistanceConstraint、齒輪關節GearConstraint、鎖定關節LockConstaint、位移關節PrismaticConstraint、旋轉關節Revilute Constraint。
1)距離關節DistanceConstraint:
按照指定的距離distance將兩個剛體約束在一塊兒,其中任何一個剛體的位置發生變化,會牽着另外一個剛體運動,以保證二者的間距爲distance。可是兩個剛體的角度不受約束,能夠繞着節點旋轉。DistanceConstraint構造函數爲:
function DistanceConstraint(bodyA:Body, bodyB:Body, options:object)
其中,bodyA和bodyB爲受約束的剛體,options爲關節設置選項,能夠缺省,P2以默認值設置,其中選項爲:
·distance:兩個剛體受到約束時保持的間距,默認爲添加關節時兩個剛體之間的間距
·localAnchorA:關節點相對於剛體bodyA本地座標系統的座標系,默認[0,0]
·localAnchorB:關節點相對於剛體bodyB本地座標系統的座標系,默認[0,0]
·maxForce:剛體運動中,若是距離不等於distance,爲保持距離而對剛體施加的最大做用力,默認爲Number.MAX_VALUE
除了上面的構造函數中的參數,距離關節還包含一些屬性:
·lowerLimit:設置距離關節約束範圍下限,即bodyA到bodyB的距離最小值,默認爲0,該屬性必須大於0。只有當lowerLimitEnabled爲true時才起做用。
·lowerLimitEnabled:是否設置距離關節約束範圍下限,默認false。
·upperLimit:設置距離關節約束範圍上限,即bodyA到bodyB的距離最大值,默認爲0,該屬性必須大於0。只有當upperLimitEnabled爲true時才起做用。
·upperLimitEnabled:是否設置距離關節約束範圍上限,默認false
·position:bodyA和bodyB的當前間距
能夠經過joint.collideConnected屬性爲true,避免平臺和車輪之間的碰撞。建立完成後須要使用world的addConstraint(joint)方法將關節加入世界。
2)齒輪關節GearConstraint:
按照指定的比例ratio,將兩個剛體的角度angleA和angleB約束爲angle=angleB*ratio。其中任何一個剛體的角度變換,都會牽着另外一個剛體的角度變化,以確保兩個剛體角度的比例爲ratio。剛體的座標位置不受約束,能夠自由向任意方向移動。構造函數爲:
function GearConstraint(bodyA:body, bodyB:Body, options:Object)
其中,bodyA和bodyB爲受關節約束的兩個剛體,options爲關節設置選項,能夠缺省,P2會按默認值進行設置。選項爲:
·angle:兩個剛體的相對角度差,齒輪關節會將一個剛體的角度減去該角度差後,再保證角度變化量的比例爲ratio
·ratio:兩個剛體的角度變化量的比例,當ratio=2時,bodyB旋轉180°,bodyA只轉90°
·maxForce:當兩個剛體的角度比例不是ratio時,爲將其約束爲ratio而對剛體施加的最大扭力
齒輪關節還有兩種方法:
·setMaxForce(force):當bodyB的角度偏離angle,齒輪關節對bodyB施加的最大扭力
·getMaxForce():number:獲取setMaxForce()中設置的最大做用力
3)鎖定關節LockConstraint:
將兩個剛體綁定在一塊兒,使其相對座標位置、角度差保存不變,彷彿被釘在一塊兒。此關節中的任何剛體座標或角度發生變化,都會牽着另外一個剛體的座標和角度變化,以確保兩個剛體相對座標和角度分別爲localOffsetB和localAngleB。構造函數:
function LockConstraint(bodyA:Body, bodyB:body, options:Object)
其中,bodyA和bodyB爲受關節約束的兩個剛體,options爲關節設置選項,能夠缺省,P2會按默認值進行設置。選項爲:
·localOffsetB:剛體bodyB在關節約束下,相對於bodyA本地座標系的偏移量,默認爲添加關節時兩個剛體的相對位置
·localAngleB:剛體bodyB在關節約束下,相對於bodyA本地座標系統的角度,默認時爲添加關節時兩個剛體的相對角度
·maxForce:當兩個剛體未達到關節約束的localOffsetB和localAngleB,爲使其達到約束指定狀態,而能夠施加的最大做用力,默認Number.MAX_VALUE
LockConstraint還包含幾個方法:
·setMaxForce(force):當bodyB的位置偏離localOffsetB,或角度差不等於localAngleB時,鎖定關節對bodyB施加的最大做用力。
·getMaxForce():number:獲取setMaxForce()中設置的最大做用力
4)位移關節PrismaticConstraint:
將剛體bodyB的運動方向,限定爲在剛體bodyA本地座標系統中的一個指定向量。構造函數爲:
function PrismaticContraint(bodyA:Body, bodyB:Body, options:Object)
其中,bodyA和bodyB爲受約束的剛體,options爲關節設置選項,能夠缺省,P2以默認值設置,其中選項爲:
·maxForce:當bodyB相對於bodyA的位置偏離localAxisA時,爲使其恢復到約束位置,能夠施加的最大做用力,默認爲Number.MAX_VALUE
·localAnchorA:控制點anchorA在bodyA本地座標系下的座標,默認[0,0]
·localAnchorB:控制點anchorB在bodyB本地座標系下的座標,默認[1,0]
·localAxisA:剛體受到約束時只能夠在該座標軸方向上移動,這是剛體bodyA座標系下的一個向量,默認[1,0]
·disableRotationalLock:是否禁止bodyB繞節點旋轉,默認false,即bodyB不能自由旋轉,此值只有在構造函數中設置才起做用。
·upperLimitEnabled:是否開啓bodyB移動方向上限,默認false,此時能夠沿localAxisA正方向無限移動
·upperLimit:設置bodyB沿localAxisA正方向能夠移動的最大距離,默認爲1
·lowerLimitEnabled:是否開啓bodyB移動方向下限,默認false,此時能夠沿localAxisA負方向無限移動
·lowerLimit:設置bodyB沿localAxisA負方向能夠移動的最大距離,默認爲0,該屬性值要小於upperLimit
除了上述在構造函數中的參數,PrismaticConstraint還有其餘一些屬性:
·motorEnabled:是否開啓馬達屬性,與motorSpeed配合使用。開啓後,關節會對bodyB施加做用力,使其線速度達到motorSpeed,並在約束範圍內一直保持該速度。開啓或關閉馬達屬性,要用enableMotor()和disableMotor()方法。
·motorSpeed:開啓馬達屬性後,bodyB的目標速度值
·position:在localAxisA上,bodyB相對於bodyA的當前位置
PrismaticConstraint還有一些方法,用於調整關節的相關屬性:
·setLimits(lower, upper):設置位移關節的上下限,其中lower必定要小於upper
·disableMotor():關閉馬達屬性
·enableMotor():開啓馬達屬性
能夠建立一個空剛體來固定關節。所謂空剛體,就是沒有包含任何形狀對象的剛體,因此不會與任何剛體發生碰撞模擬。
5)旋轉關節RevoluteConstraint:
限制兩個剛體只能繞指定的控制點旋轉,該控制點是剛體bodyA本地座標系下的座標。其中一個剛體的位置或角度發生變化時,爲了確保控制點和剛體的相對位置不變,另外一個剛體也會被牽制發生位置和角度的變化。構造函數爲:
function RevoluteConstraint(bodyA:Body, bodyB:Body, options:Object)
其中,bodyA和bodyB爲受關節約束的兩個剛體,options爲關節設置選項,能夠缺省,P2會按默認值進行設置。選項爲:
·worldPivot:全局座標系下的關節節點,bodyA和bodyB均受約束,只能繞該節點旋轉。設置該節點後,旋轉關節會自動計算localPivotA和localPivotB本地節點。
·localPivotA:節點worldPivot在bodyA剛體本地座標系統下的座標,默認[0,0]
·localPivotB:節點worldPivot在bodyB剛體本地座標系統下的座標,默認[0,0]
·maxForce:當剛體座標偏離節點時,爲使其恢復到節點位置,能夠施加的最大做用力,默認爲Number.MAX_VALUE
RevoluteConstraint還包含幾個方法:
·setLimits(lower:number, upper:number):設置bodyB繞節點旋轉角度的上下限,值爲弧度
·enableMotor():開啓馬達屬性,與setMotorSpeed()配合使用,關節會對bodyB施加扭力,使其達到setMotorSpeed()
·disableMotor():關閉馬達屬性
·setMotorSpeed(speed):設置bodyB的目標角速度,只有開啓馬達屬性後才其做用。
·getMotorSpeed():number:讀取馬達的當前速度
旋轉關節經常使用於模擬小車運動。
8. 彈簧Spring:
P2中用來約束剛體運動的還有彈簧Spring。彈簧除約束兩個剛體之間的運動軌跡外,經過damping阻尼和stiffness剛度係數等屬性,使得剛體在向目標移動時,出現相似彈簧的簡諧運動。Spring只是抽象的父類,參與運動模擬的是兩個子類LinearSpring和RotationalSpring。
1)LinearSpring:
LinearSpring是線性彈簧,對剛體的約束行爲和距離關節DistanceConstraint相同,按照指定的距離restLength將兩個剛體約束在一塊兒,其中任何一個剛體的位置發生變化,會牽制着另外一個剛體運動,以保證二者的間距爲distance。在運動過程當中,剛體bodyB呈現簡諧運動。兩個剛體的角度不受約束,能夠繞節點旋轉。構造函數:
function LinearSpring(bodyA:Body, bodyB:Body, options:Object)
其中,bodyA和bodyB爲受彈簧約束的兩個剛體,options爲關節設置選項,能夠缺省,P2會按默認值進行設置。選項爲:
·stiffness:彈簧的剛度係數,默認100。
·damping:彈簧作簡諧運動過程當中的阻尼係數,默認1
·restLength:彈簧不受力狀態下的長度,默認爲worldAnchorA和worldAnchorB間的距離
·localAnchorA:剛體bodyA本地座標系下的節點座標,默認[0,0]
·localAnchorB:剛體bodyB本地座標系下的節點座標,默認[0,0]
·worldAnchorA:彈簧節點在全局座標系下的座標,設置後將自動轉換並覆蓋localAnchorA
·worldAnchorB:彈簧節點在全局座標系下的座標,設置後將自動轉換並覆蓋localAnchorB
2)RotationalSpring:RotationalSpring是扭力彈簧,對剛體的約束相似齒輪關節,按照指定的restAngle約束兩個剛體之間的角度差。當剛體的角度不等於restAngle時,bodyB會進行簡諧運動旋轉,直至角度差恢復至restAngle。兩個剛體的座標位置不受約束,能夠自由移動。 function LinearSpring(bodyA:Bodt, bodyB:Body, options:Object) 其中,bodyA和bodyB爲受彈簧約束的兩個剛體,options爲關節設置選項,能夠缺省,P2會按默認值進行設置。選項爲: ·restAngle:彈簧不受力無簡諧運動下剛體bodyA和bodyB間的角度差,默認爲建立扭力彈簧時兩個剛體之間的角度差 ·stiffness:彈簧的剛度係數,默認100。 ·damping:彈簧作簡諧運動過程當中的阻尼係數,默認1