【h5遊戲開發】egret引擎p2物理引擎(2) - 小球碰撞地面搞笑的物理現象
-
重力的方向和地面的問題
p2中默認的方向是從上到下,如果重力默認是正數的話,物體放到世界中是會從上面往下面飄的p2中plane地面默認的方向是y軸的方向,而在p2中y軸的方向默認是從上往下首先來看,重力
gravity是正數,並且plane地面在視圖中間,小球是怎麼運動// 創建世界 this.world = new p2.World() this.world.sleepMode = p2.World.BODY_SLEEPING this.world.gravity = [0,5] // 設置彈性屬性 this.world.defaultContactMaterial.restitution = 1; // 創建地面 this.planeBody = new p2.Body({ position:[egret.MainContext.instance.stage.stageWidth/2,egret.MainContext.instance.stage.stageHeight/2] }) let plane = new p2.Plane()

小球受重力的影響從上往下掉,沒有問題,並且即使是在地面下面,地面的方向也是從上往下,小球下到地面之後依舊是從上往下運動
下面把世界的重力改成從下往上,相當於漂浮的感覺

可以看到
plane下方,也就是在p2引擎中的y軸正方向地面上的小球依照物理定律,自由落地,從下往上落到地面上plane上方,也就是p2物理引擎中的地下世界,是會奇怪的往地面上跑總體來說:在
p2物理世界中,地面上的物體,遵循自由落體和牛頓引力相關運動原理,但是默認方向是從上往下的Y軸方向,所以如果地面在最下面的話,需要將plane地面水平翻轉180『,並且此時重力可以設置為正數,達到從上往下的效果正確的效果是這樣的:

實現程式碼
// 創建世界 this.world = new p2.World() this.world.sleepMode = p2.World.BODY_SLEEPING this.world.gravity = [0,5] // 設置彈性屬性 this.world.defaultContactMaterial.restitution = 1; // 創建地面 this.planeBody = new p2.Body({ position:[egret.MainContext.instance.stage.stageWidth/2,egret.MainContext.instance.stage.stageHeight/2] }) let plane = new p2.Plane() this.planeBody.angle = Math.PI // 地面需要旋轉180度,不然是從上往下 -
生成阻礙方塊,並且不允許方塊有重疊區域
實現原理:
縱坐標可以設定在同一水平線上,橫坐標位置隨機,通過隨機數生成位置,並且通過隨機數確定方塊的大小,生成第二個隨機方塊的時候判斷時候與前面生成的所有方塊有重疊部分` 半徑 只差的絕對值 < 半徑值和`, 如果有重疊部分的話,嵌套調用,如果沒有的話,即生成新的方塊實現程式碼:
private randomNum(minNum,maxNum):number{ // 生成隨機數的方法,借鑒自菜鳥教程 switch(arguments.length){ case 1: return parseInt(Math.random() * minNum+1 + '',10); break; case 2: return parseInt(Math.random()*(maxNum-minNum+1)+minNum,10); break; default: return 0; break; } } private genereteBox():void{ /** * 生成隨機個障礙盒子 */ let stageHeight = egret.MainContext.instance.stage.stageHeight // 獲取螢幕高度 let stageWidth = egret.MainContext.instance.stage.stageWidth // 獲取螢幕寬度 let count = this.randomNum(1,4) // 生成的障礙個數 let arr = [] let generatePosition = ()=>{ // 保證this指向問題 let size = this.randomNum(30,70) let location = this.randomNum(size,stageWidth-size) if(arr.length === 0){ arr.push({ size:size, location:location }) return [size,location] }else{ for(let index = 0;index<arr.length;index++){ if((Math.abs(location-arr[index].location)) < (size + arr[index].size)){ console.log(size,location) return generatePosition() } } arr.push({ size:size, location:location }) return [size,location] } } for(let index = 0;index<count;index++){ let [size,location] = generatePosition() let react:egret.Shape = this.createBox(size) let reactShape: p2.Shape = new p2.Box({ width:size, height:size }) let reactBody:p2.Body = new p2.Body({ type:p2.Body.STATIC, position:[ location,stageHeight-size ], }) reactBody.addShape(reactShape) reactBody.displays = [react] this.world.addBody(reactBody) } } -
點擊生成小球邏輯
實現邏輯
1.生成小球剛體,形狀,及綁定貼圖 2.生成小球的位置為點擊事件的位置 3.綁定到`egret`的點擊事件上實現程式碼
//步驟1,2 private generateBall(locationX:number,locationY:number):void{ var ball:egret.Shape var ballShape:p2.Shape var ballBody:p2.Body ball = this.createBall() ballShape = new p2.Circle({ radius:10 }) ballBody = new p2.Body({ position:[locationX,locationY], mass:50, overlaps:false }) ball.width = (<p2.Circle>ballShape).radius*2 ball.height = (<p2.Circle>ballShape).radius*2 ball.anchorOffsetX = ball.width/2 ball.anchorOffsetY = ball.height/2 ballBody.addShape(ballShape) ballBody.displays = [ball] this.world.addBody(ballBody) } // 步驟3 this.addEventListener(egret.TouchEvent.TOUCH_TAP,(e:egret.TouchEvent)=>{ this.generateBall(e.localX,e.localY) // e.localX和e.localY 可以拿到點擊事件的點擊X或者y },this)
-
小球碰撞出現錨點問題,即碰撞不是發生在邊緣,而是感覺發生在球心或者什麼位置
原因分析:
1.可能是因為貼圖的視覺中心和 剛體的中心沒有設置在一起 在`egret`貼圖的中心,默認是在坐標左上角 但是在`p2`物理引擎中,默認的圖形錨點確是在剛體的中心 所以在使用兩個合併時需要將貼圖的錨點中心設置到和剛體中心一樣 程式碼如下:
private createBox(width:number,angle:number):egret.Shape{ var box = new egret.Shape() box.graphics.beginFill(0x0000ff) box.graphics.drawRect(0,0,width,width) box.graphics.endFill() box.anchorOffsetX = box.width/2 box.anchorOffsetY = box.height/2 box.rotation = angle * 180 / Math.PI; console.log('box rotation : ' + box.rotation) this.addChild(box) return box } -
小球和方塊出現重疊的現象,而且碰撞中心感覺只是在小球的中心點,並不是小球的邊緣碰撞
問題現象:
檢查原因:
原來是小球貼圖`egret.shape`和小球物理引擎中剛體的大小不一樣`p2.body` 小球的剛體結構實際上是在貼圖內部的一個小區域程式碼如下:
//修改後 private generateBall(locationX:number,locationY:number):void{ var ball:egret.Shape var ballShape:p2.Shape var ballBody:p2.Body var size:number = 10 ball = this.createBall(size*2) // size是剛體半徑的大小,生成小球貼圖的時候需要兩倍大小,因為egret drawCirle是直徑 ballShape = new p2.Circle({ radius:size }) ballBody = new p2.Body({ position:[locationX,locationY], mass:50 }) ball.width = (<p2.Circle>ballShape).radius*2 // 小球寬度也是直徑 ball.height = (<p2.Circle>ballShape).radius*2 // 小球高度頁是直徑 ball.anchorOffsetX = ball.width/2 //參考下一個問題 ball.anchorOffsetY = ball.height/2 ballBody.addShape(ballShape) ballBody.displays = [ball] this.world.addBody(ballBody) } -
貼圖的旋轉和剛體的旋轉數值明明設置的是一樣的,但是旋轉角度和碰撞反應感覺角度並不正確
問題原因:
`egret`中表示旋轉的`rotate`屬性是弧度制 而 `p2`中表示旋轉的`angle`屬性是角度制,即在`p2`中寫多少度就是旋轉多少度,但是在`egret`的貼圖中需要將角度轉換為弧度 轉換公式如下
數學上是用弧度而非角度,因為360的容易整除對數學不重要,而數學使用弧度更方便。角度和弧度關係是:2π弧度=360°。從而1°≈0.0174533弧度,1弧度≈57.29578°。
- 角度轉換為弧度公式:弧度=角度÷180×π
2)弧度轉換為角度公式: 角度=弧度×180÷π
** 程式碼如下:**
```typescript
protected freshFrame():void{
this.world.step(1)
var len:number = this.world.bodies.length
for(let index = 0;index<len;index++){
var body:p2.Body = this.world.bodies[index]
var display: egret.DisplayObject = body.displays[0]
display.x = body.position[0]
display.y = body.position[1]
display.rotation = body.angle * 180/ Math.PI
}
}
-
在兩側填上阻止的牆壁,形成密閉的環境



