Як довільно розмістити об'єкти, які не перетинаються?


11

Я створюю навколишнє середовище для гри, яку я розвиваю. Я використовую OpenGLі кодую в Java.

Я намагаюся навмання розмістити дерева у своєму світі (щоб створити ліс), але не хочу, щоб моделі перетиналися (що відбувається, коли два дерева розміщені занадто близько один до одного). Ось малюнок того, про що я говорю:

введіть тут опис зображення

Я можу надати більше коду, якщо необхідно, але ось основні фрагменти. Я зберігаю свої предмети в ArrayListс List<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 і scaleXє шкалою в напрямку x і т.д.

Ви можете бачити, що я розміщую ці дерева випадковим чином random.nextFloat()для xта zпозицій, і обмежую діапазон, щоб дерева з’являлися в потрібному місці. Однак я хотів би якось контролювати ці позиції, так що якщо він спробує розмістити дерево занадто близько до раніше розміщеного дерева, він перерахує нову випадкову позицію. Я думав, що кожне дерево Entityможе мати інше поле, наприклад treeTrunkGirth, і якщо дерево розміщене на відстані між місцем розташування іншого дерева і його treeTrunkGirth, то воно перерахує нову позицію. Чи є спосіб досягти цього?

Я радий додати більше фрагментів коду та деталей за необхідності.


3
Вибір диск Пуассона повинен зробити цю роботу. Не знаю, чи найкраще це для цього і ніколи насправді не реалізовував / не використовував, але це здається принаймні гарним початком. Перегляньте цю статтю: devmag.org.za/2009/05/03/poisson-disk-sampling
Марс

@Mars Wow, це посилання дуже корисно, дякую. Я побачу, що я можу зробити, і, можливо, повернусь із власною відповіддю.
wcarhart

@Pikalek Так, я думаю, що питання, яке ви пов’язали, є дублікатом. Чи просто я б використовував площину xz як область для "зіркової карти", як в іншому питанні?
wcarhart

Так, використовуйте площину xz у вашому випадку. Крім того, використовуйте treeTrunkGirthзамість константи для визначення мінімальної відстані для розміщення дерева, якщо вони потребують змін.
Пікалек

@Pikalek Якщо ви додасте все це у відповідь, я виберу це як найкраще. Дякую за допомогу.
wcarhart

Відповіді:


15

А Пуассона-диск Відбір проб розподіл дозволить вибрати випадкові точки мінімальна відстань один від одного. Ваша ситуація схожа на це питання , але оскільки ваші дерева не є ідеалізованими точками, вам потрібно буде змінити перевірку відстані наступним чином: відстань між потенційним новим деревом та наявним деревом має бути менше суми їх радіусів .

Алгоритм Брідсона може ефективно вирішити задачу в 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

Так, я відредагував це, щоб зробити це явним.
Пікалек

Спробуйте використовувати math.pow on tree.r + other tree.r, 2 замість math.sqrt, sqrt, як правило, повільніше, ніж
Pow

1
@Ferrybig Порівнювати відстані у квадраті швидше, але це не змінює факту, що це алгоритм грубої сили & все одно буде O (n ^ 2). Якщо потрібне швидше рішення, використовуйте алгоритм Брідсона. Крім того, використання Math.pow(x,2)не обов'язково є кращим / відмінним, ніж використання, x*xяк обговорювалося тут .
Пікалек

1
У мене була схожа проблема з місцевістю, що забезпечує пристойне розповсюдження та відсутність перекриттів. Я фактично зробив варіацію, у моїй версії трес / пензлик випадковим чином розповсюджуються по місцевості. Потім я запускаю функцію розміщувати цю функцію, щоб перевірити відстань кожного предмета один до одного, де вони були занадто близькими, я розсунув їх. Це, мабуть, вплине на інші дерева в цьому районі. Я повторював це, поки у мене не було зіткнень. це повільніше, але те, що я також мав як бонус, були такі речі, як галявина (не скрізь вкрита!) і щільність дерева здавалася більш "цікавою".
ErnieDingo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.