用P2刚体属性实现贴图

上一节,我们学习了P2物理引擎的基本用法,并使用P2DebugDraw绘制出模拟结果。实际上,只要知道了刚体的坐标、角度,并相应的更新图片素材的位置等属性,就可以轻松的实现贴图啦。
本节,我们就来学习一下刚体常用的属性,这些属性主要分为3大类:

4

角度相关属性

  •  angle :角度。刚体的角度
  • angularDamping :角速度阻尼。刚体在角速度方向上收到的阻尼
  • angularForce :扭力。刚体在角速度方向上收到的扭力
  • angularVelocity :角速度。刚体的角速度
  • previousAngle :前角度。上一次step()执行后刚体的角度
  • interpolatedAngle :后角度。下一次step()刚体预计的角度

速度相关属性

  • position :坐标。刚体的坐标位置
  • damping :速度阻尼。刚体在线性速度方向上收到的阻尼
  • force :作用力。刚体在线性速度方向上收到的扭力
  • velocity :速度。刚体的速度
  • previousPosition :前坐标。上一次step()执行后刚体的坐标
  • interpolatedPosition :后坐标。下一次step()刚体预计的坐标

其他属性

  • type :刚体类型。刚体当前应用的类型,Dynamic、Kinematic或Static
  • fixedRotation :锁定角度。锁定角度后,刚体角度不发生变化,可以用于横版游戏人物
  • gravityScale :重力本例。=0时,刚体不受重力影响;<0时,刚体向上移动。
  • mass :刚体质量。刚体的质量,用于作用力等需要质量的公式运算
  • inertia :角速度惯性。刚体在角速度上的惯性,值越大惯性越大
  • shapes :形状。刚体中绑定的所有形状清单

举个栗子

掌握了刚体的这些属性,给物理游戏贴图,简直就和扣鼻屎一样的简单和爽快了,在P2中实现的贴图效果如下图所示:

bodyImageDemo


大痔过程

创建示例的大痔过程如下:

1>为刚体body自定义一个任意名称的属性,如userData,利用这个属性为刚体绑定一个贴图对象。
需要说明的是,Egret团队为p2中的Body类添加了一个数组类型的displays属性。本例中我按照Box2D里的习惯,添加了userData属性,来绑定自定属性,包括素材图片。

        this.addChild(asset);
        body.userData = asset;

2>遍历世界中的每一个刚体,并访问刚体的position和angle属性。

        this.world.bodies.forEach(function (b: p2.Body) {

3>根据刚体的角度和坐标,来更新贴图的位置和角度,实现贴图与刚体的同步运动。

            if (b.userData != null) {
                b.userData.x = b.position[0];
                b.userData.y = b.position[1];
                b.userData.rotation = b.angle*180/Math.PI;
            }
        });

完整的代码如下:

class Main extends AbstractP2Test {
    private debugDraw: p2DebugDraw;
    private world: p2.World;

    public constructor() {
        super();
    }
    public onAppReady(): void {
        this.createWorld();
        this.createGround();
        this.createBodies();
        this.createDebug();

        this.stage.addEventListener(egret.TouchEvent.TOUCH_BEGIN, this.addOneBox, this);
    }
    private createWorld(): void {
        var wrd:p2.World = new p2.World();
        wrd.sleepMode = p2.World.BODY_SLEEPING;
        wrd.gravity = [0,10];
        this.world = wrd;
    }
    private createGround(): void {
        var stageHeight:number = egret.MainContext.instance.stage.stageHeight;
        var groundShape: p2.Plane = new p2.Plane();
        var groundBody: p2.Body = new p2.Body();
        groundBody.position[1] = stageHeight-100;
        groundBody.angle = Math.PI;
        groundBody.addShape(groundShape);

        this.world.addBody(groundBody);
    }
    private createBodies(): void {
        var boxShape: p2.Rectangle = new p2.Rectangle(100, 50);
        var boxBody: p2.Body = new p2.Body({ mass: 1, position: [200, 200] });
        boxBody.addShape(boxShape);
        this.world.addBody(boxBody);
        this.bindAsset(boxBody, boxShape);

        boxShape = new p2.Rectangle(50, 50);
        var boxBody: p2.Body = new p2.Body({ mass: 1, position: [200, 180],angularVelocity:1 });
        boxBody.addShape(boxShape);
        this.world.addBody(boxBody);
        this.bindAsset(boxBody, boxShape);
    }
    private createDebug(): void {
        egret.Profiler.getInstance().run();

        this.debugDraw = new p2DebugDraw(this.world);

        var sprite: egret.Sprite = new egret.Sprite();
        this.addChild(sprite);
        this.debugDraw.setSprite(sprite);
    }
    public loop(): void {
        this.world.step(60 / 1000);
        this.world.bodies.forEach(function (b: p2.Body) {
            if (b.userData != null) {
                b.userData.x = b.position[0];
                b.userData.y = b.position[1];
                b.userData.rotation = b.angle*180/Math.PI;
            }
        });
        this.debugDraw.drawDebug();
    }
    private addOneBox(e: egret.TouchEvent): void {
        var positionX: number = Math.floor(e.stageX);
        var positionY: number = Math.floor(e.stageY);

        var boxShape: p2.Rectangle = new p2.Rectangle(Math.random() *150 + 50, 100);
        var boxBody: p2.Body = new p2.Body({ mass: 1, position: [positionX, positionY], angularVelocity: 1 });
        boxBody.addShape(boxShape);
        this.world.addBody(boxBody);
        this.bindAsset(boxBody, boxShape);
    }
    private bindAsset(body: p2.Body, shape: p2.Rectangle): void {
        var asset: egret.Bitmap = this.getBitmapByRes("asset");
        asset.scaleX = shape.width / asset.width;
        asset.scaleY = shape.height / asset.height;
        this.addChild(asset);
        body.userData = asset;
    }
    private getBitmapByRes(resName): egret.Bitmap {
        var bitmap: egret.Bitmap = new egret.Bitmap();
        bitmap.texture = RES.getRes("asset");
        bitmap.anchorOffsetX = bitmap.width / 2;
        bitmap.anchorOffsetY = bitmap.height / 2;
        return bitmap;
    }
}

需要说明的是,以上没有加载素材相关的代码,为了更专注p2物理引擎的示例代码,加载素材的代码部分,被我集中到了AbstractP2Test.ts文件中了,你可以下载源代码进行查看。
如果你对素材的加载还不是很熟悉,请参考@郭少瑞_NeoGuo  的教程http://bbs.egret-labs.org/thread-248-1-1.html

代码很简单,重点是:
1>getBitmapByRes()函数中,创建的Bitmap对象,记得通过anchorOffsetX/Y设置控制点坐标,因为位图加载进来之后,默认是以左上角为初始原点的。
2>在bindAsset()函数中,通过userData属性,为刚体绑定图片素材。因为刚体与素材的尺寸不一样,所以这里通过scaleX/Y调整了图像素材asset的尺寸。
3>在loop()函数中,根据刚体的position和angle(弧度)属性,更新图像素材userData的属性。

点击链接下载原文件

http://yun.baidu.com/s/1gdALIQf

下集预告
P2中的形状

联系作者

公众号:拉小登 | 微博:拉登Dony | B站:拉小登Excel

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注