JBullet NullPointer о пошаговом моделировании в отдельном потоке

У меня есть настройка в моей игре, где физика обновляется в отдельном потоке с реализацией следующим образом

Физический процессор (физический поток)

public class PhysicsProcessor extends Runnable {
    private DynamicsWorld world;

    public PhysicsProcessor() {
        /* basic dynamics world setup with dvbt broadphase :P */
    }

    public synchronized getDynamicsWorld() { return world; }

    @Override
    public void run() {
        /* update time */

        //update phyics
        getDynamicsWorld().stepSimulation(/* delta time */);
    }
}

Пример создания объекта в основном потоке

myPhysicsProcessor.getDynamicsWorld.addRigidBody(/* some rigid body created here */);

Проблема заключается в том, что при запуске игры я иногда получаю исключение нулевого указателя в отдельном потоке "stepSimulation", которое было вызвано setAabb в моей широкой базе данных dbvt.

Кто-нибудь есть какие-либо предложения о том, что я могу сделать, чтобы предотвратить это исключение, или как обойти это?

1 ответ

Вы могли бы сделать мир final судя по всему. Тогда это покажет вам бесполезность синхронизации доступа:

public class PhysicsProcessor extends Runnable {
    private final DynamicsWorld world;

    public synchronized DynamicsWorld getDynamicsWorld() { return world; } //why sync? it can't change
}

Вы видите, когда вы делаете: myPhysicsProcessor.getDynamicsWorld().addRigidBody(...) синхронизация останавливается после getDynamicsWorld() возвращается. Так addRigidBody(...) вызывается вне безопасного синхронизированного контекста.

Нет, то, что вы хотите сделать, это убедиться, что он всегда используется в блоке синхронизации:

@Override
public void run() {
    //update physics
    synchronized(this) {
      world.stepSimulation(/* delta time */);
    }
}

public void addBody(/*args here*/) {
    synchronized(this) {
      world.addRigidBody(/*args here*/);
    }
}

Теперь это нормально для одного метода, но если вы захотите сделать много методов DynamicsWorld таким образом, за пределами бегуна или просто хотите другой выбор, тогда этот не требует синхронизации:

public interface IWorldRunable {    
  void run(DynamicsWorld world);    
}    

public class PhysicsProcessor extends Runnable {
    private final DynamicsWorld world;
    private final ConcurrentLinkedQueue<IWorldRunable> worldRunables = ConcurrentLinkedQueue<IWorldRunable>();

    public PhysicsProcessor() {
        /* basic dynamics world setup with dvbt broadphase :P */
    }

    @Override
    public void run() {
        /* update time */

        //run runables on world
        for(IWorldRunable runable : worldRunables)
          runable.run(world);

        //clear runables
        worldRunables.clear();

        //update phyics
        world.stepSimulation(/* delta time */);
    }

    public void do(IWorldRunable runnable) {
        worldRunables.add(runnable);
    }
}

Затем вы должны сделать это, чтобы добавить тело:

myPhysicsProcessor.do(new IWorldRunable(){      
  @Override
  public void run(DynamicsWorld world){
     world.addRigidBody(...);
  }
});

Это будет выполнено до следующего stepSimulation на той же ветке, что и нить, так что не беспокойтесь.

Другие вопросы по тегам