1

I am new to AndEngine game development (or game development in general) and I am working on a very simple game. The setting is that I have 4 main sprites in the game:

a.) Fighter Plane b.) Tank c.) Foot soldiers d.) Bomb

The game is in a current infinite loop. The fighter plane comes out from the left, zooms across the screen and comes out the right again continuously every second. The Tank comes out from the left side of the screen, makes its way to the middle of the screen, drops off some foot soldiers who make their way to the right side of the screen. The tank will then face left and go back to where it came from.

The player can tap the screen to launch a bomb that goes straight downwards. When it hits either the tank or the soldiers, the bomb explodes (leaving an explosion sprite) and the bomb, as well as the object it collides with (tank or soldier) detaches from the scene.

My dilemma is that, I can't seem to solve the logic involving the collision. I mean sure, I get the functions, but I'm stumped logic wise. Here is a snapshot of the game itself:

enter image description here

Here is my snippet (this function is called from the onCreateScene update handler:

    protected void checkTankCollision() {
    // TODO Auto-generated method stub

    int numCompares = bombGroup.getChildCount();
    for (int i = 0; i < numCompares; i++) {
        Sprite s = (Sprite) bombGroup.getChildByIndex(i);
        for (int j = 0; j < numCompares; j++) {
            Sprite s2 = (Sprite) tankGroup.getChildByIndex(j);
            if (s.collidesWith(s2)) {
                // boom
                AnimatedSprite boom = createExplosion();
                boom.setPosition(s.getX(), s.getY());
                getEngine().getScene().attachChild(boom);

                // WARNING: cannot detach from the list
                // while looping through the list
                final Sprite a = s;
                final Sprite b = s2;

                // this will do the action after the scene is done updating
                getEngine().getScene().postRunnable(new Runnable() {

                    @Override
                    public void run() {
                        a.detachSelf();
                        b.detachSelf();
                    }
                });
            }
        }
    }

}

The game force closes after some bomb launches. It gives an indexOutOfBoundsException error.

Here's the logCat:

    09-22 00:12:14.565: E/AndroidRuntime(924): FATAL EXCEPTION: UpdateThread
    09-22 00:12:14.565: E/AndroidRuntime(924): java.lang.IndexOutOfBoundsException:  Invalid index 1, size is 1
    09-22 00:12:14.565: E/AndroidRuntime(924):  at  java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at java.util.ArrayList.get(ArrayList.java:304)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at  org.andengine.entity.Entity.getChildByIndex(Entity.java:612)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at com.cs119.bombthetank2.BombTankActivity.checkTankCollision(BombTankActivity.java:660)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at com.cs119.bombthetank2.BombTankActivity$4.onUpdate(BombTankActivity.java:194)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at org.andengine.engine.handler.UpdateHandlerList.onUpdate(UpdateHandlerList.java:47)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at org.andengine.entity.Entity.onManagedUpdate(Entity.java:1395)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at org.andengine.entity.scene.Scene.onManagedUpdate(Scene.java:284)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at org.andengine.entity.Entity.onUpdate(Entity.java:1167)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at org.andengine.engine.Engine.onUpdateScene(Engine.java:591)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at org.andengine.engine.Engine.onUpdate(Engine.java:586)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at org.andengine.engine.Engine.onTickUpdate(Engine.java:548)
    09-22 00:12:14.565: E/AndroidRuntime(924):  at org.andengine.engine.Engine$UpdateThread.run(Engine.java:820)

The Entity and Scene activities are part of the AndEngine, and there's really no way there could be an error there. BombTheTank2 is the name of my activity, so yeah, the error is there.

Erasmus
  • 427
  • 2
  • 10
  • 23
  • Have you tried to replace numCompares in the two for-loops with bombGroup.getChildCount()? – const-ae Sep 21 '12 at 15:22
  • Yes, it's the same, and wouldnt that be the same as numCompares? – Erasmus Sep 21 '12 at 15:28
  • Doesnt work, nullPointerException at the very start of the program – Erasmus Sep 21 '12 at 15:42
  • Well, I thought no, because when you are iterating over a list and then are removing elements of it, then it could have helped when you would dynamically check the limits of the list. Another idea, does it help if you add i--; after you remove the elements? The last idea was rubbish, so I deleted it, when this doesn't work, I have really no more idea ;) – const-ae Sep 21 '12 at 15:46

2 Answers2

2

You seem to be detaching the Sprites before you stop iterating through the list of children. Let's do it the clean way. Try this:

protected void checkTankCollision() {
    int numCompares = bombGroup.getChildCount();
    final ArrayList<Sprite> toBeDetached = new ArrayList<Sprite>();
    for (int i = 0; i < numCompares; i++) {
        Sprite s = (Sprite) bombGroup.getChildByIndex(i);
        for (int j = 0; j < numCompares; j++) {
            Sprite s2 = (Sprite) tankGroup.getChildByIndex(j);
            if (s.collidesWith(s2)) {
                // boom
                AnimatedSprite boom = createExplosion();
                boom.setPosition(s.getX(), s.getY());
                getEngine().getScene().attachChild(boom);

                // WARNING: cannot detach from the list
                // while looping through the list
                toBeDetached.add(s);
                toBeDetached.add(s2);

            }
        }
    }
    runOnUpdateThread(new Runnable() {
        @Override
        public void run() {
            for (Sprite s : toBeDetached) {
                s.detachSelf();
            }
            toBeDetached.clear();
        }
    });
}

EDIT: The actual problem was somewhere else - Erasmus accidentally iterated over the Tank group the same number of times as over the Bomb group. The solution was to check the number of tanks and stop iterating sooner.

I personally always use For-Each loops unless I really need to know the index, it saved me a lot of headaches :-)

JohnEye
  • 6,436
  • 4
  • 41
  • 67
  • Hm. ArrayList needs to be final in order for this to work. I did change it to final, but it still gives an indexOutOfBoundsError – Erasmus Sep 21 '12 at 17:56
  • I think what jmr499485 was suggesting (decrementing numCompares inside runnable) was correct, however, numCompares can't be referenced from inside the runnable because its in an enclosed type. – Erasmus Sep 21 '12 at 17:58
  • Forgot to put final there, I have already fixed it in the answer. On what line does the error occur? – JohnEye Sep 21 '12 at 17:58
  • Sprite s2 = (Sprite) tankGroup.getChildByIndex(j); – Erasmus Sep 21 '12 at 17:59
  • Wait a second, you are checking collision with something from tankGroup without checking whether you have enough tanks. I think the application crashes because you have more bombs than tanks. The line above the one causing problems should read `for (int j = 0; j < tankGroup.getChildCount(); j++) {` – JohnEye Sep 21 '12 at 18:13
  • Wow I never thought of it that way! I totally forgot to check tankCount (although this will prove another problem when attaching a new tank into the scene, but thats another problem for another day). Thanks mr JohnEye! – Erasmus Sep 21 '12 at 18:27
  • You're welcome. I edited the answer to reflect the real solution. – JohnEye Sep 21 '12 at 19:00
1

I think you need to do the "detach" on the UpdateThread, so instead of this

            getEngine().getScene().postRunnable(new Runnable() {

                @Override
                public void run() {
                    a.detachSelf();
                    b.detachSelf();
                }

try this

runOnUpdateThread(new Runnable() {

                    @Override
                    public void run() {
                        a.detachSelf();
                        b.detachSelf();
                    }

every time you detach something, you should reduce your numCompares value, Once you detach something the array that holds those entities is now smaller. Since you are pulling the array length at the beginning, you are eventually running past the end of the array - thus your IndexOutOfBounds error. The detaching still needs to be run on the UpdateThread.

jmroyalty
  • 2,527
  • 1
  • 17
  • 21
  • I understand, however, the problem is that in order to decrement numCompares from within the runnable, it must be final, but it can't since it's inside an enclosed type. Any way to remedy this? – Erasmus Sep 21 '12 at 17:48
  • you probably should kill the iterators and use a while loop – jmroyalty Sep 21 '12 at 18:09