如何随机放置不重叠的实体?


11

我正在为正在开发的游戏创建随机生成的环境。我正在使用OpenGL和编码Java

我试图将树木随机放置在我的世界中(以创建森林),但是我不希望模型重叠(当两棵树之间的距离太近时会发生这种情况)。这是我在说什么的图片:

在此处输入图片说明

如果需要,我可以提供更多代码,但这是必要的代码片段。我存储在我的对象ArrayListList<Entity> entities = new ArrayList<Entity>();。然后,我使用以下方法将该列表添加到该列表中:

Random random = new Random();
for (int i = 0; i < 500; i++) {
    entities.add(new Entity(tree, new Vector3f(random.nextFloat() * 800 - 400, 
    0, random.nextFloat() * -600), 0, random.nextFloat() * 360, 0, 3, 3, 3);
}

其中每个都Entity遵循以下语法:

new Entity(modelName, positionVector(x, y, z), rotX, rotY, rotZ, scaleX, scaleY, scaleZ);

rotX是沿x轴的旋转,是x方向scaleX上的比例,等等。

您会看到我将这些树随机放置random.nextFloat()xz位置,并限制了范围,以便树将出现在所需的位置。但是,我想以某种方式控制这些位置,以便如果它试图将树放置得过于靠近先前放置的树,它将重新计算新的随机位置。我以为每棵树Entity都可以有另一个字段,例如treeTrunkGirth,并且如果将树放置在另一棵树的位置与其的距离之内treeTrunkGirth,那么它将重新计算一个新位置。有没有办法做到这一点?

我很高兴根据需要添加更多代码段和详细信息。


3
泊松圆盘采样应做的工作。不知道这是否是最好的选择,并且从未真正实现/使用过,但至少看起来是一个好的开始。查看本文:devmag.org.za/2009/05/03/poisson-disk-sampling
Mars

@Mars Wow,该链接非常有帮助,谢谢。我会看看我能做些什么,也许会回来回答我自己的问题。
wcarhart

@Pikalek是的,我认为您链接的问题是重复的。像其他问题一样,我是否仅将xz平面用作“星图”的区域?
wcarhart

是的,请根据情况使用xz平面。另外,treeTrunkGirth如果需要改变树木的距离,请使用而不是常数来确定最小距离。
皮卡列克

@Pikalek如果将所有内容加在一个答案中,我会选择它作为最佳答案。谢谢您的帮助。
wcarhart

Answers:


15

一个泊松磁盘抽样分布将允许您选择除了随机点的最小距离。您的情况与此问题类似,但是由于树不是理想点,因此您需要按以下方式更改距离检查:可能的新树与现有树之间的距离必须小于其半径的总和。

Bridson的算法可以有效地解决O(n)中的问题,但是对可变距离进行微调可能有点麻烦。如果您的参数太低和/或您正在预先计算地形,则蛮力解决方案也可能为您服务。以下是一些示例代码,它们通过对照所有先前放置的树检查每个潜在的新树放置来蛮力解决该问题:

public static class SimpleTree{
    float x;
    float z;
    float r;

    public SimpleTree(Random rng, float xMax, float zMax, float rMin, float rMax){
        x = rng.nextFloat() * xMax;
        z = rng.nextFloat() * zMax;
        r = rng.nextFloat() * (rMax-rMin) + rMin;
    }
}

private static ArrayList<SimpleTree> buildTreeList(float xMax, float zMax, 
        float rMin, float rMax, int maxAttempts, Random rng) {
    ArrayList<SimpleTree> result = new ArrayList<>();

    SimpleTree currentTree = new SimpleTree(rng, xMax, zMax, rMin, rMax);
    result.add(currentTree);

    boolean done = false;
    while(!done){
        int attemptCount = 0;
        boolean placedTree = false;
        Point nextPoint = new Point();
        SimpleTree nextTree = null;
        while(attemptCount < maxAttempts && !placedTree){
            attemptCount++;
            nextTree = new SimpleTree(rng, xMax, zMax, rMin, rMax);
            if(!tooClose(nextTree, result)){
                placedTree = true;
            }
        }
        if(placedTree){
            result.add(nextTree);
        }
        else{
            done = true;
        }
    }

    return result;
}

private static boolean tooClose(SimpleTree tree, ArrayList<SimpleTree> treeList) {
    for(SimpleTree otherTree : treeList) {
        float xDiff = tree.x - otherTree.x;
        float zDiff = tree.z - otherTree.z;

        float dist = (float)Math.sqrt((xDiff * xDiff) + (zDiff * zDiff));
        if(dist < tree.r + otherTree.r){
            return true;
        }
    }        
    return false;
}

具有以下参数:

 maxAttempts = 500;
 width = 300;
 height = 200;
 minSize = 2;
 maxSize = 15;

我能够在一秒钟内随机放置并渲染400-450棵树。这是一个例子: 在此处输入图片说明


这是使用Poisson磁盘采样吗?
wcarhart

是的,我已进行编辑以使其明确。
皮卡列克

尝试在tree.r + other tree.r,2 上使用math.pow ,而不是math.sqrt,sqrt通常比pow慢
Ferrybig 2013年

1
@Ferrybig比较平方距离比较快,但这并不能改变它是蛮力算法并且仍然为O(n ^ 2)的事实。如果需要更快的解决方案,请使用Bridson算法。此外,使用Math.pow(x,2)不一定比使用任何更好/不同的x*x这里讨论
Pikalek '16

1
我在地形方面也遇到了类似的问题,并确保了适当的传播并且没有重叠。我实际上做了一个变体,在我的版本中,发束/画笔随机分布在整个地形区域。然后,我在此之后运行一个函数,以检查每个项目彼此之间的距离,如果它们之间距离太近,则将它们推开。尽管这可能会影响该地区的其他树木。我重复了这一过程,直到没有碰撞为止。它的速度较慢,但​​我还有一个好处是诸如空地(并非到处都有覆盖!)和树木密度似乎更“有趣”的东西。
ErnieDingo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.