本文介绍了AndEngine雪碧/ Box2D的身体去除崩溃我的程序没有错误/异常信息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

限时删除!!

我正在与你必须跳过去使用的Box2D和AndEngine障碍滑板游戏。我试图让这个当玩家与物体相撞,对象是删除和爆炸放置在对象老大地位,但一些在精灵去除code是冻结我的程序导致其结束(它只是关闭本身并进入我的主屏幕),并没有错误/异常信息将显示在logcat中,所以我不知道是什么原因造成它甚至不是一个强制关闭的消息!下面是一些code片段 -

当我创建的精灵/界限我附上的JSONObject含有精灵和精灵的它是类型的机构,并附加了类似的JSONObject与身体和类型的精灵:

  / **方法来构造我们的播放器(采用x和y位置)* /
私人无效constructPlayer(最终浮动像素,最终浮动PY){


    最后美体;


    / *构建我们的玩家的精灵,并设置动画* /
    this.player =新AnimatedSprite(PX,PY,this.mSkaterTextureRegion);
    长[] frameDurations = {100,100};
    player.animate(frameDurations,4,5,真);


    身体= PhysicsFactory.createBoxBody(this.mPhysicsWorld,球员,BodyType.DynamicBody,PLAYER_FIXTURE_DEF);
    this.mPhysicsWorld.registerPhysicsConnector(新PhysicsConnector(球员,身体,真,假));
    body.setUserData(makeUserDataForBody(PLAYER_TYPE,播放器));
    player.setUserData(makeUserDataForSprite(PLAYER_TYPE,体));
    this.mScene.registerTouchArea(播放器);

    //重视我们的球员到现场
    this.mScene.attachChild(播放器);
}

私人的JSONObject makeUserDataForBody(int型的,对象的Sprite)
{
    JSONObject的myObject的=新的JSONObject();

    尝试 {
        myObject.put(类,类型);
        myObject.put(雪碧,精灵​​);
    }赶上(JSONException E){
        // TODO自动生成的catch块
        e.printStackTrace();
    }
    返回myObject的;
}
私人的JSONObject makeUserDataForSprite(int型的,身体的身体)
{
    JSONObject的myObject的=新的JSONObject();

    尝试 {
        myObject.put(身体,身体);
        myObject.put(类,类型);
    }赶上(JSONException E){
        // TODO自动生成的catch块
        e.printStackTrace();
    }
    返回myObject的;
}
 

我的code为构建梗阻精灵是pretty的大致相同构造的球员,但我设置的速度为它动:

 私人无效addObstruction(最终浮动像素,最终浮动PY){


    最后美体;
    最后雪碧myObstruction;

    myObstruction =新的Sprite(PX,PY,this.mCrateTextureRegion);

    身体= PhysicsFactory.createBoxBody(this.mPhysicsWorld,myObstruction,BodyType.DynamicBody,OBJECT_FIXTURE_DEF);

    this.mPhysicsWorld.registerPhysicsConnector(新PhysicsConnector(myObstruction,身体,真,真));

    body.setUserData(makeUserDataForBody(OBSTRUCTION_TYPE,myObstruction));
    myObstruction.setUserData(makeUserDataForSprite(OBSTRUCTION_TYPE,体));

    body.setLinearVelocity(-150f,0);
    //重视我们的梗阻现场

    this.mScene.attachChild(myObstruction);
}
 

下面是contactListener我的物理世界:

  this.mPhysicsWorld.setContactListener(新ContactListener(){

        @覆盖
        公共无效preSOLVE(联系方式联系我们,集成块oldManifold){

        }

        @覆盖
        公共无效postSolve(联系方式联系我们,ContactImpulse冲动){
        }

        @覆盖
        公共无效endContact(联系方式联系我们){
            // TODO自动生成方法存根

                车身obj1Data = contact.getFixtureA()getBody()。
                车身obj2Data = contact.getFixtureB()getBody()。

                的JSONObject obj1UserData;
                的JSONObject obj2UserData;
                INT obj1Type = 0;
                INT obj2Type = 0;
                如果(obj1Data.getUserData()!= NULL)
                {
                    obj1UserData =(的JSONObject)obj1Data.getUserData();
                    尝试 {
                    obj1Type = obj1UserData.getInt(类型);
                    }赶上(JSONException E){
                        // TODO自动生成的catch块
                        e.printStackTrace();
                    }
                }
                如果(obj2Data.getUserData()!= NULL)
                {
                    obj2UserData =(的JSONObject)obj2Data.getUserData();
                    尝试 {
                        obj2Type = obj2UserData.getInt(类型);
                    }赶上(JSONException E){
                        // TODO自动生成的catch块
                        e.printStackTrace();
                    }
                }
                开关(obj1Type)
                {
                    案例PLAYER_TYPE:
                    打破;
                    案例GRINDRAIL_TYPE:
                    如果(isGrinding)
                        {
                            endGrind();
                            如果(!isJumping)
                            下降(播放器);
                        }
                    打破;
                    案例GROUND_TYPE:
                    打破;
                    案例OBSTRUCTION_TYPE:
                    打破;

                    案例WALL_TYPE:
                        打破;


                }

                开关(obj2Type)
                {
                    案例PLAYER_TYPE:
                    打破;
                    案例GRINDRAIL_TYPE:
                    如果(isGrinding)
                        {
                            endGrind();
                            如果(!isJumping)
                            下降(播放器);
                        }
                    打破;
                    案例GROUND_TYPE:
                    打破;
                    案例OBSTRUCTION_TYPE:
                    打破;

                    案例WALL_TYPE:
                        打破;
                }


        }

        @覆盖
        公共无效beginContact(联系方式联系我们){

                车身obj1Data = contact.getFixtureA()getBody()。
                车身obj2Data = contact.getFixtureB()getBody()。


                的JSONObject obj1UserData;
                的JSONObject obj2UserData;
                INT obj1Type = 0;
                INT obj2Type = 0;
                如果(obj1Data.getUserData()!= NULL)
                {
                    obj1UserData =(的JSONObject)obj1Data.getUserData();
                    尝试 {
                    obj1Type = obj1UserData.getInt(类型);
                    }赶上(JSONException E){
                        // TODO自动生成的catch块
                        e.printStackTrace();
                    }
                }
                如果(obj2Data.getUserData()!= NULL)
                {
                    obj2UserData =(的JSONObject)obj2Data.getUserData();
                    尝试 {
                        obj2Type = obj2UserData.getInt(类型);
                    }赶上(JSONException E){
                        // TODO自动生成的catch块
                        e.printStackTrace();
                    }
                }


                //处理事情与球员碰撞
                如果(obj1Type == PLAYER_TYPE)
                {
                    playerCollisionHandler(obj2Data);
                }
                否则,如果(obj2Type == PLAYER_TYPE)
                {
                    playerCollisionHandler(obj1Data);
                }



        }
    });
 

这是我的playerCollisionHandler方式:

 私人无效playerCollisionHandler(身体secondBody)
{
    JSONObject的secondBodyData = NULL;
    如果(secondBody.getUserData()!= NULL)
    {
        secondBodyData =(的JSONObject)secondBody.getUserData();
    }


    JSONObject的用户数据=(的JSONObject)player.getUserData();
    车身playerBody = NULL;
    尝试 {
        playerBody =(身体)userdata.get(身体);
    }赶上(JSONException E){
        // TODO自动生成的catch块
        e.printStackTrace();
    }
    INT OBJTYPE = 0;
    尝试 {
        如果(secondBodyData!= NULL)
        OBJTYPE = secondBodyData.getInt(类);
    }赶上(JSONException E){
        // TODO自动生成的catch块
        e.printStackTrace();
    }
    如果(OBJTYPE == GROUND_TYPE)
    {

        如果(playerBody.getLinearVelocity()Y< 0)
        {
            / *如果精灵Ÿ速度为负的精灵跳跃,
             *不要重置价值!* /
        }
        其他
        {
            如果((isJumping)||(isFalling))
            {
                //播放落地的声音
                AndEngineTestActivity.this.mLandSound.play();
                isJumping = FALSE;
                isFalling = FALSE;
                //player.setPosition(player.getX(),GROUND_LEVEL-player.getHeight());
                //动画登陆
                player.animate(createFrameDurations(LAND_ANIM_FRAMES.length),LAND_ANIM_FRAMES,0);
            }
            如果(!rollSoundIsPlaying)
            {
                playRollSound();
            }
        }
    }
    否则,如果(OBJTYPE == GRINDRAIL_TYPE)
    {
        雪碧grindRail = NULL;
        尝试 {
            grindRail =(雪碧)secondBodyData.get(雪碧);
        }赶上(JSONException E){
            // TODO自动生成的catch块
            e.printStackTrace();
        }
        / *创建一个矩形的上限磨轨测试碰撞* /
        grindRailUpperBound =新的Rectangle(grindRail.getX(),grindRail.getY(),mGrindRailTextureRegion.getWidth(),COLLISION_BOUNDS_PIXEL_ACCURACY);
        playerLowerBound =新的Rectangle(player.getX(),player.getY()+ player.getHeight(),player.getWidth(),COLLISION_BOUNDS_PIXEL_ACCURACY);
        grindRailUpperBound.setColor(1.0F,0F,0F,1F);
        playerLowerBound.setColor(1.0F,1.0F,0F,1F);
        mScene.attachChild(playerLowerBound);
        mScene.attachChild(grindRailUpperBound);
        如果(grindRailUpperBound.collidesWith(playerLowerBound))
        {
            如果(!isGrinding)
            {
                mScene.detachChild(grindRailUpperBound);
                mScene.detachChild(playerLowerBound);
                grindPlayer(播放器);
            }
        }

        如果(!isGrinding)
        {
            / *如果做到了这一点,并自定义矩形范围没有发生冲突
             *这意味着球员相撞,磨铁的另一种方式,所以,我们伤的球员* /
            playerHitByObject();
            destroyObstruction(secondBody);
        }
    }
    否则,如果(OBJTYPE == OBSTRUCTION_TYPE)
    {
        playerHitByObject();
        destroyObstruction(secondBody);

    }
}
 

和这里是这似乎是崩溃的罪魁祸首(在destroyObtruction方法,如果我注释掉我呼吁destroyObstruction我的code运行良好,但我不知道为什么这种方法是导致飞机失事的原因.. ):

 私人无效destroyObstruction(身体obstructionBody)
{
    obstructionBody.setActive(假);


    尝试{
        JSONObject的secondBodyData = NULL;
        如果(obstructionBody.getUserData()!= NULL)
        {
            secondBodyData =(的JSONObject)obstructionBody.getUserData();
        }

        explodeObstruction(((IEntity)secondBodyData.get(雪碧))的getX(),((IEntity)secondBodyData.get(雪碧))getY()以。);

        最后PhysicsConnector obstructionPhysicsConnector = this.mPhysicsWorld.getPhysicsConnectorManager()findPhysicsConnectorByShape((IShape的)secondBodyData.get(雪碧))。
        this.mPhysicsWorld.unregisterPhysicsConnector(obstructionPhysicsConnector);
        this.mPhysicsWorld.destroyBody(obstructionPhysicsConnector.getBody());

        //this.mPhysicsWorld.destroyBody(obstructionBody);
        this.mScene.detachChild((IEntity)secondBodyData.get(雪碧));

    }赶上(例外五)
    {
        Log.d(TAG,异常:+ E);
    }
    赶上(误差e)
    {
        Log.d(TAG,错误:+ E);
    }
}

私人无效explodeObstruction(浮动像素,浮动PY)
{
    PointParticleEmitter obstructionExplosion =新PointParticleEmitter(PX,PY);
    粒子系统ExplosionParticleSystem =新粒子系统(obstructionExplosion,45,60,60,this.mCrateParticleTextureRegion);


    ExplosionParticleSystem.addParticleInitializer(新AlphaInitializer(1F));
    ExplosionParticleSystem.setBlendFunction(GL10.GL_SRC_ALPHA,GL10.GL_ONE);
    ExplosionParticleSystem.addParticleInitializer(新VelocityInitializer(-175,175,-175,175));
    ExplosionParticleSystem.addParticleInitializer(新RotationInitializer(0.0,360.0f));
    ExplosionParticleSystem.addParticleInitializer(新RotationInitializer(0F,-20°F));

    ExplosionParticleSystem.addParticleModifier(新ScaleModifier(1.0F,0.5F,0,MAX_PARTICLE_LIFE / 2));

    ExplosionParticleSystem.addParticleModifier(新AlphaModifier(1,0.35f,0,MAX_PARTICLE_LIFE));

    ExplosionParticleSystem.addParticleModifier(新ExpireModifier(MAX_PARTICLE_LIFE,MAX_PARTICLE_LIFE));


    this.mScene.attachChild(ExplosionParticleSystem);
}
 

解决方案

谷歌搜索有关的Box2D和精灵/身体移除之后事实证明,你不能从contactListener删除精灵/身体,但你可以做的是设置一个标志无论是在身体或精灵将其删除,并检查这些标志在contactListener外一个单独的更新方法。我通过一个单一的makeUserData方法创建的JSONObject与精灵/身/类型,另外一个布尔deleteStatus,确定它是否标记为删除这样做:

 私人的JSONObject makeUserData(int型的,美体,对象精灵)
{
    JSONObject的myObject的=新的JSONObject();

    尝试 {
        myObject.put(类,类型);
        myObject.put(雪碧,精灵​​);
        myObject.put(身体,身体);
        myObject.put(deleteStatus,假);
    }赶上(JSONException E){
        // TODO自动生成的catch块
        Log.d(TAG,异常创建用户数据:+ E);
    }
    返回myObject的;
}
 

然后,而不是碰撞后调用destroyObstruction()我把这种方法我创建,设置标志为删除身体内真:

 私人无效setForDestruction(身体myBody)抛出JSONException
{
    如果(myBody.getUserData()!= NULL)
    {
        ((的JSONObject)myBody.getUserData())把(deleteStatus,真正的);
    }

}
 

然后,在一个单独的更新处理(我在onLoadScene方法有一个已经更新得分)我添加了一个调用另一个方法,我做了遍历在我的物理世界的尸体在寻找这个标志:

  this.mScene.registerUpdateHandler(新IUpdateHandler(){
        @覆盖
        公共无效复位(){}

        @覆盖
        公共无效的OnUpdate(最终浮动pSecondsElapsed){
            //更新球员得分
            updateScore();

            //更新屏幕上的文字
            playerScoreText.setText(分数:+ PLAYER_SCORE);
            playerLivesText.setText(住+ PLAYER_LIVES);

            //删除标记为删除任何精灵
            尝试{
                removeObjectsSetForDestruction();
            }赶上(例外五)
            {
                Log.d(TAG,从更新异常删除的对象:+ E);
            }
            赶上(误差e)
            {
                Log.d(TAG,从更新错误删除的对象:+ E);
            }

        }
    });
 

这里是removeObjectsSetForDestruction方式:

 私人无效removeObjectsSetForDestruction()
{
    如果(this.mPhysicsWorld!= NULL)
    {
        迭代器<车身及GT; allMyBodies = this.mPhysicsWorld.getBodies(); //获取所有的尸体在我的物理世界
        布尔isDelete = FALSE;
        的JSONObject currentBodyData;
        而(allMyBodies.hasNext())
        {
             尝试 {
//这个code是在一个try / catch支架,因为我的一些机构没有附加额外的数据
                 currentBodyData =(的JSONObject)allMyBodies.next()getUserData(); //从主体得到下一个的JSONObject
                 如果(currentBodyData!= NULL)
                 {
                     isDelete =(布尔)currentBodyData.get(deleteStatus);
                    如果(isDelete)
                    {
                        destroyObstruction((身体)currentBodyData.get(身体));
                    }
                 }
            }赶上(JSONException E){
                // TODO自动生成的catch块
                Log.d(TAG,错误获取全球机构的数据:+ E);
            }
        }
    }
}
 

编辑:上的Box2D的AndEngine维基说明pretty的以及如何在世界的物理计算是脆弱的,所以你需要的时候添加/删除/移动机构在一些地方,它可能发生在同一时间,成为世界的物理学计算,这最终会导致程序崩溃非常小心。它还列出了一个解决方案,是将code到'this.runOnUpdateThread。因此,例如在我的code,当我在code增加了一个障碍物精灵(他们是从CountDownTimer补充,使他们能够在同一时间,世界一步的计算是有可能被添加的机会),我把它包在一个线程:

 私人无效addObstruction(最终浮动像素,最终浮动PY){

    runOnUpdateThread(新的Runnable(){



    @覆盖
    公共无效的run(){
        最后美体;
        最后雪碧myObstruction;

        myObstruction =新的Sprite(PX,PY-mCrateTextureRegion.getHeight(),mCrateTextureRegion);

        身体= PhysicsFactory.createBoxBody(mPhysicsWorld,myObstruction,BodyType.DynamicBody,OBJECT_FIXTURE_DEF);
        //body.setLinearDamping(10);
        //body.setAngularDamping(10);
        mPhysicsWorld.registerPhysicsConnector(新PhysicsConnector(myObstruction,身体,真,真));

        body.setUserData(makeUserData(OBSTRUCTION_TYPE,身体,myObstruction));
        myObstruction.setUserData(makeUserData(OBSTRUCTION_TYPE,身体,myObstruction));

        myObstruction.registerUpdateHandler(新IUpdateHandler(){

            @覆盖
            公共无效复位(){

            }

            @覆盖
            公共无效的OnUpdate(浮动pSecondsElapsed){
                    runOnUpdateThread(新的Runnable(){

                    @覆盖
                    公共无效的run(){
                        最后的Vector2速度= Vector2Pool.obtain(-10°F,0F);
                        body.setLinearVelocity(速度);
                        Vector2Pool.recycle(速度);

                    }
                });

            }
        });
        //重视我们的梗阻现场
        mScene.attachChild(myObstruction);

    }

    });
}
 

我用这些线程在大多数地方,我做code与身体,我可以证实这一点,于是只好随机崩溃:)

I am making a skateboarding game with obstacles you have to jump over using box2D and AndEngine. I am trying to make it so that when the player collides with an object, the object is removed and an explosion is placed at the objects old position, however something in the sprite removal code is freezing my program causing it to end (not even a force close message it just closes itself and goes to my home screen) and no error/exception information appears in logcat so I have no idea what is causing it! Here are some code snippets-

when I create the sprites/boundaries I attach a JSONObject to the body containing the sprite and the type of sprite it is, and attach a similar JSONOBject to the sprite with the body and type:

/** method to construct our player (takes an x and y position)*/
private void constructPlayer(final float pX, final float pY) {


    final Body body;


    /* construct the sprite of our player and set the animation */
    this.player = new AnimatedSprite(pX, pY, this.mSkaterTextureRegion);
    long[] frameDurations = {100, 100};
    player.animate(frameDurations, 4, 5, true);


    body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, player, BodyType.DynamicBody, PLAYER_FIXTURE_DEF);
    this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(player, body, true, false));
    body.setUserData(makeUserDataForBody(PLAYER_TYPE,player));
    player.setUserData(makeUserDataForSprite(PLAYER_TYPE,body));
    this.mScene.registerTouchArea(player);

    //attach our player to the scene
    this.mScene.attachChild(player);
}

private JSONObject makeUserDataForBody(int type, Object sprite)
{
    JSONObject myObject = new JSONObject();

    try {
        myObject.put("type", type);
        myObject.put("sprite", sprite);
    } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return myObject;
}
private JSONObject makeUserDataForSprite(int type, Body body)
{
    JSONObject myObject = new JSONObject();

    try {
        myObject.put("body", body);
        myObject.put("type", type);
    } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return myObject;
}

My code for the constructing the obstruction sprites is pretty much the same as constructing the player but I set a velocity for it to move:

private void addObstruction(final float pX, final float pY) {


    final Body body;
    final Sprite myObstruction;

    myObstruction = new Sprite(pX, pY, this.mCrateTextureRegion);

    body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, myObstruction, BodyType.DynamicBody, OBJECT_FIXTURE_DEF);

    this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(myObstruction, body, true, true));

    body.setUserData(makeUserDataForBody(OBSTRUCTION_TYPE,myObstruction));
    myObstruction.setUserData(makeUserDataForSprite(OBSTRUCTION_TYPE,body));

    body.setLinearVelocity(-150f, 0);
    //attach our Obstruction to the scene

    this.mScene.attachChild(myObstruction);
}

Here is the contactListener for my physics world:

this.mPhysicsWorld.setContactListener(new ContactListener() {

        @Override
        public void preSolve(Contact contact, Manifold oldManifold) {

        }

        @Override
        public void postSolve(Contact contact, ContactImpulse impulse) {
        }

        @Override
        public void endContact(Contact contact) {
            // TODO Auto-generated method stub

                Body obj1Data = contact.getFixtureA().getBody();
                Body obj2Data = contact.getFixtureB().getBody();

                JSONObject obj1UserData;
                JSONObject obj2UserData;
                int obj1Type = 0;
                int obj2Type = 0;
                if(obj1Data.getUserData()!=null)
                {
                    obj1UserData =(JSONObject) obj1Data.getUserData();
                    try {
                    obj1Type = obj1UserData.getInt("type");
                    }catch (JSONException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                if(obj2Data.getUserData()!=null)
                {
                    obj2UserData=(JSONObject) obj2Data.getUserData();
                    try {
                        obj2Type = obj2UserData.getInt("type");
                    } catch (JSONException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                switch (obj1Type)
                {
                    case PLAYER_TYPE:
                    break;
                    case GRINDRAIL_TYPE:
                    if(isGrinding)
                        {
                            endGrind();
                            if(!isJumping)
                            fall(player);
                        }
                    break;
                    case GROUND_TYPE:
                    break;
                    case OBSTRUCTION_TYPE:
                    break;

                    case WALL_TYPE:
                        break;


                }

                switch (obj2Type)
                {
                    case PLAYER_TYPE:
                    break;
                    case GRINDRAIL_TYPE:
                    if(isGrinding)
                        {
                            endGrind();
                            if(!isJumping)
                            fall(player);
                        }
                    break;
                    case GROUND_TYPE:
                    break;
                    case OBSTRUCTION_TYPE:
                    break;

                    case WALL_TYPE:
                        break;
                }


        }

        @Override
        public void beginContact(Contact contact) {

                Body obj1Data = contact.getFixtureA().getBody();
                Body obj2Data = contact.getFixtureB().getBody();


                JSONObject obj1UserData;
                JSONObject obj2UserData;
                int obj1Type = 0;
                int obj2Type = 0;
                if(obj1Data.getUserData()!=null)
                {
                    obj1UserData =(JSONObject) obj1Data.getUserData();
                    try {
                    obj1Type = obj1UserData.getInt("type");
                    }catch (JSONException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                if(obj2Data.getUserData()!=null)
                {
                    obj2UserData=(JSONObject) obj2Data.getUserData();
                    try {
                        obj2Type = obj2UserData.getInt("type");
                    } catch (JSONException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }


                //deal with things colliding with the player
                if(obj1Type==PLAYER_TYPE)
                {
                    playerCollisionHandler(obj2Data);
                }
                else if(obj2Type==PLAYER_TYPE)
                {
                    playerCollisionHandler(obj1Data);
                }



        }
    });

here is my playerCollisionHandler method:

private void playerCollisionHandler(Body secondBody)
{
    JSONObject secondBodyData = null;
    if(secondBody.getUserData()!=null)
    {
        secondBodyData=(JSONObject) secondBody.getUserData();
    }


    JSONObject userdata = (JSONObject) player.getUserData();
    Body playerBody = null;
    try {
        playerBody = (Body) userdata.get("body");
    } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    int objType = 0;
    try {
        if(secondBodyData!=null)
        objType = secondBodyData.getInt("type");
    } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    if(objType == GROUND_TYPE)
    {

        if(playerBody.getLinearVelocity().y<0)
        {
            /* If the sprites y velocity is negative the sprite is jumping,
             * don't reset the values!!!*/
        }
        else
        {
            if((isJumping)||(isFalling))
            {
                //play landing sound
                AndEngineTestActivity.this.mLandSound.play();
                isJumping = false;
                isFalling = false;
                //player.setPosition(player.getX(), GROUND_LEVEL-player.getHeight());
                //animate landing
                player.animate(createFrameDurations(LAND_ANIM_FRAMES.length), LAND_ANIM_FRAMES, 0);
            }
            if(!rollSoundIsPlaying)
            {
                playRollSound();
            }
        }
    }
    else if(objType == GRINDRAIL_TYPE)
    {
        Sprite grindRail=null;
        try {
            grindRail = (Sprite) secondBodyData.get("sprite");
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        /*create a rectangle at the upper bound of the grind rail to test collision*/
        grindRailUpperBound = new Rectangle(grindRail.getX(), grindRail.getY(), mGrindRailTextureRegion.getWidth(), COLLISION_BOUNDS_PIXEL_ACCURACY);
        playerLowerBound = new Rectangle(player.getX(), player.getY()+player.getHeight(), player.getWidth(), COLLISION_BOUNDS_PIXEL_ACCURACY);
        grindRailUpperBound.setColor(1.0f, 0f, 0f,1f);
        playerLowerBound.setColor(1.0f, 1.0f, 0f,1f);
        mScene.attachChild(playerLowerBound);
        mScene.attachChild(grindRailUpperBound);
        if(grindRailUpperBound.collidesWith(playerLowerBound))
        {
            if(!isGrinding)
            {
                mScene.detachChild(grindRailUpperBound);
                mScene.detachChild(playerLowerBound);
                grindPlayer(player);
            }
        }

        if(!isGrinding)
        {
            /* if it reaches this point and the custom rectangle bounds did not collide
             * it means the player has collided with the grind rail another way, so we hurt the player*/
            playerHitByObject();
            destroyObstruction(secondBody);
        }
    }
    else if(objType == OBSTRUCTION_TYPE)
    {
        playerHitByObject();
        destroyObstruction(secondBody);

    }
}

and here is the destroyObtruction method which seems to be the culprit of the crashes (if i comment out my calls to destroyObstruction my code runs fine, but I'm not sure why this method is causing the crash...):

private void destroyObstruction(Body obstructionBody)
{
    obstructionBody.setActive(false);


    try{
        JSONObject secondBodyData = null;
        if(obstructionBody.getUserData()!=null)
        {
            secondBodyData=(JSONObject) obstructionBody.getUserData();
        }

        explodeObstruction(((IEntity) secondBodyData.get("sprite")).getX(),((IEntity) secondBodyData.get("sprite")).getY());

        final PhysicsConnector obstructionPhysicsConnector = this.mPhysicsWorld.getPhysicsConnectorManager().findPhysicsConnectorByShape((IShape) secondBodyData.get("sprite"));
        this.mPhysicsWorld.unregisterPhysicsConnector(obstructionPhysicsConnector);
        this.mPhysicsWorld.destroyBody(obstructionPhysicsConnector.getBody());

        //this.mPhysicsWorld.destroyBody(obstructionBody);
        this.mScene.detachChild((IEntity) secondBodyData.get("sprite"));

    }catch(Exception e)
    {
        Log.d(TAG, "Exception:"+e);
    }
    catch(Error e)
    {
        Log.d(TAG, "Error:"+e);
    }
}

private void explodeObstruction(float pX, float pY)
{
    PointParticleEmitter obstructionExplosion = new PointParticleEmitter(pX, pY);
    ParticleSystem ExplosionParticleSystem = new ParticleSystem(obstructionExplosion, 45, 60, 60, this.mCrateParticleTextureRegion);


    ExplosionParticleSystem.addParticleInitializer(new AlphaInitializer(1f));
    ExplosionParticleSystem.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
    ExplosionParticleSystem.addParticleInitializer(new VelocityInitializer(-175, 175, -175, 175));
    ExplosionParticleSystem.addParticleInitializer(new RotationInitializer(0.0f, 360.0f));
    ExplosionParticleSystem.addParticleInitializer(new RotationInitializer(0f, -20f));

    ExplosionParticleSystem.addParticleModifier(new ScaleModifier(1.0f, 0.5f, 0, MAX_PARTICLE_LIFE/2));

    ExplosionParticleSystem.addParticleModifier(new AlphaModifier(1, 0.35f, 0, MAX_PARTICLE_LIFE));

    ExplosionParticleSystem.addParticleModifier(new ExpireModifier(MAX_PARTICLE_LIFE, MAX_PARTICLE_LIFE));


    this.mScene.attachChild(ExplosionParticleSystem);
}
解决方案

After googling about box2D and sprite/body removal it turns out you can't remove a sprite/body from the contactListener, but what you can do is set a flag in either the body or sprite to delete it and check for these flags in a seperate update method outside the contactListener. I did this by making a single 'makeUserData' method to create a JSONObject with the sprite/body/type and additionally a boolean 'deleteStatus' that determines if it flagged for deletion:

private JSONObject makeUserData(int type, Body body, Object sprite)
{
    JSONObject myObject = new JSONObject();

    try {
        myObject.put("type", type);
        myObject.put("sprite", sprite);
        myObject.put("body", body);
        myObject.put("deleteStatus", false);
    } catch (JSONException e) {
        // TODO Auto-generated catch block
        Log.d(TAG,"Exception creating user data:"+e);
    }
    return myObject;
}

Then instead of calling destroyObstruction() after collision I call this method i created to set the flag for deletion within the body to true:

private void setForDestruction(Body myBody) throws JSONException
{
    if(myBody.getUserData()!=null)
    {
        ((JSONObject)myBody.getUserData()).put("deleteStatus", true);
    }

}

Then in a seperate update handler (I had one in my onLoadScene method already to update the score) I added a call to another method I made to iterate through the bodies in my physics world looking for this flag:

 this.mScene.registerUpdateHandler(new IUpdateHandler() {
        @Override
        public void reset() { }

        @Override
        public void onUpdate(final float pSecondsElapsed) {
            //update the players score
            updateScore();

            //update the text on the screen
            playerScoreText.setText( "Score: "+PLAYER_SCORE);
            playerLivesText.setText("Lives:"+PLAYER_LIVES);

            //remove any sprites flagged for deletion
            try{
                removeObjectsSetForDestruction();
            }catch(Exception e)
            {
                Log.d(TAG,"Exception removing objects from update:"+e);
            }
            catch(Error e)
            {
                Log.d(TAG,"Error removing objects from update:"+e);
            }

        }
    });

And here is the removeObjectsSetForDestruction method:

private void removeObjectsSetForDestruction()
{
    if(this.mPhysicsWorld!=null)
    {
        Iterator<Body> allMyBodies = this.mPhysicsWorld.getBodies();//gets all the bodies in my physics world
        boolean isDelete = false;
        JSONObject currentBodyData;
        while(allMyBodies.hasNext())
        {
             try {
//this code is in a try/catch bracket because some of my bodies don't have the extra data attached
                 currentBodyData = (JSONObject)allMyBodies.next().getUserData();//gets the next JSONOBject from the body
                 if(currentBodyData!=null)
                 {
                     isDelete = (Boolean) currentBodyData.get("deleteStatus");
                    if(isDelete)
                    {
                        destroyObstruction((Body) currentBodyData.get("body"));
                    }
                 }
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                Log.d(TAG,"Error getting world bodies data:"+e);
            }
        }
    }
}

EDIT: The AndEngine wiki on box2D explains pretty well how the world physics calculation is fragile so you need to be really careful when adding/deleting/moving bodies as in some places it could happen at the same time as the world physics calculation, which eventually causes the program to crash. It also outlines a solution which is to place the code into 'this.runOnUpdateThread'. So for example in my code when I added an obstruction sprite in my code (they are added from a CountDownTimer so the chances that they could be added at the same time as world step calculation is likely) I wrapped it in a thread:

private void addObstruction(final float pX, final float pY) {

    runOnUpdateThread(new Runnable() {



    @Override
    public void run() {
        final Body body;
        final Sprite myObstruction;

        myObstruction = new Sprite(pX, pY-mCrateTextureRegion.getHeight(), mCrateTextureRegion);

        body = PhysicsFactory.createBoxBody(mPhysicsWorld, myObstruction, BodyType.DynamicBody, OBJECT_FIXTURE_DEF);
        //body.setLinearDamping(10);
        //body.setAngularDamping(10);
        mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(myObstruction, body, true, true));

        body.setUserData(makeUserData(OBSTRUCTION_TYPE,body,myObstruction));
        myObstruction.setUserData(makeUserData(OBSTRUCTION_TYPE,body,myObstruction));

        myObstruction.registerUpdateHandler(new IUpdateHandler() {

            @Override
            public void reset() {

            }

            @Override
            public void onUpdate(float pSecondsElapsed) {
                    runOnUpdateThread(new Runnable() {

                    @Override
                    public void run() {
                        final Vector2 velocity = Vector2Pool.obtain(-10f, 0f);
                        body.setLinearVelocity(velocity);
                        Vector2Pool.recycle(velocity);

                    }
                });

            }
        });
        //attach our Obstruction to the scene
        mScene.attachChild(myObstruction);

    }

    });
}

I've used these threads in most places I do code with bodies and I can confirm this stopped my random crashes :)

这篇关于AndEngine雪碧/ Box2D的身体去除崩溃我的程序没有错误/异常信息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

1403页,肝出来的..

09-06 07:58