Comment supprimer un corps box2d en cas de collision?

10

Je suis encore nouveau dans la programmation Java et Android et j'ai tellement de mal à supprimer un objet en cas de collision. J'ai regardé sur le Web et j'ai constaté que je ne devrais jamais gérer la suppression des corps BOX2D pendant la détection de collision (un écouteur de contacts) et que je devais ajouter mes objets à une liste de tableaux et définir une variable dans la section Données utilisateur du corps à supprimer ou non et à gérer l'action de suppression dans un gestionnaire de mise à jour. J'ai donc fait ceci: d'abord, je définis deux ArrayLists, une pour les visages et une pour les corps:

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

Ensuite, quand je crée un visage et que je connecte ce visage à son corps, je les ajoute à leurs listes de tableaux comme ceci:

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);

maintenant j'ajoute un écouteur de contacts et un gestionnaire de mise à jour dans la onloadscene comme ceci:

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) {

}
});

Mon plan est de détecter quels deux corps sont entrés en collision dans l'écouteur de contacts en vérifiant une variable dans la section des données utilisateur du corps, obtenir leurs numéros dans la liste de tableaux et enfin utiliser le gestionnaire de mise à jour pour supprimer ces corps.

Les questions sont les suivantes: est-ce que j'utilise correctement l'arraylist? Comment ajouter une variable aux données utilisateur (le code s'il vous plaît). J'ai essayé de supprimer un corps dans ce gestionnaire de mise à jour, mais cela me renvoie toujours NullPointerException, alors quelle est la bonne façon d'ajouter un gestionnaire de mise à jour et où dois-je l'ajouter. Tout autre conseil pour ce faire serait formidable. Merci d'avance.

Ayham
la source

Réponses:

7

Dans JBox2d, pour supprimer au bon moment:

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.
}   

Utilisez body.getUserData()et body.setUserData()pour lire et écrire userDatasur le Body.

Ingénieur
la source
1

J'ai un problème similaire il y a une semaine mais en C ++ et je trouve une solution sur internet! Voici le code de méthode que j'utilise après Box2D world-> Step et cela fonctionne:

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;
}

Bonne chance avec le portage et bonne journée. J'espère que vous économiserez votre temps avec cette aide;)

edit: la méthode sprite-> is_killed () vérifie si sprite et son corps physique sont prêts à être supprimés.

Gregory
la source
1
-1, la question concerne Java et c'est une tâche assez différente selon les langages. Ce n'est pas non plus un très bon C ++ - essayez d'utiliser un std :: set ou std :: unordered_set, et j'utiliserais également un algorithme STL pour gérer la destruction, ou au moins une meilleure condition de boucle.
1

Si vous souhaitez ajouter un isDeadindicateur à vos données utilisateur, ajoutez-le simplement à tout ce que vous définissez comme données utilisateur lorsque vous créez leBody .

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

Puis dans endContact() drapeau les corps que vous voulez être morts comme morts:

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

Assurez-vous ensuite de retirer les objets morts update(). Ne faites pas cela pendant la mise à jour de PhysicsWorld:

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