为什么我在 LIbgdx 中创建实体时有时会出现 box2Dcrash
Why do I sometimes have a box2Dcrash when I create bodies in LIbgdx
我正在开发的 libgdx mini-game 中经常出现 "random" 致命错误。据我所知,这是一个 box2d 本机错误,与 body 创建有关。一个可能的原因是在某个时间步内生成了 body,但我相信我已经采取了适当的应对措施。
下面的案例看起来很相似,但实际上是关于在遍历数组时从数组中删除 objects :Deleting and creating body in libGDX .
关于我自己的问题,这里是崩溃的输出:
<控制台>
EntityManager: entity generation begin ---------
IceSpike: generating physics
IceSpike: generated physics
IceSpike: generating graphics
EntityManager: entity generation registered
IceSpike: generating physics
IceSpike: generated physics
IceSpike: generating graphics
EntityManager: entity generation registered
IceSpike: generating physics
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000000066bcbd0d, pid=5288, tid=0x00000000000013d4
#
# JRE version: Java(TM) SE Runtime Environment (8.0_111-b14) (build 1.8.0_111-b14)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C [gdx-box2d64.dll+0xbd0d]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# D:\Codage\projects\eclipse\git\spacegame\SpaceGame\android\assets\hs_err_pid5288.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
AL lib: (EE) alc_cleanup: 1 device not closed
日志文件:http://pastebin.com/JjBGnTri
说到代码,我会尽量做到简单。希望我没有删掉问题的一部分。
我有一个 EntityManager 来处理我的实体的生成和销毁。游戏中的所有实体都继承自 Entity(一个大的抽象 blob)。他们都有一个box2dbody,生成在generatePhysics(World physics)
.
EntityManager
private GameWorld world;
private Array<Entity> generation;
private Array<Entity> generationCpy;
private Array<Entity> trash;
private Array<Entity> trashCpy;
public void update(float delta)
{
//generate new entities
Gdx.app.debug(this.getClass().getSimpleName(), "entity generation begins ---------");
if(generation.size != 0)
{
generationCpy = new Array<Entity>(generation);
for (Entity entity : generationCpy)
{
entity.generate(world.getPhysics(),world.getAssetManager());
}
}
//destroy old entities
Gdx.app.debug(this.getClass().getSimpleName(), "entity destruction begins ---------");
if(trash.size != 0)
{
trashCpy = new Array<Entity>(trash);
for(Entity entity : trashCpy)
{
trash.removeValue(entity, true);
entity.destroy(world.getPhysics());
entity = null;
}
}
Gdx.app.debug(this.getClass().getSimpleName(), "entity generation complete ------");
实体
public final void generate(World physics, AssetManager assetManager)
{
if(state != EntityState.GENERATED)
{
generatePhysics(physics);
generateGraphics(assetManager);
setState(EntityState.GENERATED);
}
else
Gdx.app.error(this.getClass().getSimpleName(), "entity couldn't be generated. It had allready been generated");
}
到目前为止,似乎只关注尖峰,大多数时候,一切都很顺利。其余时间,这是崩溃的部分:
IceSpikes
@Override
public void generatePhysics(World physics)
{
Gdx.app.debug(this.getClass().getSimpleName(), "generating physics");
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(this.position.cpy().scl(1 / PPM));
body = physics.createBody(bodyDef);
body.setBullet(true);
PolygonShape shape = new PolygonShape();
Vector2 vertices[] = new Vector2[3];
vertices[0] = new Vector2(-width/2,-height/2).scl(1f/PPM);
vertices[1] = new Vector2(0,height/2).scl(1f/PPM);
vertices[2] = new Vector2(width/2,-height/2).scl(1f/PPM);
shape.set(vertices);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 1f;
fixtureDef.restitution = 1f;
fixtureDef.filter.categoryBits = CollisionManager.BIT_PROJECTILE;
fixtureDef.filter.maskBits = CollisionManager.BIT_PLAYER;
Fixture fixture = body.createFixture(fixtureDef);
fixture.setUserData(this);
shape.dispose();
Gdx.app.debug(this.getClass().getSimpleName(), "generated physics");
}
这表明这是由于 IceSpikes 的定时创建,这是在碰撞之后(在 world.step() 中)。然而,这恰恰是我整个实体管理系统的重点。它 generates/removes 在 world.step() 之外的主体,正如输出所证实的那样。
此外,复制的数组被设置为防止实体在循环时从生成和垃圾数组中删除。
我一定是忽略了什么,但是什么?知道如何从那里开始吗?
创建和删除实体(基本上有物理组件)应该用锁绑定。
创建和删除必须在物理世界解锁后进行。
private World physicsWorld;
private PhysicsSystem physicsSystem;
@Override
protected void initialize() {
physicsWorld =physicsSystem.getPhysicsWorld();
}
@Override
protected void processSystem() {
if(!physicsWorld.isLocked()) {
removeBodyFromWorld();
addBodyIntoWorld();
}
}
在 removeBodyFromWorld 方法中删除您想要的实体并在 addBodyIntoWorld 方法中创建新实体。
希望对您有所帮助。
谢谢
至于您是否忽略了某些内容,从此处发布的代码来看,您在 vertices
(在 generatePhysics
方法中)方面忽略了 shape
。
没有任何地方指示多边形使用这些顶点(或任何其他顶点)。但是,在创建将使用多边形的夹具之前,必须设置一些 3 个或更多顶点。
问题原来是由于同一实体的多次创建/销毁引起的,这是由我的 entityManager 中的多个引用引起的。我的实体管理系统是基于监听器的。每个实体都有一个枚举 EntityState,实体管理器会在状态更改时收到通知。我以为我确保此状态在其余代码中仅更改一次,但事实证明,它没有。
一个简单的解决方法是添加一个 if(this.state != state)
条件。
这是现在的样子:
public final void setState(EntityState state)
{
if(this.state == state)
Gdx.app.error(this.getClass().getSimpleName(), "the state was allready " + state);
else
{
this.state = state;
switch(state)
{
case CREATED: notifyCreation(this); break;
case GENERATED: notifyGeneration(this); break;
case TO_BE_REMOVED: notifyFlagForRemoval(this); break;
case INACTIVE: notifyDestruction(this); break;
default : Gdx.app.error(this.getClass().getSimpleName(), "entity state became " + state);
}
}
}
然而,"why" 仍然让我有些摸不着头脑,因为我已经准备好采取某些反制措施,比如在销毁它之前检查 body 是否为 null :
protected final void destroyBody(World physics)
{
Gdx.app.debug(this.getClass().getSimpleName(), "destroying body" );
if(body != null)
{
final Array<JointEdge> joints = body.getJointList();
while (joints.size > 0)
{
Gdx.app.log("GameWorld", "destroying joint" );
Joint joint = joints.get(0).joint;
physics.destroyJoint(joint);
joints.removeIndex(0);
}
physics.destroyBody(body);
}
else
{
Gdx.app.error(this.getClass().getSimpleName(), "entity's body not found. No action taken." );
}
}
就我而言,这是已修复的,即使该修复非常针对我的情况并且对我来说有点老套。
我正在开发的 libgdx mini-game 中经常出现 "random" 致命错误。据我所知,这是一个 box2d 本机错误,与 body 创建有关。一个可能的原因是在某个时间步内生成了 body,但我相信我已经采取了适当的应对措施。
下面的案例看起来很相似,但实际上是关于在遍历数组时从数组中删除 objects :Deleting and creating body in libGDX .
关于我自己的问题,这里是崩溃的输出:
<控制台>
EntityManager: entity generation begin ---------
IceSpike: generating physics
IceSpike: generated physics
IceSpike: generating graphics
EntityManager: entity generation registered
IceSpike: generating physics
IceSpike: generated physics
IceSpike: generating graphics
EntityManager: entity generation registered
IceSpike: generating physics
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000000066bcbd0d, pid=5288, tid=0x00000000000013d4
#
# JRE version: Java(TM) SE Runtime Environment (8.0_111-b14) (build 1.8.0_111-b14)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C [gdx-box2d64.dll+0xbd0d]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# D:\Codage\projects\eclipse\git\spacegame\SpaceGame\android\assets\hs_err_pid5288.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
AL lib: (EE) alc_cleanup: 1 device not closed
日志文件:http://pastebin.com/JjBGnTri
说到代码,我会尽量做到简单。希望我没有删掉问题的一部分。
我有一个 EntityManager 来处理我的实体的生成和销毁。游戏中的所有实体都继承自 Entity(一个大的抽象 blob)。他们都有一个box2dbody,生成在generatePhysics(World physics)
.
EntityManager
private GameWorld world;
private Array<Entity> generation;
private Array<Entity> generationCpy;
private Array<Entity> trash;
private Array<Entity> trashCpy;
public void update(float delta)
{
//generate new entities
Gdx.app.debug(this.getClass().getSimpleName(), "entity generation begins ---------");
if(generation.size != 0)
{
generationCpy = new Array<Entity>(generation);
for (Entity entity : generationCpy)
{
entity.generate(world.getPhysics(),world.getAssetManager());
}
}
//destroy old entities
Gdx.app.debug(this.getClass().getSimpleName(), "entity destruction begins ---------");
if(trash.size != 0)
{
trashCpy = new Array<Entity>(trash);
for(Entity entity : trashCpy)
{
trash.removeValue(entity, true);
entity.destroy(world.getPhysics());
entity = null;
}
}
Gdx.app.debug(this.getClass().getSimpleName(), "entity generation complete ------");
实体
public final void generate(World physics, AssetManager assetManager)
{
if(state != EntityState.GENERATED)
{
generatePhysics(physics);
generateGraphics(assetManager);
setState(EntityState.GENERATED);
}
else
Gdx.app.error(this.getClass().getSimpleName(), "entity couldn't be generated. It had allready been generated");
}
到目前为止,似乎只关注尖峰,大多数时候,一切都很顺利。其余时间,这是崩溃的部分:
IceSpikes
@Override
public void generatePhysics(World physics)
{
Gdx.app.debug(this.getClass().getSimpleName(), "generating physics");
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(this.position.cpy().scl(1 / PPM));
body = physics.createBody(bodyDef);
body.setBullet(true);
PolygonShape shape = new PolygonShape();
Vector2 vertices[] = new Vector2[3];
vertices[0] = new Vector2(-width/2,-height/2).scl(1f/PPM);
vertices[1] = new Vector2(0,height/2).scl(1f/PPM);
vertices[2] = new Vector2(width/2,-height/2).scl(1f/PPM);
shape.set(vertices);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 1f;
fixtureDef.restitution = 1f;
fixtureDef.filter.categoryBits = CollisionManager.BIT_PROJECTILE;
fixtureDef.filter.maskBits = CollisionManager.BIT_PLAYER;
Fixture fixture = body.createFixture(fixtureDef);
fixture.setUserData(this);
shape.dispose();
Gdx.app.debug(this.getClass().getSimpleName(), "generated physics");
}
这表明这是由于 IceSpikes 的定时创建,这是在碰撞之后(在 world.step() 中)。然而,这恰恰是我整个实体管理系统的重点。它 generates/removes 在 world.step() 之外的主体,正如输出所证实的那样。
此外,复制的数组被设置为防止实体在循环时从生成和垃圾数组中删除。
我一定是忽略了什么,但是什么?知道如何从那里开始吗?
创建和删除实体(基本上有物理组件)应该用锁绑定。
创建和删除必须在物理世界解锁后进行。
private World physicsWorld;
private PhysicsSystem physicsSystem;
@Override
protected void initialize() {
physicsWorld =physicsSystem.getPhysicsWorld();
}
@Override
protected void processSystem() {
if(!physicsWorld.isLocked()) {
removeBodyFromWorld();
addBodyIntoWorld();
}
}
在 removeBodyFromWorld 方法中删除您想要的实体并在 addBodyIntoWorld 方法中创建新实体。
希望对您有所帮助。
谢谢
至于您是否忽略了某些内容,从此处发布的代码来看,您在 vertices
(在 generatePhysics
方法中)方面忽略了 shape
。
没有任何地方指示多边形使用这些顶点(或任何其他顶点)。但是,在创建将使用多边形的夹具之前,必须设置一些 3 个或更多顶点。
问题原来是由于同一实体的多次创建/销毁引起的,这是由我的 entityManager 中的多个引用引起的。我的实体管理系统是基于监听器的。每个实体都有一个枚举 EntityState,实体管理器会在状态更改时收到通知。我以为我确保此状态在其余代码中仅更改一次,但事实证明,它没有。
一个简单的解决方法是添加一个 if(this.state != state)
条件。
这是现在的样子:
public final void setState(EntityState state)
{
if(this.state == state)
Gdx.app.error(this.getClass().getSimpleName(), "the state was allready " + state);
else
{
this.state = state;
switch(state)
{
case CREATED: notifyCreation(this); break;
case GENERATED: notifyGeneration(this); break;
case TO_BE_REMOVED: notifyFlagForRemoval(this); break;
case INACTIVE: notifyDestruction(this); break;
default : Gdx.app.error(this.getClass().getSimpleName(), "entity state became " + state);
}
}
}
然而,"why" 仍然让我有些摸不着头脑,因为我已经准备好采取某些反制措施,比如在销毁它之前检查 body 是否为 null :
protected final void destroyBody(World physics)
{
Gdx.app.debug(this.getClass().getSimpleName(), "destroying body" );
if(body != null)
{
final Array<JointEdge> joints = body.getJointList();
while (joints.size > 0)
{
Gdx.app.log("GameWorld", "destroying joint" );
Joint joint = joints.get(0).joint;
physics.destroyJoint(joint);
joints.removeIndex(0);
}
physics.destroyBody(body);
}
else
{
Gdx.app.error(this.getClass().getSimpleName(), "entity's body not found. No action taken." );
}
}
就我而言,这是已修复的,即使该修复非常针对我的情况并且对我来说有点老套。