libgdx 中遍历数组列表的问题
Issues with iterating through arraylists in libgdx
我正在 libgdx 重制导弹司令部。
我有 3 个数组列表:bullets
、missiles
和 houses
。
我一直在尝试遍历列表以检测 missiles
和 houses
以及 missiles
和 bullets
之间的冲突,但我收到的所有内容都非常广泛的错误消息到目前为止已经尝试过:
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();
}
}
我正在 libgdx 重制导弹司令部。
我有 3 个数组列表:bullets
、missiles
和 houses
。
我一直在尝试遍历列表以检测 missiles
和 houses
以及 missiles
和 bullets
之间的冲突,但我收到的所有内容都非常广泛的错误消息到目前为止已经尝试过:
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();
}
}