【转】p2物理引擎在egret中的应用

原文地址

- 效果:

点击在线查看 ,中间一个静态地面,一个长方形物体会掉在上面中间位置

这里写图片描述

前言:

使用egret游戏引擎,配置好p2物理引擎(是个坑,详情查看官方手册模块化配置和第三方库的使用方法

搞起来:

步骤大致有这四个,1、创建世界world,2、创建刚体body,3、给刚体创建形状shape,4、用egret把世界给渲染(图形化)出来。对p2不太熟悉的推荐先看看这篇文章p2入门手册

1、创建世界

    //创建world 
    var world: p2.World = new p2.World();
    //一定时间后(刚体休息不动了)停止不必要的模拟,减少消耗
    world.sleepMode = p2.World.BODY_SLEEPING;

2、创建地面和长方形物体

//创建地面       
var gshape: p2.Plane = new p2.Plane();//创建地面形状shape
var gbody: p2.Body = new p2.Body({
    position: [0,-sh / 2]
});//创建地面刚体,sh是舞台高度转化到物理世界的值
gbody.addShape(gshape);//把形状放到刚体里面
world.addBody(gbody);//把刚体放到世界里面

//创建长方形物体
var boxShape: p2.Shape = new p2.Rectangle(2,1);//创建长方形形状
var boxBody: p2.Body = new p2.Body({ mass: 1,position: [sw / 2,0],angularVelocity: 1 });//创建长方形刚体
boxBody.addShape(boxShape); //把形状放到刚体里面
world.addBody(boxBody); //把刚体放到世界里面

3、显示(渲染)刚体前

p2本身是不会画图的,他出逻辑,egret出图。这意味着我们每创建一个p2刚体,都得为其创建一个egret显示对象,然后把p2刚体的位置角度等信息及时同步给它相应的egret显示对象,这样它就能呈现在在画面上了。
p2世界的单位和坐标不同于egret,锚点的位置也不同,想要同步,需要明白这个:

a、p2的坐标和egret的坐标不同

这里写图片描述

b、p2的单位和egret单位不同
p2的坐标系不单是原点和方向跟传统的Egret坐标系不一样,连单位也是有差别的,长度单位有一个比例,每一个涉及位置的计算,都需要按该比例进行换算得出。该比例因数为factor = 50。

c、p2的锚点和egret的锚点不同
p2刚体的默认锚点是在中间,egret显示对象的锚点默认位于其左上角。何意?看图:

这里写图片描述

d、因此
在我们的示例中,
长方形被画在舞台顶部中间,所以它的位置应该是position:[sw / 2,0] (sw是舞台宽度)。
地面是被画在舞台中间,所以它的位置应该是position:[0,-sh/2] (sw是舞台宽度)。
长方形刚体的egret显示对象锚点从左上角修改到中央:

4、显示(渲染)刚体
这一步要做的就是及时同步p2中刚体的位置角度等信息给它的egret显示对象啦。
在我们的示例中,

a、有一个长方形刚体和地面刚体,因此我们先分别给他们建立egret显示对象,并且关联他们:

        //创建地面       
    var gshape: p2.Plane = new p2.Plane(); 使用plane可以创建平面,它是不会动的,这里我们用它来创建地面
    var gbody: p2.Body = new p2.Body({
        position: [0,-sh / 2]
    }); 
    gbody.addShape(gshape);
    world.addBody(gbody);
    //为地面创建egret显示对象   
    var ground: egret.DisplayObject = this.createGround();//写了个函数,画宽度为舞台宽度、高40px的egret长方形做P2地面的显示对象
    //下面这行代码就是实现同步的精华,只一步就把p2地面刚体和egret地面显示对象关联起来了。
    gbody.displays = [ground]; <pre name="code" class="plain" >// 把渲染好的物体呈现到舞台
    this.addChild(ground);


    //创建长方形
    var boxShape: p2.Shape = new p2.Rectangle(2,1);
    var boxBody: p2.Body = new p2.Body({ mass: 1,position: [sh / 2,sw / 2],angularVelocity: 1 });
    boxBody.addShape(boxShape);
    world.addBody(boxBody);
    //为长方形创建egret显示对象   
    var display: egret.DisplayObject = this.createSprite();//写了个函数,画宽度为80px、高40px的egret长方形做p2长方形的显示对象
    display.width = (<p2.Rectangle>boxShape).width * factor;
    display.height = (<p2.Rectangle>boxShape).height * factor;
    display.anchorOffsetX = display.width / 2
    display.anchorOffsetY = display.height / 2;

    private createGround(): egret.Sprite {
        var result: egret.Sprite = new egret.Sprite();
        result.graphics.beginFill(0x2d78f4);
        result.graphics.drawRect(0,0,600,40);
        result.graphics.endFill();
        return result;
    }    

    private createSprite(): egret.Sprite {
        var result: egret.Sprite = new egret.Sprite();
        result.graphics.beginFill(0x37827A);
        result.graphics.drawRect(0,0,80,40);
        result.graphics.endFill();
        return result;
    }

b、物理世界也是有时间的,刚体也是会动的,所以我们要随着时间推进,不断的把刚体的位置角度信息同步给它的egret显示对象

    //通过添加帧事件,把监听到的刚体信息及时同步给egret显示对象
    egret.Ticker.getInstance().register(function(dt) {
         //让物理世界的时间推进起来
         world.step(dt / 1000);
        //同步长方形刚体的位置角度给它的egret显示对象display</span>
        ground.x = gbody.position[0] * factor;
        ground.y = sh - gbody.position[1] * factor;
        ground.rotation = 360 - gbody.angle * 180 / Math.PI;

        //同步长方形刚体的位置角度给它的egret显示对象display
        display.x = boxBody.position[0] * factor;
        display.y = sh - boxBody.position[1] * factor;
        display.rotation = 360 - boxBody.angle * 180 / Math.PI;
        //长方形休眠时改变它的透明度以便我们知道
        if(boxBody.sleepState == p2.Body.SLEEPING) {
             display.alpha = 0.5;
        } else {
             display.alpha = 1;
        }
    },this); 

c、大功告成

终极代码:把egret的入口类即Main.ts文件内容改成这个即可


class Main extends egret.DisplayObjectContainer{
    public constructor() { 
        super();
        this.addEventListener(egret.Event.ADDED_TO_STAGE,this.onAddToStage,this);
    }

    private onAddToStage(event:egret.Event){

        var factor: number = 50;        
        var sh: number = (egret.MainContext.instance.stage.stageHeight) / factor;
        var sw: number = (egret.MainContext.instance.stage.stageWidth) / factor;
        //创建world
        var world: p2.World = new p2.World();
        world.sleepMode = p2.World.BODY_SLEEPING;
        //创建地面       
        var gshape: p2.Plane = new p2.Plane();
        var gbody: p2.Body = new p2.Body({
                position:[0,-sh/2]
            });
        gbody.addShape(gshape);
        world.addBody(gbody);
        //添加显示对象   
        var ground: egret.DisplayObject = this.createGround();
        gbody.displays = [ground];
        this.addChild(ground);


        //添加长方形刚体
        var boxShape: p2.Shape = new p2.Rectangle(1,1);
        var boxBody: p2.Body = new p2.Body({ mass: 1,position: [sw / 2,0],angularVelocity: 3 });
        boxBody.addShape(boxShape);
        world.addBody(boxBody);
        //添加长方形刚体的显示对象   
        var display: egret.DisplayObject = this.createSprite();
        display.width = (<p2.Rectangle>boxShape).width * factor;
        display.height = (<p2.Rectangle>boxShape).height * factor;
        display.anchorOffsetX = display.width / 2
        display.anchorOffsetY = display.height / 2;
        //同步egret对象和p2对象
        boxBody.displays = [display];
        this.addChild(display);

        //添加帧事件侦听
        egret.Ticker.getInstance().register(function(dt) {
            //使世界时间向后运动
            world.step(dt / 1000);
            ground.x = gbody.position[0] * factor;
            ground.y = sh - gbody.position[1] * factor;
            ground.rotation = 360 - gbody.angle * 180 / Math.PI;

            display.x = boxBody.position[0] * factor;
            display.y = sh - boxBody.position[1] * factor;
            display.rotation = 360 - boxBody.angle * 180 / Math.PI;
            if(boxBody.sleepState == p2.Body.SLEEPING) {
                display.alpha = 0.5;
            }else {
                display.alpha = 1;
                    }
        },this);       
    }

    private createGround(): egret.Sprite {
        var result: egret.Sprite = new egret.Sprite();
        result.graphics.beginFill(0x2d78f4);
        result.graphics.drawRect(0,0,600,40);
        result.graphics.endFill();
        return result;
    }   


    private createSprite(): egret.Sprite {
        var result: egret.Sprite = new egret.Sprite();
        result.graphics.beginFill(0x37827A);
        result.graphics.drawRect(0,0,80,40);
        result.graphics.endFill();
        return result;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值