ВИДЕО ДЛЯ ЖИЗНИ: 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) {
}
});
У меня точно такая же проблема случилась со мной последние 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;
}
}
}
}
Кроме того, убедитесь, что вы не располагаете() слишком рано! Также может быть проблема.
Я надеюсь, что это было полезно :-)
Вы должны поставить свои лазеры в массив. Они выходят из сферы действия и уничтожаются, но тела остаются в живых. Тела перерабатываются 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);
}
Я знаю, что этот вопрос довольно старый, но я хотел поделиться своим опытом с этой ТОЧНОЙ такой же ошибкой, где выбранный ответ не помог.
Поэтому для тех, кто все еще испытывает эту проблему, я решил это:
Вместо того, чтобы создавать и удалять мои тела в произвольные моменты времени, я собрал все создание и все удаление всего за два метода: создание и удаление:
//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()!