产生不重叠的随机对象(Java)?

Spawn random objects without overlapping (Java)?

我正在 Java 开发一款游戏,其中一部分要求对象在屏幕顶部生成并继续掉落。我有三个可能生成的对象,以及三个可能生成它们的 x 坐标,所有这些都存储在一个名为 xCoordinate[].
的数组中 其中一个对象属于 class,称为 Enemy,它继承了我称为 FallingThings 的 class。在 FallingThings class 中,我有生成新对象的方法,我的敌人方法如下:

public static void generateNewEnemy() {
    xIndexEnemyOld = xIndexEnemy;
    xIndexEnemy = new Random().nextInt(3);
    if (delayTimer == 0) {
        while (xIndexEnemy == xIndexEnemyOld) {
            xIndexEnemy = new Random().nextInt(3);
        }
    }
    if (xIndexEnemy != xIndexMoney && xIndexEnemy != xIndexFriend) {
        Enemy enemy = new Enemy(xCoordinates[xIndexEnemy]);
        enemies.add((Enemy) enemy);
    } else {
        generateNewEnemy();
    }
}

xIndexEnemy表示xCoordinates数组的索引。
xIndexMoneyxIndexFriend 是其他两个对象的 xCoordinates 数组的索引(与这些值的比较确保一个对象不会直接生成在另一个对象之上)。
delayTimer 变量表示新对象生成之间的随机延迟,这是在我的主 class.
之前设置的 我将 Enemy 对象的每个实例存储在 ArrayList 中。

一切正常,除了有时一个对象会在其自身上方生成(例如,延迟为 0,因此两个敌对对象直接生成在彼此之上,并继续以相同的速度掉落同时)。

过去两天我一直在尝试破解这个问题,但我完全理解为什么我的代码现在不能正常工作。我什至尝试实施碰撞检测来检查 space 中是否已经存在另一个对象,但这也没有用。

如果有任何建议和想法,我将不胜感激。

如我所见,如果 delay == 0 一切都很好,但如果不是,你就有机会生成具有相同索引的新敌人。也许你想调用 return; if delayTimer != 0?

已更新

看看你在这种情况下有什么: 老敌人指数 = 1 NewEnemyIndex = random(3) -> 1 延迟定时器 = 2

然后你不传递给你的 if 语句,然后在下一个如果一切正常,如果你的敌人没有与金钱或其他东西相同的索引,那么你创建与之前具有相同索引的新敌人

EDIT2

看来你还是没明白你函数的问题。它在另一个答案中得到解决,但我会尽量让它更清楚。

public static void generateNewEnemy() {
    xIndexEnemyOld = xIndexEnemy;

这是完全错误的。您不能在没有实际使用新索引的情况下设置旧索引。

    xIndexEnemy = new Random().nextInt(3);
    if (delayTimer == 0) {
        while (xIndexEnemy == xIndexEnemyOld) {
            xIndexEnemy = new Random().nextInt(3);
        }
    }

这其实还可以。你正在生成一个索引,直到你得到一个不同的索引。它可能不是最优雅的解决方案,但它可以完成工作。

    if (xIndexEnemy != xIndexMoney && xIndexEnemy != xIndexFriend) {
        Enemy enemy = new Enemy(xCoordinates[xIndexEnemy]);
        enemies.add((Enemy) enemy);
    } else {
        generateNewEnemy();
    }
}

这是您的问题(以及在那里设置旧索引)。您不仅必须生成与旧索引不同的索引,还必须与 IndexMoney 和 IndexFriend 不同。

现在,如果 IndexOld = 0、IndexMoney = 1 和 IndexFriend = 2 会发生什么情况?你必须生成一个不同于 0 的索引,所以你得到(再次,例如)1。IndexMoney 也是 1,所以条件将失败并且你进行递归调用。 (你为什么还要递归调用?)

OldIndex 为 0,现在在下一次调用中将其设置为 1。因此 IndexOld = 1、IndexMoney = 1 和 IndexFriend = 2。您现在看到问题了吗?重叠索引现在是错误的。并且无论递归调用多少次,新索引都只能为0

你不止一次搬起石头砸自己的脚。递归调用不会导致无限循环(实际上是堆栈溢出),因为您正在更改旧索引。 (又是放错地方了)

如果条件成立,那么新生成的索引不能与之前的任何索引重叠。看你之前说的,不是你想要的。

你可以这样简化你的函数,

public static void generateNewEnemy() {
    xIndexEnemy = new Random().nextInt(3);
    if (delayTimer == 0) {
        while (xIndexEnemy == xIndexEnemyOld) {
            xIndexEnemy = new Random().nextInt(3);
        }
    }

    Enemy enemy = new Enemy(xCoordinates[xIndexEnemy]);
    enemies.add((Enemy) enemy);
    xIndexEnemyOld = xIndexEnemy;
    // Now that you used the new index you can store it as the Old one
}

有用吗?当 delayTimer 为 0 时,它肯定会避免重叠,但我不知道你的其余代码(我也不想知道)以及你做了什么。该知道的是你

关于我的建议,它们是如何生成您想要的索引的替代方法。我假设您会知道如何将它们融入您的代码,但您仍然可以在解决实际问题后自由尝试它们。


原答案

这是一个建议。

你可以做的一件事是从数组中获取这些敌人 "borrow" 元素。假设你有一个数组,

ArrayList< Float > coordinates = new ArrayList< Float >();
// Add the coordinates you want ...

您可以 select 一个索引,但请改用数组的最大大小,然后删除您选择的元素。通过这样做,您将删除其中一个索引选项。

int nextIndex = new Random().nextInt( coordinates.size() );
float xCoordinate = coordinates.get( nextIndex );
coordinates.remove( nextIndex ); // Remove the coordinate

稍后,当您完成该值时(例如,经过足够的时间或敌人死亡时),您可以将其放回数组中。

coordinates.add( xCoordinate );

现在该值再次可用,您不必费心检查索引。

嗯,这是我的建议的总体思路。您必须调整它以使其按您需要的方式工作,特别是当您将值放回数组时,因为我不知道您可以在代码中的哪个位置执行此操作。


编辑:

另一种选择是,您保留之前的数组。无需从中删除任何值。

当您想获得一个新坐标时,创建一个仅包含可用值的额外数组,即不会与其他对象重叠的值。

...
if (delayTimer == 0) {
    ArrayList< Integer > availableIndexes = new ArrayList< Integer >();
    for ( int i = 0; i < 3; ++i ) {
        if ( i != xIndexEnemyOld ) {
            availableIndexes.add( i );
        }
    }
    int selectedIndex = new Random().nextInt( availableIndexes.size() );
    xIndexEnemy = availableIndexes.get( selectedIndex );
}
// Else no need to use the array
else {
    xIndexEnemy = new Random().nextInt( 3 );
}
...

现在您确定得到的索引应该不同,因此无需检查它是否重叠。 缺点是你必须创建这个额外的数组,但它使你的条件更简单。

(我保留了您代码中的 "new Random()",但其他 answers/comments 指的是您应该使用单个实例,请记住这一点)