【问题标题】:Why do I sometimes have a box2Dcrash when I create bodies in LIbgdx为什么我在 LIbgdx 中创建实体时有时会出现 box2Dcrash
【发布时间】:2017-01-20 14:42:52
【问题描述】:

我正在开发的 libgdx 迷你游戏中经常出现“随机”致命错误。对于我收集到的内容,这是一个 box2d 原生错误,与身体创建有关。造成这种情况的一个可能原因是在某个时间步内生成了一个实体,但我相信我已经采取了适当的应对措施。

以下情况看起来很相似,但实际上是在循环遍历数组时从数组中删除对象: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)。它们都有一个box2d body,在generatePhysics(World physics)中生成。

实体管理器

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

到目前为止,似乎只关注尖峰,而且大多数时候,一切运行顺利。剩下的时间,这是崩溃的部分:

冰钉

@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() 中)。然而,这正是我整个实体管理系统的重点。它在 world.step() 之外生成/移除物体,正如输出所证实的那样。

此外,复制的数组被设置为防止实体在循环通过时从生成和垃圾数组中删除。

我一定忽略了什么,但是什么?知道如何从那里开始吗?

【问题讨论】:

  • 为什么这被否决了?我是新人,我至少知道为什么,所以下次我可以改进我的帖子......
  • libgdx 论坛上有人说这可能是因为误用了.scl(1f/PPM) 导致身体太小而爆炸。但是,我尝试将它们扩大一点,但我仍然有这个问题。我还在调查。

标签: java libgdx box2d


【解决方案1】:

实体的创建和删除(基本上有物理组件)应该用锁绑定。

物理世界解锁时必须进行创建和删除。

private World physicsWorld;
private PhysicsSystem physicsSystem;

@Override
protected void initialize() {

    physicsWorld =physicsSystem.getPhysicsWorld();
}

@Override
protected void processSystem() {

    if(!physicsWorld.isLocked()) {
          removeBodyFromWorld();    
          addBodyIntoWorld();
    }
}

在 removeBodyFromWorld 方法中删除您想要的实体并在 addBodyIntoWorld 方法中创建新的主体。

希望它可能会有所帮助。
谢谢

【讨论】:

  • 感谢您的回答。恐怕我已经确定我不在物理世界的一步了。为了确定,我为实体生成和删除添加了一个条件if(!world.getPhysics().isLocked()),但问题仍然存在。不过,我很高兴您向我介绍了isLocked() 方法。这在未来可能会变得非常方便。
【解决方案2】:

关于您是否忽略了某些内容,从此处发布的代码中,您忽略了 shape 与 vertices 相关的内容(在 generatePhysics 方法中)。

没有指示多边形使用这些顶点(或任何其他顶点)。然而,在创建将使用多边形的夹具之前,必须设置一些 3 个或更多顶点。

【讨论】:

  • 啊,谢谢。输出entity generation registered 消息的代码怎么样?我没有看到它,我也想检查它的逻辑。
  • 来自我的实体管理系统。它基于 EntityState。 EntityManager 监听状态变化,并将实体放入生成数组、特定数组(射弹/小行星/可收集...)或垃圾箱中。我开始认为这个问题确实更有可能来自那里。目前我需要对它进行更多调查(我目前正在这样做),所以我猜这个帖子是“搁置”的。
【解决方案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);
        }
    }
}

但是,“为什么”仍然让我有些摸不着头脑,因为我已经采取了某些应对措施,例如在销毁尸体之前检查尸体是否为空:

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." );
  }

}

就我而言,这是已修复的,即使该修复非常针对我的情况并且对我来说有些生硬。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-14
    • 1970-01-01
    • 2012-05-30
    • 2013-09-28
    • 2014-12-16
    • 1970-01-01
    相关资源
    最近更新 更多