LibGDX Остановить тело от принятия свойств ранее удаленного тела

1

ВИДЕО ДЛЯ ЖИЗНИ: 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;
    }

}

EDIT: Ошибка при сбое:

#
# 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) {
        }
    });
  • 0
    Во-первых, есть ли еще одна трассировка стека, которая выводится в дополнение к DEVICE_NOT_CLOSED? Обычно это происходит, когда окно, на котором вы запускаете игру, внезапно падает. Во-вторых, вы должны попытаться установить userData тела в null, прежде чем уничтожать его. Наконец, вы можете дать код, в котором будет отмечен отмеченныйForDelete для объекта, подлежащего уничтожению?
  • 0
    Спасибо за ответ. Я добавил запрошенный код. Я установил пользовательские данные равными нулю перед рукой и все еще получаю ту же ошибку.
Теги:
libgdx
box2d

3 ответа

3
Лучший ответ

У меня точно такая же проблема случилась со мной последние 2 дня.

Это была простая ошибка, когда у меня был объект ArrayList of Entities, который отображается на данный момент. Если произошло столкновение, я установил флаг так же, как вы. Позже я пропустил свой Entity ArrayList и удалил все тела Entities, которые были помечены, но не сами Entity!

Вот мой код удаления. Это работает для меня, я надеюсь, что это сработает и для вас.

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

Кроме того, убедитесь, что вы не располагаете() слишком рано! Также может быть проблема.

Я надеюсь, что это было полезно :-)

  • 0
    Обратите внимание, что destroyBody автоматически уничтожает также все связанные соединения, поэтому нет необходимости делать это вручную
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);
}
  • 0
    Ух ты. Что ж, ваша игра (очевидно) на шаг впереди того уровня, на котором я сейчас нахожусь. Можете ли вы указать мне правильное направление, какие файлы начать смотреть?
-1

Я знаю, что этот вопрос довольно старый, но я хотел поделиться своим опытом с этой ТОЧНОЙ такой же ошибкой, где выбранный ответ не помог.

Поэтому для тех, кто все еще испытывает эту проблему, я решил это:

Вместо того, чтобы создавать и удалять мои тела в произвольные моменты времени, я собрал все создание и все удаление всего за два метода: создание и удаление:

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

delete:

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

       }
    }          
}

где я их назвал:

    @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);

}

наконец, my createEnemy():

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

и мой createBullet():

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

Все тела в toBeDeleted находятся после того, как я обозначил их для удаления так:

((UserData) enemy.getBody().getUserData()).isFlaggedForDelete = true;

И добавьте их так:

  if(((UserData)body.getUserData()).isFlaggedForDelete){
        toBeDeleted.add(body);
    }

в моем методе обновления!

Надеюсь, это поможет кому-то. По сути, моя ошибка заключалась в создании/уничтожении тел, не проверяя, был ли мир заблокирован, и я исправил его вызовом! World.isLocked()!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню