P2中的形状(上)

为了精确的模拟物理碰撞,我们必须为刚体设定,与贴图相同的形状,例如下图是我们喜闻乐见的几个游戏人物,它们都必须转化为物理引擎中有棱有角的形状,才能完成游戏中的出色表现。
shapInP2Part1-18

P2中设置了7中常用的形状,来满足游戏中常见的需求,这些形状包括:

  •  Capsule:胶囊形状。
  •  Circle:标准圆型。
  •  HeightFeild:地面形状。这种形状由一组y坐标组成,用来模拟高低不平的地面,如Tiny wing中的地面。
  •  Line:线段形状,用来创建高度为1个像素,长度为length的线段。
  •  Particle:粒子形状。粒子形状的尺寸均为零,没有质量和惯性….
  •  Plane:平面形状。
  •  Rectangle:矩形。

如果上面这些形状无法满足你的需求,可以通过convex类,根据实际的需要,通过已知的一组顶点,创建自定义的形状。

shapInP2Part1-13
和其他的物理引擎相比,P2有一个独特的特性,在无其他类库或插件的帮助下,可以创建凹多边形,唯一的条件是,vertices中的顶点不存在交叉或中空。

shapInP2Part1-12

形状的属性

p2中的形状种类很多,属性也不尽相同,但这些形状都继承自shape类,所以它们都拥有相同的一些属性,包括:collisionGroup、collisionMask、sensor和material。

  • collisionGroup:碰撞分组,与接下来的collisionMask一起使用,限制形状只与规定条件的形状碰撞。
  • collisionMask:碰撞筛选,与collisionGroup一起使用,限制形状至于规定条件的形状发生碰撞。
  • sensor:设置形状是否为感应区域,默认为false,如果设置为true,则该形状不参与碰撞模拟,只作为感应区域,触发碰撞事件。
  • material:形状材质,和Box2D物理引擎不同,p2中没有b2Fixture概念,刚体的材质信息,由形状中的material类,以及ContactMaterial类来定义。具体的内容,我们将在后续的教程中,陆续向你解释。

P2中的collisionGroup、collisionMask属性,和Box2D中的FilterData.categoryBits和FilterData.maskBits是一样的;而sensor属性,和Box2D中的sensor是相同的,具体请参考
http://www.ladeng6666.com/blog/2012/10/28/create-sensor-body-using-issensor/
http://www.ladeng6666.com/blog/2012/11/02/filterdata-to-separate-the-box2d-collision/

除此之外,每个形状子类,还包括了很多专属的属性,来定义形状的尺寸大小,具体请参考下表:

shapInP2Part1-08

形状的这些属性,可以在形状类的构造函数中指定,以Rectangle为例,代码如下:

    private createRectangle(x:number,y:number): void {
        var shape: p2.Rectangle = new p2.Rectangle(100, 50);
        var body: p2.Body = new p2.Body({ mass: 1, position: [x, y] });
        body.addShape(shape);
        this.world.addBody(body);
    }

或者在创建了形状对象之后,再设定属性,代码如下:

        shape.width = Math.random() * 50 + 30;

类似的,这一节,大家只是有一个大致的了解即可,针对这些特殊的形状,如Plane、HeightFeild等,我会在后面的教程中,陆续的讲解。

 举个栗子

下载本节示例并运行后,点击舞台任意位置,可以创建形状随机的刚体。如下图所示:

shapeinp2demo

示例中四周的围墙,使用了Plane形状搭建,底部高低不平的地面,通过HeightField形状实现,点击舞台任务一位置,创建矩形、圆形、胶囊或线段形状刚体。

大痔过程

  •  为每个形状创建单独的函数,如CreateCircle()、CreateRectangle、CreateLine(),来分别创建对应形状的刚体。
  •  在鼠标事件处理函数中,随机调用这些形状函数,创建对应的形状。

完整的源代码如下:

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

    public constructor() {
        super();
    }
    public onAppReady(): void {
        this.createWorld();
        this.createGround();
        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 groundShape: p2.Plane = new p2.Plane();
        var groundBody: p2.Body = new p2.Body();

        groundBody.addShape(groundShape);
        groundBody.type = p2.Body.STATIC;

        groundBody.position = [0,300];
        groundBody.angle = Math.PI;

        this.world.addBody(groundBody);
    }
    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.debugDraw.drawDebug();
    }
    private addOneBox(e: egret.TouchEvent): void {
        var positionX: number = Math.floor(e.stageX);
        var positionY: number = Math.floor(e.stageY);
        var ran: number = Math.random();
        if (ran < 0.2) {
            this.createCircle(positionX, positionY);
        } else if (ran < 0.4) {
            this.createCapsule(positionX, positionY);
        } else if (ran < 0.6) {
            this.createLine(positionX, positionY);
        } else if (ran < 0.8) {
            this.createParticle(positionX, positionY);
        } else {
            this.createRectangle(positionX, positionY);
        }
    }
    private createRectangle(x:number,y:number): void {
        var shape: p2.Rectangle = new p2.Rectangle(100, 50);
        shape.width = Math.random() * 50 + 30;
        var body: p2.Body = new p2.Body({ mass: 1, position: [x, y] });
        body.addShape(shape);
        this.world.addBody(body);
    }
    private createCapsule(x: number, y: number): void {
        var capsuleShape = new p2.Capsule(50, 10);
        var body: p2.Body = new p2.Body({ mass: 1, position: [x, y], angularVelocity: 1 });
        body.addShape(capsuleShape);
        this.world.addBody(body);
    }
    private createCircle(x: number, y: number): void {
        var circleShape: p2.Circle = new p2.Circle(30);
        var body: p2.Body = new p2.Body({ mass: 1, position: [x, y], angularVelocity: 1 });
        body.addShape(circleShape);
        this.world.addBody(body);
    }
    private createLine(x: number, y: number): void {
        var shape: p2.Line = new p2.Line(150);
        var body: p2.Body = new p2.Body({ mass: 1, position: [x, y], angularVelocity: 1 });
        body.addShape(shape);
        this.world.addBody(body);
    }
    private createParticle(x: number, y: number): void {
        var shape: p2.Particle = new p2.Particle();
        var body: p2.Body = new p2.Body({ mass: 1, position: [x, y], angularVelocity: 1 });
        body.addShape(shape);
        this.world.addBody(body);
    }
}

下载源代码

http://pan.baidu.com/s/1kT6Appl

联系作者

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

18 Replies to “P2中的形状(上)”

  1. 目前我看到你的bindAsset是单纯只支持矩形的.能否将常用的形状甚至于说自定义形状都添加进去呢.同时我看到constraint约束类似乎还没办法使用?我照其他地方使用没问题的p2的逻辑.然而在egret里面报错了.譬如这样Cannot use ‘in’ operator to search for ‘collideConnected’ in 4

  2. 自定义形状,我会在后后续的章节中进行介绍,关于p2中使用Constraint报错的问题,是因为官方的p2.d.ts中API不全导致的,我这两天优先更新一下p2.d.ts吧,最近好多同学在问。请持续关注我的网站!

  3. p2中刚体的弹性,没有单独的属性,而是通过ContactMaterial对象指定。通过world的addMaterial()添加该对象后,在对象里设置碰撞对象的material属性,可以指定刚体之间的碰撞弹性,我会持续更新教程。

  4. 提示没有,可能是编译后,项目文件夹下的libs->p2->p2.d.ts里,没有定义这个函数,如果我分析的原因不对,可以加我微信交流。

  5. 博主我遇到这样error TS2339: Property ‘hitTest’ does not exist on type ‘World’,用的是你的p2

发表回复

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