Uninstalling AndEngine Sprite / Box2D Body causes my program to crash without error / exception information?

I do skateboarding with obstacles that you have to jump with box2D and AndEngine. I'm trying to make sure that when a player collides with an object, the object is deleted and the explosion is placed in the old position of the objects, however, something in the sprite removal code freezes my program, forcing it to end (not even forcibly closing the message, it just closes and goes to my home screen) and no error / exception information appears in logcat, so I have no idea what causes this! Here are some code snippets -

When I create sprites / borders, I attach a JSONObject to the body containing the sprite and the type of sprite, and attach a similar JSONOB 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 building sprite sprites is pretty much similar to the player design, but I set the speed for it:

 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 contact 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 on my destroyObstruction calls, my code works fine, but I'm not sure why this method causes 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); } 
+4
source share
1 answer

After searching in the box2D window and sprite / body removal, you cannot remove the sprite / body from contactListener, but what you can do is set the flag in the body or hide to remove it and check these flags in a separate update method outside the contact. I did this by creating one makeUserData method to create a JSONObject using sprite / body / type and optionally the logical β€œdeleteStatus”, which determines whether the flag for deletion is checked:

 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 the collision, I call this method, which I created to set the flag for deletion inside the body to true:

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

Then, in a separate update handler (I had one in my onLoadScene method that already updated the score), I added a call to another method that I made to iterate through the bodies in my physical world in search of 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-encyclopedia on box2D pretty well explains how the physics of the world is fragile, so you need to be very careful when adding / removing / moving bodies, as in some places this can happen at the same time as computing the world physics, which in ultimately causes the program to crash. It also describes a solution, which is to put the code in "this.runOnUpdateThread". So, for example, in my code, when I added an obstruction sprite to my code (they are added from CountDownTimer, so the probability that they can be added at the same time as calculating the world step), I wrapped it in a stream:

 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 used these streams in most places where I make code with bodies, and I can confirm that this stopped my random crashes :)

+8
source

All Articles