วิธีลบเนื้อหา box2d เมื่อเกิดการชน


10

ฉันยังใหม่กับการเขียนโปรแกรมจาวาและ Android และฉันมีปัญหามากการลบวัตถุเมื่อเกิดการชน ฉันมองไปรอบ ๆ เว็บและพบว่าฉันไม่ควรจัดการกับการลบเนื้อหาของ BOX2D ในระหว่างการตรวจจับการชน (ผู้ฟังรายชื่อผู้ติดต่อ) และฉันควรเพิ่มวัตถุของฉันลงในรายการอาร์เรย์และตั้งค่าตัวแปรในส่วนข้อมูลผู้ใช้ของร่างกายเพื่อลบหรือไม่ แอ็คชันการลบในตัวจัดการอัพเดต ดังนั้นฉันจึงทำสิ่งนี้: ก่อนอื่นให้นิยาม ArrayLists สองอันสำหรับใบหน้าและอีกอันสำหรับร่างกาย:

ArrayList<Sprite> myFaces = new ArrayList<Sprite>();
ArrayList<Body> myBodies = new ArrayList<Body>();

จากนั้นเมื่อฉันสร้างใบหน้าและเชื่อมต่อใบหน้านั้นกับร่างกายฉันเพิ่มพวกเขาไปยัง ArrayLists ของพวกเขาเช่นนี้:

face = new AnimatedSprite(pX, pY, pWidth, pHeight, this.mBoxFaceTextureRegion);
Body BoxBody = PhysicsFactory.createBoxBody(mPhysicsWorld, face, BodyType.DynamicBody, objectFixtureDef);
mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(face, BoxBody, true, true));

myFaces.add(face);
myBodies.add(BoxBody);

ตอนนี้ฉันเพิ่มผู้ฟังรายชื่อผู้ติดต่อและตัวจัดการอัปเดตใน onloadscene เช่นนี้:

this.mPhysicsWorld.setContactListener(new ContactListener() {
private AnimatedSprite face2;
@Override
public void beginContact(final Contact pContact) {
}
@Override
public void endContact(final Contact pContact) {
}
@Override
public void preSolve(Contact contact,Manifold oldManifold) {

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



scene.registerUpdateHandler(new IUpdateHandler() {


@Override
public void reset() { }

@Override
public void onUpdate(final float pSecondsElapsed) {

}
});

แผนของฉันคือการตรวจสอบว่าสองร่างใดที่ชนกันใน listener ผู้ติดต่อโดยการตรวจสอบตัวแปรจากส่วนข้อมูลผู้ใช้ของร่างกายรับตัวเลขในรายการอาร์เรย์และในที่สุดก็ใช้ตัวจัดการอัปเดตเพื่อลบเนื้อหาเหล่านี้

คำถามคือ: ฉันใช้ arraylist ถูกต้องหรือไม่ วิธีการเพิ่มตัวแปรไปยังข้อมูลผู้ใช้ (รหัสโปรด) ฉันพยายามลบเนื้อหาในตัวจัดการอัปเดตนี้ แต่ก็ยังส่งฉัน NullPointerException ดังนั้นวิธีที่ถูกต้องในการเพิ่มตัวจัดการอัปเดตคืออะไรและฉันควรเพิ่มที่ไหน คำแนะนำอื่น ๆ ในการทำเช่นนี้จะดีมาก ขอบคุณล่วงหน้า.

คำตอบ:


7

ใน JBox2d เพื่อลบตามเวลาที่ถูกต้อง:

public class Main
{
    World world;
    ...

    public void update() //your game loop
    {
        ... //do all actual update loop stuff, including detection of collision/death/destruction
        for (Entity entity : manager.entitiesToRemove)
        {
            world.destroyBody(entity.body); //this might be all you need -- adapt to your own purposes. but you will still need a list such that you remove only at the end of each tick.
        }

        manager.entitiesToRemove.clear();
    }
}

public class Entity
{
    Body body; //body representing this Entity
    EntityManager manager; //set ref via Entity constructor
    ...

    //Call from your contact listener when the entity expires
    //body.userData is set to the Entity representing that body
    //so you can get access to the Entity from the Body, as vice versa.
    public void die()
    {
        manager.removeEntity(this);
    }
    ...
}   

public class EntityManager
{
    public List<Entity> entities = new ArrayList<Entity>(); //extant entities
    public List<Entity> entitiesToAdd = new ArrayList<Entity>(); //forthcoming entities
    public List<Entity> entitiesToRemove = new ArrayList<Entity>(); //erstwhile entities <-- the important one for you.
    ...
    public void remove()
    {
        if (!stage.entitiesToRemove.contains(entity))
            stage.entitiesToRemove.add(entity);
            //or just use a Set<Entity>, as dual additions are implicitly prevented.
    }
    ...
    //also add(), and other utility functions for managing entities.
}   

ใช้body.getUserData()และbody.setUserData()การอ่านและการเขียนในuserDataBody


1

ฉันมีปัญหาที่คล้ายกันเมื่อสัปดาห์ที่แล้ว แต่ใน C ++ และฉันหาวิธีแก้ปัญหาผ่านอินเทอร์เน็ต! นี่คือรหัสวิธีการที่ฉันใช้หลังจาก Box2D world-> Step และใช้งานได้:

void physics::clean_up() {
std::vector<b2Body *> to_nuke;

b2Body * body = _world->GetBodyList();
for(; body; body = body->GetNext()) {
    gx::sprite * sprite = (gx::sprite *)body->GetUserData();
    if(sprite->is_killed()) {
        to_nuke.push_back(body);
    }
}

std::sort(to_nuke.begin(), to_nuke.end());
// destroying, but skip duplicates!
uint i = 0;
uint size = to_nuke.size();
while(i < size) {
    b2Body * b = to_nuke[i++];
    while(i < size && to_nuke[i] == b) {
        ++i;
    }
    _world->DestroyBody(b);
    b = 0;
}

ขอให้โชคดีกับการย้ายและมีวันที่ดี ฉันหวังว่าคุณจะประหยัดเวลาของคุณด้วยความช่วยเหลือนี้;)

แก้ไข: วิธีสไปรต์ -> is_killed () ตรวจสอบว่าสไปรท์และร่างกายของมันพร้อมที่จะลบ


1
-1 คำถามเกี่ยวกับ Java และนี่เป็นงานที่ค่อนข้างแตกต่างกันในภาษาต่างๆ มันยังไม่ดีมาก C ++ - ลองใช้ std :: set หรือ std :: unordered_set และฉันจะใช้อัลกอริธึม STL เพื่อจัดการกับการทำลายเช่นกันหรืออย่างน้อยก็เป็นสภาพลูปที่ดีกว่า

1

หากคุณต้องการที่จะเพิ่มธงข้อมูลของผู้ใช้ของคุณเพียงแค่เพิ่มเข้าไปในสิ่งที่คุณตั้งค่าเป็นข้อมูลที่ผู้ใช้ของคุณเมื่อคุณสร้างisDeadBody

GameObject box = new GameObject(face, boxBody);
boxBody.setUserData(box);

จากนั้นติดendContact()ธงศพที่คุณต้องการตายเหมือนตาย:

if( a collision happens ) {
    ((GameObject) bodyA.getUserData()).setDead(true);
    ((GameObject) bodyB.getUserData()).setDead(true);
}

จากนั้นตรวจสอบให้แน่ใจว่าคุณลบวัตถุที่ตายแล้วupdate()ออก อย่าทำอย่างนี้ในขณะที่ PhysicsWorld กำลังอัปเดต:

foreach(GameObject go : gameObjects) {
    if(go.isDead()) {
         destroyGameObject(go);
         go.onDestroyed();
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.