Como destruo um corpo Box2D em contato sem obter um erro de asserção IsLocked?

7

Recebo este erro ao tentar remover um corpo do mundo:

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

O que estou fazendo errado?

Siddharth
fonte
3
Você realmente leu esse erro?
o0 '.

Respostas:

22

Da minha pouca experiência com o box2d na libgdx, às vezes pode ser difícil isolar e resolver problemas com exceções que só foram agravadas pela última porta. Antes da 2.2.1, eu podia remover facilmente corpos do mundo sem problemas de sincronização, como você está enfrentando, mas depois de migrar para a libgdx build que suportava a 2.2.1, comecei a ver os mesmos problemas. A solução alternativa para mim, sugerida por várias pessoas, foi que você não pode remover corpos do mundo enquanto o mundo estiver sendo simulado.

Existe a possibilidade de você tentar remover um corpo do mundo quando o mundo está sendo pisado? Basicamente, se você tentar fazer isso, o box2d não gosta, então o que você precisa fazer é remover os corpos fora do world.step. O que fiz foi adicionar uma classe de utilidade para o corpo .userDatacom um bool isFlaggedForDelete que seria verificado fora do método world.step.

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

Se você executar algo assim logo após o seu world.step, ele deverá funcionar. No seu código em que você está tentando destruir o corpo, defina-o como .isFlaggedForDelete como true e ele será removido antes do próximo world.step.

Chuck D
fonte
2
Para mundos que contêm muitos corpos, o sweepDeadBodiesmétodo proposto aqui desperdiça muitos ciclos percorrendo os corpos que não serão excluídos. Nesses casos, considere enviar ponteiros para os corpos a serem destruídos em uma deadBodieslista em um escopo externo e, em seguida, iterá- lo após a steplimpeza e limpá-lo uma vez feito.
Anko12 /
5

Então, temos o seu erro:

/var/lib/hudson/jobs/libgdx/workspace/trunk/gdx/jni/Box2D/Dynamics/b2World.cpp:134: void b2World :: DestroyBody (b2Body *): Falha na asserção 'IsLocked () == false'.

Vamos dividir:

A maior parte da primeira parte desse erro são instruções úteis para informar onde o erro está ocorrendo.

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

Parece que o erro está sendo gerado em um arquivo de origem chamado b2World.cpp dentro da estrutura de diretórios mostrada. E está acontecendo:

: 134:

Na linha 134.

Então parece,

void b2World :: DestroyBody (b2Body *):

a função b2World: DestroyBody () que retorna voide aceita um ponteiro para a b2Bodyestá relatando o erro.

Isso terminou a parte "where" do erro, agora está nos dizendo o "what". Parece que está tentando afirmar que isLocked()é igual afalse

Falha na asserção `IsLocked () == false '.

Mas está falhando.

Agora nunca usei o Box2D, mas acho que isso significa que o corpo que você está tentando destruir está trancado. Você precisará descobrir por que o corpo está bloqueado e como desbloqueá-lo.

MichaelHouse
fonte
4

A implementação do Libgdx é apenas uma ponte para a versão nativa, portanto todas as mesmas regras se aplicam.

Nunca tente remover um corpo / acessório / junta quando a simulação estiver em execução.

Nunca tente remover um corpo / acessório / articulação mais de uma vez.

Nunca deixe ponteiros (também conhecidos como referências) para acessórios ou juntas ao excluir o corpo.

Para ser mais cauteloso - talvez até um pouco paranóico - eu uso esse método para remover meus corpos:

/**
 * 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
fonte
2

Esta é a melhor maneira de fazer isso, eu li no manual do Box2D. Você deve chamar isso logo após CURRENT_WORLD.step (.., ..., ...);

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
fonte
sim .... funciona muito bem
Vishal Kumar
0

O erro diz tudo, você precisa verificar se o mundo está bloqueado no momento em que você deseja remover o corpo

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

que funcionou para mim :)

Rudy_TM
fonte