libgdx 中遍历数组列表的问题

Issues with iterating through arraylists in libgdx

我正在 libgdx 重制导弹司令部。

我有 3 个数组列表:bulletsmissileshouses。 我一直在尝试遍历列表以检测 missileshouses 以及 missilesbullets 之间的冲突,但我收到的所有内容都非常广泛的错误消息到目前为止已经尝试过:

Exception in thread "LWJGL Application" java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
    at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
    at java.base/java.util.Objects.checkIndex(Objects.java:372)
    at java.base/java.util.ArrayList.get(ArrayList.java:458)
    at com.mygdx.game.Screen.MCGameScreen.render(MCGameScreen.java:112)
    at com.badlogic.gdx.Game.render(Game.java:46)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:215)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.run(LwjglApplication.java:120)

我尝试遍历导致(可能 missiles)数组列表的每种方式都存在相同的问题,以检查由于碰撞而已被删除的位置上的某些内容.我不想 "reuse" 数组中的对象,我更愿意删除它们。

这是用于创建新导弹并将其添加到 missiles 数组的代码,每 2 秒创建一个新导弹,在屏幕顶部给定一个随机位置,并将其添加到missiles.

timer += delta;

        if(timer >= 2) {
            Sprite temp = new Sprite(missile);
            temp.setPosition((float) (Math.random() * Gdx.graphics.getWidth() - temp.getWidth()), Gdx.graphics.getHeight());
            missiles.add(temp);
            timer = 0;
        }

这是创建新项目符号的代码,并将其添加到 bullets 数组中,每次按下 space 栏时,都会创建一个新项目符号,设置为中心坦克的位置,并添加到 bullets.

if(keycode == Input.Keys.SPACE) {
            Sprite temp = new Sprite(bullet);
            temp.setPosition(tank.getX() + tank.getWidth()/2 - temp.getWidth()/2, tank.getY() + tank.getHeight());
            bullets.add(temp);
        }

这是我在创建要添加到列表的 temp 变量以及数组列表本身时使用的预设精灵的初始化。

bullet = new Sprite(new Texture("bullet.png"));
bullets = new ArrayList<>();

missile = new Sprite(new Texture("missile.png"));
missiles = new ArrayList<>();

house = new Sprite(new Texture("house.png"));
houses = new ArrayList<>();

这是我尝试检查碰撞的第一种方法,我还使用相同的循环来移动导弹和子弹。

        for(int i = bullets.size()-1; i >= 0; i--) {
            bullets.get(i).translateY(bulletSpeed);
            if(bullets.get(i).getY() >= Gdx.graphics.getHeight() + bullet.getHeight()) {
                bullets.remove(i);
            }
            for(int j = missiles.size()-1; j >= 0; j--) {
                if(missiles.get(j).getBoundingRectangle().overlaps(bullets.get(i).getBoundingRectangle())) {
                    missiles.remove(j);
                    bullets.remove(i);
                }
            }
        }

        for(int i = missiles.size()-1; i >= 0; i--) {
            missiles.get(i).translateY(missileSpeed);
            if(missiles.get(i).getY() <= 0) {
                missiles.remove(i);
            }
            for(int j = houses.size()-1; j >= 0; j--) {
                if(houses.get(j).getBoundingRectangle().overlaps(missiles.get(i).getBoundingRectangle())) {
                    houses.remove(j);
                    missiles.remove(i);
                }
            }
        }

这是我尝试过的第二种方法,我将所有内容拆分到单独的循环中,以防数组索引在删除后仍在循环中使用。

        for(int i = bullets.size()-1; i >= 0; i--) {
            bullets.get(i).translateY(bulletSpeed);
            if(bullets.get(i).getY() >= Gdx.graphics.getHeight() + bullet.getHeight()) {
                bullets.remove(i);
            }
        }

        for(int i = missiles.size()-1; i >= 0; i--) {
            missiles.get(i).translateY(missileSpeed);
            if(missiles.get(i).getY() <= 0) {
                missiles.remove(i);
            }
        }

        for(int i = missiles.size()-1; i >= 0; i--) {
            for(int j = bullets.size()-1; j >= 0; j--) {
                if(missiles.get(i).getBoundingRectangle().overlaps(bullets.get(j).getBoundingRectangle())) {
                    missiles.remove(i);
                    bullets.remove(j);
                }
            }
        }

        for(int i = missiles.size()-1; i >= 0; i--) {
            for(int j = houses.size()-1; j >= 0; j--) {
                if(missiles.get(i).getBoundingRectangle().overlaps(houses.get(j).getBoundingRectangle())) {
                    missiles.remove(i);
                    houses.remove(j);
                }
            }
        }

我也试过这个变体,使用增强的 for 循环。

        for(Sprite missile: missiles) {
            missile.translateY(missileSpeed);
            if(missile.getY() <= 0) {
                missiles.remove(missiles.indexOf(missile));
            }
        }

        for(Sprite bullet: bullets) {
            bullet.translateY(bulletSpeed);
            if(bullet.getY() >= Gdx.graphics.getHeight() + bullet.getHeight()) {
                bullets.remove(bullets.indexOf(bullet));
            }
        }

        for(Sprite missile: missiles) {
            for(Sprite bullet: bullets) {
                if(missile.getBoundingRectangle().overlaps(bullet.getBoundingRectangle())) {
                    missiles.remove(missiles.indexOf(missile));
                    bullets.remove(bullets.indexOf(bullet));
                }
            }
        }

如果您需要我遗漏的任何详细信息,请告诉我!

我回到我的答案,将每个操作拆分成它自己的 for 循环:

        for(int i = bullets.size()-1; i >= 0; i--) {
            bullets.get(i).translateY(bulletSpeed);
        }

        for(int i = bullets.size()-1; i >= 0; i--) {
            if(bullets.get(i).getY() >= Gdx.graphics.getHeight() + bullet.getHeight()) {
                bullets.remove(i);
            }
        }

        for(int i = missiles.size()-1; i >= 0; i--) {
            missiles.get(i).translateY(missileSpeed);
        }

        for(int i = missiles.size()-1; i >= 0; i--) {
            if(missiles.get(i).getY() <= 0) {
                missiles.remove(i);
            }
        }

        for(int i = missiles.size()-1; i >= 0; i--) {
            for(int j = bullets.size()-1; j >= 0; j--) {
                if(missiles.get(i).getBoundingRectangle().overlaps(bullets.get(j).getBoundingRectangle())) {
                    bullets.remove(j);
                    missiles.remove(i);
                    break;
                }
            }
        }

        for(int i = missiles.size()-1; i >= 0; i--) {
            for(int j = houses.size()-1; j >= 0; j--) {
                if(missiles.get(i).getBoundingRectangle().overlaps(houses.get(j).getBoundingRectangle())) {
                    houses.remove(j);
                    missiles.remove(i);
                    break;
                }
            }
        }

我在从数组中删除一个项目后添加了一个 break;,因为该索引将不再被使用。

所以有一件事我立即注意到并且会担心。当您遍历列表时,您会改变它们。你打电话给

List<E>.remove(i);

这被高度认为是不安全的操作。

如果你真的想在迭代时改变列表,标准和安全的方法是调用列表'

`iterator()` 

函数然后调用 remove() 在迭代器本身上。

不能 100% 确定这是否是您最初问题的解决方案,但这很可能就是给您带来麻烦的原因

以下是您的每种方法存在的问题:

第一种方式:

    for(int i = bullets.size()-1; i >= 0; i--) {
        bullets.get(i).translateY(bulletSpeed);
        if(bullets.get(i).getY() >= Gdx.graphics.getHeight() + bullet.getHeight()) {
            bullets.remove(i);
        }
        for(int j = missiles.size()-1; j >= 0; j--) {
            if(missiles.get(j).getBoundingRectangle().overlaps(bullets.get(i).getBoundingRectangle())) {
                missiles.remove(j);
                bullets.remove(i);
            }
        }
    }

您正在向后迭代项目符号列表,这通常是可以的,但是您在删除项目符号后继续尝试访问循环中的项目符号。当子弹到达屏幕顶部时,您将其移除。然后你在内部导弹循环中再次调用 bullets.get(i),但是那颗子弹已经从列表中消失,所以如果它是列表中的最后一颗子弹,你会得到 IndexOutOfBoundsException。

第二种方式:

    for(int i = missiles.size()-1; i >= 0; i--) {
        for(int j = bullets.size()-1; j >= 0; j--) {
            if(missiles.get(i).getBoundingRectangle().overlaps(bullets.get(j).getBoundingRectangle())) {
                missiles.remove(i);
                bullets.remove(j);
            }
        }
    }

在这里,当发现碰撞时,您移除导弹,但随后您继续迭代子弹,因此如果它是列表中的最后一个,则对 missiles.get(i) 的调用将失败并出现 IndexOutOfBoundsException。您可以通过在移除导弹后添加 break 语句来解决此问题,因为无需继续检查移除的导弹与其余子弹:

for(int i = missiles.size()-1; i >= 0; i--) {
    for(int j = bullets.size()-1; j >= 0; j--) {
        if(missiles.get(i).getBoundingRectangle().overlaps(bullets.get(j).getBoundingRectangle())) {
            missiles.remove(i);
            bullets.remove(j);
            break;
        }
    }
}

你可能想对你的房子循环做同样的事情。

第三种方式:

使用增强的迭代语法时无法安全地删除项目(因为它是从低到高而不是向后迭代)。您可以做的是专门使用迭代器并在迭代器上调用删除:

    for(Iterator<Sprite> iterator = missiles.iterator(); iterator.hasNext();) {
        Sprite missile = iterator.next()
        missile.translateY(missileSpeed);
        if(missile.getY() <= 0) {
            iterator.remove();
        }
    }