简体   繁体   English

遍历 libgdx 中的数组列表的问题

[英]Issues with iterating through arraylists in libgdx

I'm working on a Missile Command remake in libgdx.我正在 libgdx 中进行导弹命令重制。

I have 3 array lists: bullets , missiles , and houses .我有 3 个数组列表: bulletsmissileshouses I've been trying to iterate through the lists to detect collisions between missiles and houses , and missiles and bullets , but I get this very broad error message for everything I've tried so far:我一直在尝试遍历列表以检测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$1.run(LwjglApplication.java:120)

Theres been the same issue with every way I've tried to iterate through the lists that causes the (probably missiles ) array list to check for something at a position that was already removed due to a collision.我尝试遍历导致(可能是missiles )数组列表检查 position 中的某些内容的每种方式都存在相同的问题,该内容已因碰撞而被移除。 I do not want to "reuse" the objects in the array, and I'd much prefer just removing them.我不想“重用”数组中的对象,我更喜欢只删除它们。

This is the code for creating a new missile, and adding them to the missiles array, every 2 seconds a new missile is created, given a random position along the top of the screen, and added to missiles .这是创建新导弹的代码,并将它们添加到missiles阵列中,每 2 秒创建一个新导弹,在屏幕顶部给定一个随机 position,并添加到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;
        }

This is the code for creating a new bullet, and adding it to the bullets array, every time the space bar is pressed, a new bullet is created, set to the center of the tank's position, and added to bullets .这是创建新子弹的代码,并将其添加到bullets数组中,每次按下空格键时,都会创建一个新子弹,设置在坦克 position 的中心,并添加到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);
        }

This is the initialization of the preset sprites I use when creating the temp variables to be added to the lists, as well as the arraylists themselves.这是我在创建要添加到列表的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<>();

This is the first way I've tried to check for collisions where I also used the same loops to move the missiles and bullets.这是我尝试检查碰撞的第一种方法,我也使用相同的循环来移动导弹和子弹。

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

This is the second method I've tried, I split up everything in to separate loops in case the index of the array was still being used in the loop even after it was removed.这是我尝试过的第二种方法,我将所有内容拆分为单独的循环,以防数组的索引即使在删除后仍在循环中使用。

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

I've also tried this variant, using enhanced for loops.我也尝试过这种变体,使用增强的 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));
                }
            }
        }

Please let me know if you need any details that I left out!如果您需要我遗漏的任何细节,请告诉我!

I went back to my answer where I split up each operation in to it's own for loop:我回到了我的答案,我将每个操作分成了自己的 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;
                }
            }
        }

I added a break;我加了一个break; after I removed an item from the array, as that index would no longer be in use.在我从数组中删除一个项目之后,因为该索引将不再使用。

So one thing right off the bat that I notice and would be concerned about.因此,我立即注意到并会担心的一件事。 While your iterating through the list's, your mutating them.当您遍历列表时,您会改变它们。 You make a call to你打电话给

List<E>.remove(i);

and this is highly regarded as a less than safe operation.这被高度认为是不安全的操作。

If you really want to mutate a list while iterating, the standard and safe way to do it is make a call to the lists'如果你真的想在迭代时改变一个列表,标准和安全的方法是调用列表'

`iterator()` 

function and then calling remove() on the iterator itself. function 然后在迭代器本身上调用remove()

Not 100% sure if this is the solution to your original problem but this easily could be what is giving you trouble不是 100% 确定这是否是您最初问题的解决方案,但这很容易给您带来麻烦

Here are the problems with each of your approaches:以下是您的每种方法的问题:

First way:第一种方式:

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

You are iterating through the bullets list backwards, which would normally be OK, but then you continue to try to access the bullet in the loop after it's been removed.您正在向后迭代项目符号列表,这通常是可以的,但是在它被删除后您继续尝试访问循环中的项目符号。 When a bullet reaches the top of the screen, you remove it.当子弹到达屏幕顶部时,您将其移除。 Then you you call bullets.get(i) again in your inner missiles loop, but that bullet is already gone from the list so you get IndexOutOfBoundsException if it was the last bullet in the list.然后你在你的内部导弹循环中再次调用bullets.get(i) ,但是那个子弹已经从列表中消失了,所以如果它是列表中的最后一个子弹,你会得到 IndexOutOfBoundsException。

Second way:第二种方式:

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

Here, when a collision is found, you remove the missile, but then you keep iterating through the bullets, so the call to missiles.get(i) will fail with IndexOutOfBoundsException if it was the last one in the list.在这里,当发现碰撞时,您移除了导弹,但随后您继续遍历子弹,因此如果它是列表中的最后一个,则对missiles.get(i)的调用将失败并返回 IndexOutOfBoundsException。 You can fix this by adding a break statement after removing the missile, since there's no need to keep checking the removed missile against the rest of the bullets:您可以通过在移除导弹后添加break语句来解决此问题,因为无需继续检查已移除的导弹是否与子弹的 rest 进行对比:

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

You would want to do the same thing to your loop with the houses.你会想对你的房子循环做同样的事情。

Third way:第三种方式:

You cannot safely remove items when using enhanced iteration syntax (because it's iterating from low to high instead of backwards).使用增强的迭代语法时,您不能安全地删除项目(因为它是从低到高而不是向后迭代)。 What you can do is specifically use an Iterator and call remove on the iterator:您可以做的是专门使用迭代器并在迭代器上调用 remove:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM