Làm cách nào để đặt ngẫu nhiên các thực thể không trùng nhau?


11

Tôi đang tạo một môi trường được tạo ngẫu nhiên cho một trò chơi tôi đang phát triển. Tôi đang sử dụng OpenGLvà mã hóa trong Java.

Tôi đang cố gắng đặt ngẫu nhiên các cây trong thế giới của mình (để tạo một khu rừng), nhưng tôi không muốn các mô hình chồng lên nhau (điều này xảy ra khi hai cây được đặt quá gần nhau). Đây là hình ảnh về những gì tôi đang nói về:

nhập mô tả hình ảnh ở đây

Tôi có thể cung cấp thêm mã nếu cần thiết, nhưng đây là đoạn trích cần thiết. Tôi đang lưu trữ các đối tượng của tôi trong một ArrayListvới List<Entity> entities = new ArrayList<Entity>();. Tôi sau đó thêm vào danh sách đó bằng cách sử dụng:

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);
}

trong đó mỗi Entitycái theo cú pháp sau:

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

rotXlà góc quay dọc theo trục x và scaleXlà tỷ lệ theo hướng x, v.v.

Bạn có thể thấy rằng tôi đang đặt các cây này một cách ngẫu nhiên random.nextFloat()cho các vị trí xzvị trí, và giới hạn phạm vi để các cây sẽ xuất hiện ở vị trí mong muốn. Tuy nhiên, tôi muốn bằng cách nào đó kiểm soát các vị trí này, để nếu nó cố gắng đặt một cây quá gần với cây được đặt trước đó, nó sẽ tính toán lại một vị trí ngẫu nhiên mới. Tôi đã nghĩ rằng mỗi cây Entitycó thể có một trường khác, chẳng hạn như treeTrunkGirth, và nếu một cây được đặt ở khoảng cách giữa vị trí của cây khác và nó treeTrunkGirth, thì nó sẽ tính toán lại một vị trí mới. Có cách nào để hoàn thành nó không?

Tôi rất vui khi thêm nhiều đoạn mã và chi tiết khi cần thiết.


3
Lấy mẫu đĩa Poisson nên làm công việc. Không biết nếu nó tốt nhất cho việc này và chưa bao giờ thực sự thực hiện / sử dụng nó, nhưng dường như ít nhất là một khởi đầu tốt. Kiểm tra bài viết này: devmag.org.za/2009/05/03/poisson-disk-sampling
Sao Hỏa

@Mars Wow, liên kết đó là siêu hữu ích, cảm ơn. Tôi sẽ xem những gì tôi có thể làm, và có thể trở lại với câu trả lời của riêng tôi.
wcarhart

@Pikalek Vâng, tôi nghĩ rằng câu hỏi mà bạn liên kết là trùng lặp. Tôi có thể sử dụng mặt phẳng xz làm khu vực cho "bản đồ sao" như trong câu hỏi khác không?
wcarhart

Có, sử dụng mặt phẳng xz trong trường hợp của bạn. Ngoài ra, sử dụng treeTrunkGirththay vì hằng số để xác định khoảng cách tối thiểu để đặt cây nếu chúng cần thay đổi.
Pikalek

@Pikalek Nếu bạn thêm tất cả vào câu trả lời, tôi sẽ chọn nó là câu trả lời hay nhất. Cảm ơn đã giúp đỡ.
wcarhart

Câu trả lời:


15

Một Poisson-Disk lấy mẫu phân phối sẽ cho phép bạn chọn điểm ngẫu nhiên khoảng cách tối thiểu ngoài. Tình huống của bạn tương tự như câu hỏi này , nhưng vì cây của bạn không được lý tưởng hóa nên bạn cần thay đổi kiểm tra khoảng cách như sau: khoảng cách giữa một cây mới tiềm năng và một cây hiện có, phải nhỏ hơn tổng bán kính của chúng .

Thuật toán của Bridson có thể giải quyết vấn đề một cách hiệu quả trong O (n), nhưng có thể hơi khó khăn để điều chỉnh nó cho các khoảng cách khác nhau. Nếu các thông số của bạn ở mức thấp & / hoặc bạn đang tính toán trước địa hình của mình, một giải pháp vũ lực cũng có thể phục vụ bạn. Dưới đây là một số mã mẫu có thể giải quyết vấn đề bằng cách kiểm tra mọi vị trí cây mới tiềm năng đối với tất cả các cây được đặt trước đó:

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;
}

Với các thông số sau:

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

Tôi đã có thể đặt ngẫu nhiên & kết xuất giữa 400-450 cây trong một giây. Đây là một ví dụ: nhập mô tả hình ảnh ở đây


Đây có phải là sử dụng lấy mẫu đĩa Poisson?
wcarhart

Có, tôi đã chỉnh sửa để làm cho rõ ràng.
Pikalek

Hãy thử sử dụng math.pow trên tree.r + other tree.r, 2, thay vì math.sqrt, sqrt thường chậm hơn pow
Ferrybig

1
@Ferrybig So sánh khoảng cách bình phương nhanh hơn, nhưng điều đó không thay đổi thực tế rằng đó là thuật toán vũ phu & vẫn sẽ là O (n ^ 2). Nếu cần một giải pháp nhanh hơn, hãy sử dụng thuật toán của Bridson. Ngoài ra, sử dụng Math.pow(x,2)không nhất thiết phải tốt hơn / khác với sử dụng x*xnhư được thảo luận ở đây .
Pikalek

1
Tôi đã có một vấn đề tương tự với địa hình và đảm bảo một số trải rộng và không chồng chéo. Tôi thực sự đã thực hiện một biến thể, trong phiên bản của tôi, tress / cọ được trải ngẫu nhiên trên khu vực địa hình. Sau đó tôi chạy một chức năng đăng bài này để kiểm tra khoảng cách của mọi vật phẩm với nhau, nơi chúng ở quá gần, tôi đẩy chúng ra xa nhau. Điều này mặc dù sẽ ảnh hưởng đến các cây khác trong khu vực. Tôi lặp lại điều này cho đến khi tôi không có va chạm. nó chậm hơn nhưng những gì tôi cũng có như một phần thưởng là những thứ như khoảng trống (không phải nơi nào cũng được che chắn!) và mật độ cây có vẻ "thú vị" hơn.
ErnieDingo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.