Wie zerstöre ich einen Box2D-Körper bei Kontakt, ohne einen IsLocked-Assertionsfehler zu erhalten?

7

Ich erhalte diesen Fehler, wenn ich versuche, einen Körper von der Welt zu entfernen:

java: /var/lib/hudson/jobs/libgdx/workspace/trunk/gdx/jni/Box2D/Dynamics/b2World.cpp:134: void b2World :: DestroyBody (b2Body *): Behauptung 'IsLocked () == false' gescheitert.

Was mache ich falsch?

Siddharth
quelle
3
Hast du diesen Fehler tatsächlich gelesen ?
o0 '.

Antworten:

22

Aufgrund meiner geringen Erfahrung mit box2d in libgdx kann es manchmal schwierig sein, Probleme mit Ausnahmen zu isolieren und zu lösen, die nur durch den neuesten Port verschlechtert wurden. Vor 2.2.1 konnte ich ohne Synchronisierungsprobleme, wie sie auftreten, problemlos Körper aus der Welt entfernen, aber nach der Migration auf den libgdx-Build, der 2.2.1 unterstützt, wurden dieselben Probleme angezeigt. Die Problemumgehung für mich, die von mehreren Personen vorgeschlagen wurde, war, dass Sie keine Körper aus der Welt entfernen können, während die Welt möglicherweise simuliert wird.

Gibt es eine Möglichkeit, dass Sie versuchen, einen Körper aus der Welt zu entfernen, wenn die Welt betreten wird? Wenn Sie dies versuchen, gefällt es box2d im Grunde nicht. Sie müssen also nur Körper außerhalb des entfernen world.step. Was ich getan habe, war eine Utility-Klasse für den Körper .userDatamit einem Bool isFlaggedForDelete, die außerhalb der world.step-Methode überprüft wird.

public void sweepDeadBodies() {
   for (Iterator<Body> iter = CURRENT_WORLD.getBodies(); iter.hasNext();) {
     Body body = iter.next();
     if(body!=null) {
          YourCustomUserData data = (YourCustomUserData) body.getUserData();
          if(data.isFlaggedForDelete) {
          CURRENT_WORLD.destroyBody(body);
            body.setUserData(null);
            body = null;
          }
     }
}

Wenn Sie so etwas direkt nach Ihrem world.step ausführen, sollte es funktionieren. Setzen Sie in Ihrem Code, in dem Sie versuchen, den Körper zu zerstören, einfach .isFlaggedForDelete auf true, damit er vor dem nächsten world.step entfernt wird.

Chuck D.
quelle
2
Für Welten mit vielen Körpern sweepDeadBodiesverschwendet die hier vorgeschlagene Methode viele Zyklen, die durch Körper iterieren, die nicht gelöscht werden. In solchen Fällen sollten Sie in Betracht ziehen, Zeiger auf die zu zerstörenden Körper auf eine deadBodiesListe in einem äußeren Bereich zu verschieben und diese nach dem zu durchlaufen und sie anschließend zu löschen step.
Anko
5

Wir haben also Ihren Fehler:

/var/lib/hudson/jobs/libgdx/workspace/trunk/gdx/jni/Box2D/Dynamics/b2World.cpp:134: void b2World :: DestroyBody (b2Body *): Behauptung 'IsLocked () == false' fehlgeschlagen.

Lassen Sie es uns zusammenfassen:

Der größte Teil des ersten Teils dieses Fehlers ist eine hilfreiche Anleitung, um Ihnen mitzuteilen, wo der Fehler auftritt.

/var/lib/hudson/jobs/libgdx/workspace/trunk/gdx/jni/Box2D/Dynamics/b2World.cpp

Es scheint, dass der Fehler in einer Quelldatei mit dem Namen b2World.cpp in der angezeigten Verzeichnisstruktur generiert wird. Und es passiert:

: 134:

In Zeile 134.

Dann sieht es so aus,

void b2World :: DestroyBody (b2Body *):

Die Funktion b2World: DestroyBody (), die voideinen Zeiger auf a zurückgibt und akzeptiert, b2Bodymeldet den Fehler.

Damit ist der "Wo" -Teil des Fehlers beendet, jetzt sagt er uns das "Was". Sieht so aus, als würde versucht zu behaupten, dass dies isLocked()gleich istfalse

Die Behauptung "IsLocked () == false" ist fehlgeschlagen.

Aber es scheitert.

Jetzt habe ich Box2D noch nie benutzt, aber ich vermute, das bedeutet, dass der Körper, den Sie zerstören wollen, gesperrt ist. Sie müssen herausfinden, warum der Körper gesperrt ist und wie Sie ihn entsperren können.

MichaelHouse
quelle
4

Die Libgdx-Implementierung ist nur eine Brücke zur nativen Version, sodass dieselben Regeln gelten.

Versuchen Sie niemals, einen Körper / eine Vorrichtung / ein Gelenk zu entfernen, während die Simulation ausgeführt wird.

Versuchen Sie niemals, einen Körper / eine Vorrichtung / ein Gelenk mehr als einmal zu entfernen.

Hinterlassen Sie niemals Zeiger (auch als Verweise bezeichnet) auf Vorrichtungen oder Gelenke, wenn Sie den Körper löschen.

Um besonders vorsichtig zu sein - vielleicht sogar ein bisschen paranoid - benutze ich diese Methode, um meine Körper zu entfernen:

/**
 * Safe way to remove body from the world. Remember that you cannot have any
 * references to this body after calling this
 *
 * @param body
 *            that will be removed from the physic world
 */

public static void removeBodySafely(Body body) {
    //to prevent some obscure c assertion that happened randomly once in a blue moon
    final ArrayList<JointEdge> list = body.getJointList();
    while (list.size() > 0) {
        world.destroyJoint(list.get(0).joint);
    }
    // actual remove
    world.destroyBody(body);
}
kalle_h
quelle
2

Dies ist der beste Weg, ich lese es im Box2D-Handbuch. Sie müssen dies direkt nach CURRENT_WORLD.step (.., ..., ...) aufrufen.

Iterator<Body> i = CURRENT_WORLD.getBodies();
Body node=i.next();
while (i.hasNext()) {
    Body oBj=node;
    node=i.next();
    YourCustomUserData data = (YourCustomUserData) oBj.getUserData();
    if(data!=null &&  data.isFlaggedForDelete){
        CURRENT_WORLD.destroyBody(oBj);             
    }
}
Tiarsoft
quelle
ja .... funktioniert
super
0

Der Fehler sagt alles, Sie müssen überprüfen, ob die Welt in dem Moment gesperrt ist, in dem Sie den Körper entfernen möchten

if(!world.isLocked())
     world.destroyBody(body)

das hat bei mir funktioniert :)

Rudy_TM
quelle