LibGDX阻止身体获取先前删除的身体的属性

时间:2014-08-11 12:39:21

标签: java libgdx box2d

视频的视频: https://www.youtube.com/watch?v=3Mrro8sjcqo#t=25

我正在制作我的第一个LibGDX游戏,而且我在破坏和创建机构方面遇到了麻烦。我一直在使用这个代码片段(因为它似乎无处不在)来删除正文:

private void sweepDeadBodies() {
    for (Iterator<Body> iter = tmpBodies.iterator(); iter.hasNext();) {
        Body body = iter.next();
        if (body != null) {
            Entity data = (Entity) body.getUserData(); //Just the bodies data
            if (data.isFlaggedForDelete) {
                iter.remove();
                world.destroyBody(body);
                body.setUserData(null);
                body = null;
            }
        }
    }
}

这很好,就像预期的那样,但在运行它之后不久就会崩溃。我得到了整个“ AL lib:(EE)alc_cleanup:1设备未关闭”的事情。我做了一些调试并得出结论,当一个物体被摧毁后,当我的一个实体发射一个射弹时它会崩溃。这是一个非常奇特的问题。被摧毁的实体不断进入缓慢的圈子。在被摧毁之后,当下一个射弹被射击时,它将显示为正常,但不会移动,并对其所摧毁的实体进行同样的慢速圆周运动。如果玩家再次开火,它会崩溃。我很难过。有什么想法吗?

以下是有问题的源代码

/*Render Loop in screen:*/    

public void render(float delta) {
    Gdx.gl.glClearColor(0, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);



    // Update World, Camera, and State Time
    world.step(TIMESTEP, VELOCITYITERATIONS, POSITIONITERATIONS);
    sweepDeadBodies();
    camera.update();
    stateTime += delta;

    // Get Fire Buttons
    System.out.println(player.stateTime % 2);
    if (aButton.isPressed() && player.stateTime / 0.25 >= 1) {
        player.stateTime = 0f;
        //Projectile in question
        Laser laser = new Laser(world, player.getBody().getPosition()); 
    }

    // Update the player
    player.update(joyStick.getKnobPercentX(), joyStick.getKnobPercentY(),
            delta);
    enemy.update(delta);

    // Order the bodies
    world.getBodies(tmpBodies);
    Iterator<Body> iterator = tmpBodies.iterator();
    int j;
    boolean flag = true; // set flag to true to begin first pass

    while (flag) {
        flag = false; // set flag to false awaiting a possible swap
        for (j = 0; j < tmpBodies.size - 1; j++) {
            if (tmpBodies.get(j).getType() == BodyType.DynamicBody
                && tmpBodies.get(j + 1).getType() == BodyType.StaticBody) {

                tmpBodies.swap(j, j + 1);
                flag = true;

            }
        }
    }

    /*ADDED THIS, FORGOT TO LEAVE WHEN TRIMMING CODE FOR POST*/
    for (Body body : tmpBodies) {

        // Entity
        if (body.getUserData() instanceof Entity) {
            Entity entity = (Entity) body.getUserData();

            if (entity.sprite != null) {
                entity.sprite.setPosition(body.getPosition().x
                        - entity.sprite.getWidth() / 2,
                        body.getPosition().y - entity.sprite.getHeight()
                                / 2);
                entity.sprite.setRotation(body.getAngle()
                        * MathUtils.radiansToDegrees);
                entity.sprite.draw(batch);
            }

            // Damage
            if (entity.health <= 0) {
                // entity.isFlaggedForDelete = true;
                entity.die();
            }
        }
    }

    // Render Box2D World
    debugRenderer.render(world, camera.combined);

    // Render Stage
    stage.draw();

}


/*Laser Class:*/  
public class Laser {

    private Body    body;
    private Fixture fixture;
    private Vector2 velocity    = new Vector2();
    private float   speed       = 4800;
    private World   world;

    public Laser(World world, Vector2 pos) {
        this.world = world;

        // Body Definition
        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyType.DynamicBody;
        bodyDef.position.set(pos);
        bodyDef.fixedRotation = true;
        bodyDef.gravityScale = 0;
        bodyDef.linearVelocity.x = 100f;

        // Shape
        PolygonShape shape = new PolygonShape();
        shape.setAsBox(2, 0.25f);

        // Fixture Definition
        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.shape = shape;
        fixtureDef.restitution = 0f;
        fixtureDef.friction = .8f;
        fixtureDef.density = 0f;
        fixtureDef.filter.categoryBits = Entities.CATEGORY_PLAYER_PROJECTILE;
        fixtureDef.filter.maskBits = Entities.MASK_PLAYER_PROJECTILE;

        // Create Body
        body = world.createBody(bodyDef);
        fixture = body.createFixture(fixtureDef);

        // Assign Entity to Body
        Sprite sprite = new Sprite(new Texture("sprites/projectiles/laser.png"));
        sprite.setSize(2, 0.25f);
        Entity entity = new Entity();
        entity.sprite = sprite;
        entity.speed = 100f;
        entity.damage = 20f;
        entity.type = Entities.CATEGORY_PLAYER_PROJECTILE;
        body.setUserData(entity);
        body.setBullet(true);
    }

    public float getRestitution() {
        return fixture.getRestitution();
    }

    public void setRestitution(float restitution) {
        fixture.setRestitution(restitution);
    }

    public Body getBody() {
        return body;
    }

    public Fixture getFixture() {
        return fixture;
    }

}

修改 崩溃时抛出错误:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000000066bcbd0d, pid=63044, tid=52440
#
# JRE version: Java(TM) SE Runtime Environment (7.0_45-b18) (build 1.7.0_45-b18)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.45-b08 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:
# C:\Programming\Eclipse Projects\#######\desktop\hs_err_pid63044.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.sun.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

记录错误(只有堆栈跟踪,就像我能找到的那样) (TestControls是我们正在使用的屏幕)

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  com.badlogic.gdx.physics.box2d.World.jniCreateBody(JIFFFFFFFFZZZZZF)J+0
j  com.badlogic.gdx.physics.box2d.World.createBody(Lcom/badlogic/gdx/physics/box2d/BodyDef;)Lcom/badlogic/gdx/physics/box2d/Body;+80
J  ####.###########.########.screens.levels.TestControls.render(F)V
j  com.badlogic.gdx.Game.render()V+19
j  com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop()V+619
j  com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run()V+27
v  ~StubRoutines::call_stub

为删除设置标志的世界联系人侦听器:

    world.setContactListener(new ContactListener() {
        @Override
        public void beginContact(Contact contact) {
        }

        @Override
        public void endContact(Contact contact) {
            if (contact.getFixtureA().getFilterData().categoryBits == Entities.CATEGORY_PLAYER_PROJECTILE
                    && contact.getFixtureB().getFilterData().categoryBits == Entities.CATEGORY_ENEMY) {
                ((Entity) contact.getFixtureA().getBody().getUserData()).isFlaggedForDelete = true;
                ((Entity) contact.getFixtureB().getBody().getUserData())
                        .damage(((Entity) contact.getFixtureA().getBody()
                                .getUserData()).damage);
            }
            if (contact.getFixtureA().getFilterData().categoryBits == Entities.CATEGORY_ENEMY
                    && contact.getFixtureB().getFilterData().categoryBits == Entities.CATEGORY_PLAYER_PROJECTILE) {
                ((Entity) contact.getFixtureB().getBody().getUserData()).isFlaggedForDelete = true;
                ((Entity) contact.getFixtureA().getBody().getUserData())
                        .damage(((Entity) contact.getFixtureB().getBody()
                                .getUserData()).damage);
            }
        }

        @Override
        public void preSolve(Contact contact, Manifold oldManifold) {
        }

        @Override
        public void postSolve(Contact contact, ContactImpulse impulse) {
        }
    });

3 个答案:

答案 0 :(得分:3)

过去两天我遇到了完全相同的问题。

这是一个简单的错误,我有一个实体的ArrayList,此刻显示。如果碰撞发生,我就像你一样设置一个标志。稍后,我运行我的Entity ArrayList并删除被标记的每个实体主体 - 但不是实体本身!

这是我的删除代码。它适用于我,我希望它也适合你。

CopyOnWriteArrayList<Entity> entities = new CopyOnWriteArrayList<Entity>();

public void deleteEntities() {
    for(Entity entity: entities){
        Body body = entity.getBody();
        if (body != null) {
            EntityData data = (EntityData) body.getUserData();
            if (data.isFlaggedForDelete()) {
                final Array<JointEdge> list = body.getJointList();
                //delete all joints attached
                while (list.size > 0) {
                    myWorld.getWorld().destroyJoint(list.get(0).joint);
                }
                //nullify everything, remove the entity from entities and destroy the body
                body.setUserData(null);
                myWorld.getWorld().destroyBody(body);
                entities.remove(entity);
                body = null;                    
            }
        }
    }
}

此外,请确保您不要太早处置()!也可能是一个问题。

我希望这有用:-)

答案 1 :(得分:1)

您必须将激光器放入阵列中。他们正在超出范围并被摧毁,但身体仍然活着。箱体由box2d回收,因此可能会导致问题。在渲染中创建它们也是非常糟糕的。您应该使用池来处理生命周期短的任何事情。

以任何方式接触被处理的身体都会炸毁游戏,所以最好不要在游戏过程中丢弃它们。

我最近发布了广泛使用池的libgdx / box2d游戏。您可以在GitHub查看代码。

同样是endContact(),清理它。它无法阅读。这样的事可能吗?

@Override
public void endContact(Contact contact) {
    Fixture fA = contact.getFixtureA();
    Fixture fB = contact.getFixtureB();
    if (isPlayerProjectile(fA) && isEnemy(fB)) {
        Entity projectile = (Entity) contact.getFixtureA().getBody().getUserData();
        Entity enemy = (Entity) contact.getFixtureB().getBody().getUserData();
        solveProjectileEnemyContact(projectile, enemy);
    } else if (isPlayerProjectile(fB) && isEnemy(fA)) {
        Entity projectile = (Entity) contact.getFixtureB().getBody().getUserData();
        Entity enemy = (Entity) contact.getFixtureA().getBody().getUserData();
        solveProjectileEnemyContact(projectile, enemy);
    }
}

private boolean isPlayerProjectile(Fixture fixture){
    return fixture.getFilterData().categoryBits == Entities.CATEGORY_PLAYER_PROJECTILE;
}

private boolean isEnemy(Fixture fixture){
    return fixture.getFilterData().categoryBits == Entities.CATEGORY_ENEMY;
}

private void solveProjectileEnemyContact(Entity projectile, Entity enemy){
    projectile.isFlaggedForDelete = true;
    enemy.damage(projectile.damage);
}

答案 2 :(得分:-1)

我知道这个问题很古老,但我想分享一下这个完全相同的错误,我选择的答案没有帮助。

因此对于任何仍然遇到此问题的人来说,这就是我解决它的方法:

我不是随机创建和删除我的身体,而是只用2种方法创建和删除所有创建和删除:

filterOptions

删除:

 [
            'attribute' => 'name',
            'label' => 'labelname',
            ...
            ....
            'filterOptions' => [ 'title' => 'prova'],
        ],

我打电话给他们:

    //CREATE ALL
public void createAll(){    
    if(!world.isLocked()){ //KEY FACTOR
        if(createEnemy){
            createEnemy();
        }
        if(createBullet){
            createBullet();     
        }
    }   
}

最后我的createEnemy():

    //get rid of all bodies  (toBeDeleted is an Array<Body>)
public void sweepDeadBodies() {
    if(!world.isLocked()){ // KEY FACTOR
    Array<Body> wB = getBodies();   
       for (Body body : wB) {         
         if(body!=null && toBeDeleted.contains(body, false)) {
                  body.setUserData(null);
                  world.destroyBody(body);
                  toBeDeleted.removeValue(body, false);
         }

       }
    }          
}

和我的createBullet():

    @Override
public void draw() {
    super.draw();

    glyphLayout.setText(scoreFont, score);

    //Do all my deleting and creating after the world.step !!! VERY IMPORTANT!!!
    sweepDeadBodies();
    removeEmtpyActors(); //I use actors as containers for the body, so here i loop though them all and nullify them
    createAll();
    renderer.render(world, camera.combined);

}

toBeDeleted中的所有实体都在我将它们标记为删除后如下:

  private void createEnemy() {
        Enemy enemy= new Enemy(WorldUtils.createEnemy(world));
        ((UserData) enemy.getBody().getUserData()).isFlaggedForDelete = false;
        addActor(enemy);
        addGameActor(enemy); //again this is just for me, you might be using entities or something else
        createEnemy = false;
    }

并像这样添加它们:

public void createBullet(){
        Bullet bullet = new Bullet(WorldUtils.createBullet(world));
        ((UserData) bullet.getBody().getUserData()).isFlaggedForDelete = false;
        addActor(bullet);
        addGameActor(bullet);
        bullet.fire();  //just adds a linear impulse
        createBullet= false;
    }

在我的更新方法中!

我希望这有助于某人。基本上我的错误是创建/销毁尸体而不检查世界是否被锁定,我通过调用!world.isLocked()来修复!