产生不重叠的随机对象(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数组的索引。
xIndexMoney
和 xIndexFriend
是其他两个对象的 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 指的是您应该使用单个实例,请记住这一点)
我正在 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数组的索引。
xIndexMoney
和 xIndexFriend
是其他两个对象的 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 指的是您应该使用单个实例,请记住这一点)