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
на той же ветке, что и нить, так что не беспокойтесь.