¿Cómo eliminar un cuerpo box2d cuando ocurre una colisión?

10

Todavía soy nuevo en la programación de Java y Android y tengo muchos problemas para eliminar un objeto cuando se produce una colisión. Miré alrededor de la web y descubrí que nunca debería manejar la eliminación de cuerpos BOX2D durante la detección de colisión (un detector de contactos) y debería agregar mis objetos a una lista de arrays y establecer una variable en la sección de Datos de usuario del cuerpo para eliminar o no y manejar la acción de eliminación en un controlador de actualización. Así que hice esto: Primero defino dos ArrayLists, una para las caras y otra para los cuerpos:

ArrayList<Sprite> myFaces = new ArrayList<Sprite>();
ArrayList<Body> myBodies = new ArrayList<Body>();

Luego, cuando creo una cara y la conecto a su cuerpo, las agrego a sus ArrayLists de esta manera:

face = new AnimatedSprite(pX, pY, pWidth, pHeight, this.mBoxFaceTextureRegion);
Body BoxBody = PhysicsFactory.createBoxBody(mPhysicsWorld, face, BodyType.DynamicBody, objectFixtureDef);
mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(face, BoxBody, true, true));

myFaces.add(face);
myBodies.add(BoxBody);

ahora agrego un detector de contactos y un controlador de actualizaciones en la escena de carga como esta:

this.mPhysicsWorld.setContactListener(new ContactListener() {
private AnimatedSprite face2;
@Override
public void beginContact(final Contact pContact) {
}
@Override
public void endContact(final Contact pContact) {
}
@Override
public void preSolve(Contact contact,Manifold oldManifold) {

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



scene.registerUpdateHandler(new IUpdateHandler() {


@Override
public void reset() { }

@Override
public void onUpdate(final float pSecondsElapsed) {

}
});

Mi plan es detectar qué dos cuerpos colisionaron en el oyente de contacto marcando una variable de la sección de datos del usuario del cuerpo, obtener sus números en la lista de la matriz y finalmente usar el controlador de actualización para eliminar estos cuerpos.

Las preguntas son: ¿Estoy usando la lista de arrays correctamente? Cómo agregar una variable a los datos del usuario (el código por favor). Intenté eliminar un cuerpo en este controlador de actualización, pero todavía me arroja NullPointerException, entonces, ¿cuál es la forma correcta de agregar un controlador de actualización y dónde debo agregarlo? Cualquier otro consejo para hacer esto sería genial. Gracias por adelantado.

Ayham
fuente

Respuestas:

7

En JBox2d, para eliminar en el momento correcto:

public class Main
{
    World world;
    ...

    public void update() //your game loop
    {
        ... //do all actual update loop stuff, including detection of collision/death/destruction
        for (Entity entity : manager.entitiesToRemove)
        {
            world.destroyBody(entity.body); //this might be all you need -- adapt to your own purposes. but you will still need a list such that you remove only at the end of each tick.
        }

        manager.entitiesToRemove.clear();
    }
}

public class Entity
{
    Body body; //body representing this Entity
    EntityManager manager; //set ref via Entity constructor
    ...

    //Call from your contact listener when the entity expires
    //body.userData is set to the Entity representing that body
    //so you can get access to the Entity from the Body, as vice versa.
    public void die()
    {
        manager.removeEntity(this);
    }
    ...
}   

public class EntityManager
{
    public List<Entity> entities = new ArrayList<Entity>(); //extant entities
    public List<Entity> entitiesToAdd = new ArrayList<Entity>(); //forthcoming entities
    public List<Entity> entitiesToRemove = new ArrayList<Entity>(); //erstwhile entities <-- the important one for you.
    ...
    public void remove()
    {
        if (!stage.entitiesToRemove.contains(entity))
            stage.entitiesToRemove.add(entity);
            //or just use a Set<Entity>, as dual additions are implicitly prevented.
    }
    ...
    //also add(), and other utility functions for managing entities.
}   

Use body.getUserData()y body.setUserData()para leer y escribir userDataen el Body.

Ingeniero
fuente
1

¡Tengo un problema similar hace una semana pero en C ++ y encuentro una solución en Internet! Aquí está el código de método que estoy usando después de Box2D world-> Step y funciona:

void physics::clean_up() {
std::vector<b2Body *> to_nuke;

b2Body * body = _world->GetBodyList();
for(; body; body = body->GetNext()) {
    gx::sprite * sprite = (gx::sprite *)body->GetUserData();
    if(sprite->is_killed()) {
        to_nuke.push_back(body);
    }
}

std::sort(to_nuke.begin(), to_nuke.end());
// destroying, but skip duplicates!
uint i = 0;
uint size = to_nuke.size();
while(i < size) {
    b2Body * b = to_nuke[i++];
    while(i < size && to_nuke[i] == b) {
        ++i;
    }
    _world->DestroyBody(b);
    b = 0;
}

Buena suerte con la portabilidad y que tengas un buen día. Espero que ahorres tiempo con esta ayuda;)

editar: el método sprite-> is_killed () verifica si sprite y su cuerpo físico están listos para eliminar.

Gregory
fuente
1
-1, la pregunta se refiere a Java y esta es una tarea bastante diferente en diferentes idiomas. Tampoco es muy bueno C ++: intente usar un std :: set o std :: unordered_set, y también usaría un algoritmo STL para manejar la destrucción, o al menos una mejor condición de bucle.
1

Si desea agregar un isDeadindicador a sus datos de usuario, simplemente agréguelo a lo que establezca como datos de usuario cuando cree el Body.

GameObject box = new GameObject(face, boxBody);
boxBody.setUserData(box);

Luego, en la endContact()bandera, los cuerpos que quieres estar muertos como muertos:

if( a collision happens ) {
    ((GameObject) bodyA.getUserData()).setDead(true);
    ((GameObject) bodyB.getUserData()).setDead(true);
}

Luego, asegúrese de eliminar los objetos muertos update(). No hagas esto mientras el PhysicsWorld se está actualizando:

foreach(GameObject go : gameObjects) {
    if(go.isDead()) {
         destroyGameObject(go);
         go.onDestroyed();
    }
}
skyuzo
fuente