Tại sao tôi không thể vá địa hình thủ tục của mình lại với nhau?


7

Tôi đã tìm ra cách thực hiện thuật toán dịch chuyển điểm giữa để tạo bản đồ cho trò chơi của mình. Tôi muốn tạo ra một thế giới rộng lớn vô tận, vì vậy tôi đã cố gắng vá hai bản đồ lại với nhau, nhưng chúng trông không được liền mạch. Tôi nhớ thuật toán Diamond-vuông ...

Trên trang Wikipedia cho thuật toán Diamond-vuông có ghi:

Khi được sử dụng với các giá trị góc ban đầu nhất quán, phương pháp này cũng cho phép các fractals được tạo được ghép lại với nhau mà không bị gián đoạn

Sau khi đọc xong, tôi quyết định chuyển đổi phương pháp chuyển vị giữa của mình thành phương pháp mà tôi tin là phương pháp vuông kim cương thích hợp. Tuy nhiên, nó vẫn không có vẻ liền mạch mặc dù tôi sử dụng "giá trị góc ban đầu nhất quán".
Phương pháp hoạt động hoàn hảo khác.
Đây là hai bản đồ được tạo cạnh nhau:Hai bản đồ được tạo cạnh nhau

Như bạn có thể thấy, có những điểm không liên tục lớn (mặc dù có vẻ như chúng gần như có thể khớp với nhau). Những gì tôi viết không phải là một phương pháp hình vuông kim cương "thật", hoặc có thể tôi đang hiểu sai bài viết trên Wikipedia.

Vì vậy, nói cách khác, câu hỏi của tôi là: Điều gì sai với mã của tôi hoặc sự hiểu biết của tôi ngăn tôi ghép các bản đồ lại với nhau?
Cảm ơn rất nhiều!

public static float[][] generateMap(int power, float topLeft, float topRight, float bottomLeft, float bottomRight, float error, float persistence, boolean normalize, long seed){
Random random = new Random(seed);

int size = (int)Math.pow(2, power) + 1;

float[][] data = new float[size][size];

// these are for the normalization at the end.
// does it even do anything?
float min = MathHelper.min(topLeft, topRight, bottomLeft, bottomRight);
float max = MathHelper.max(topLeft, topRight, bottomLeft, bottomRight);

// set the corners to the initial values.
data[0][0] = topLeft;
data[size-1][size-1] = bottomRight;
data[0][size-1] = topRight;
data[size-1][0] = bottomLeft;

for (int i = 0; i < power; i++){

    int square = size / (int)Math.pow(2, i);
    int half = square / 2;

    for (int x = 0; x < size - square; x += square){
        for (int y = 0; y < size - square; y += square){

            // find the values of the corners of the square.
            float tl = data[y][x];
            float bl = data[y + square][x];
            float tr = data[y][x + square];
            float br = data[y + square][x + square];

            // find the values of the corners of the diamond (if they exist).
            Float xt = (y - square - half >= 0) ? data[y-square-half][x+half] : null;
            Float xb = (y + square + half < size) ? data[y+square+half][x+half] : null;
            Float xl = (x - square - half >= 0) ? data[y+half][x-square-half] : null;
            Float xr = (x + square + half < size) ? data[y+half][x+square+half] : null;

            // set the square's center to the average of the square's corners plus a random error.
            float centerVal = (tl + bl + tr + br) / 4.0f;
            centerVal += ((random.nextFloat() * 2) - 1) * error;
            data[y+half][x+half] = centerVal;

            // set the diamonds' centers to the average of the diamonds' corners (that exist) plus a random error.
            float leftVal = (tl + bl + centerVal + (xl != null ? xl : 0)) / (3.0f + (xl != null ? 1.0f : 0.0f));
            leftVal += ((random.nextFloat() * 2) - 1) * error;
            data[y+half][x] = leftVal;

            float rightVal = (tr + br + centerVal + (xr != null ? xr : 0)) / (3.0f + (xr != null ? 1.0f : 0.0f));
            rightVal += ((random.nextFloat() * 2) - 1) * error;
            data[y+half][x+square] = rightVal;

            float topVal = (tl + tr + centerVal + (xt != null ? xt : 0)) / (3.0f + (xt != null ? 1.0f : 0.0f));
            topVal += ((random.nextFloat() * 2) - 1) * error;
            data[y][x+half] = topVal;

            float bottomVal = (bl + br + centerVal + (xb != null ? xb : 0)) / (3.0f + (xb != null ? 1.0f : 0.0f));
            bottomVal += ((random.nextFloat() * 2) - 1) * error;
            data[y+square][x+half] = bottomVal;

            max = MathHelper.max(max, centerVal, leftVal, rightVal, topVal, bottomVal);
            min = MathHelper.min(min, centerVal, leftVal, rightVal, topVal, bottomVal);

        }
    }

    // reduce random error.
    error *= persistence;
}

// does this even do anything?
if (normalize) {
    float div = max - min;
    for (int i = 0; i < size; i++)
        for (int j = 0; j < size; j++)
            data[i][j] /= div;
}

return data;

}


3
Nếu bạn sẵn sàng thay đổi thuật toán một lần nữa, lấy mẫu từ hàm nhiễu như Perlin Noise sẽ không gặp vấn đề này.
Chaosed0

@ Chaosed0 Chưa kể nó nhìn có vẻ tốt hơn và cho phép bạn kiểm soát kết quả nhiều hơn bằng cách cân nhắc các đóng góp tương đối từ nhiều tần số.
bcrist

Câu trả lời:


6

Trước khi Diamond-Square bắt đầu, bạn sẽ phải đảm bảo các ranh giới ngoài cùng (và số lượng điểm giữa tiềm năng tối đa được tạo ở đó) được đặt bằng nhau ở hai bên của bản đồ (tính theo x và y). Chỉ sau đó, bạn có thể bắt đầu thế hệ đầy đủ của trung tâm với một cái gì đó tiếp cậnmột bọc liền mạch. Ý nghĩa của "nhất quán" là "tất cả các góc và cạnh ngoài cùng cần cùng một giá trị", bởi vì khi gói, về bản chất, chúng là cùng một điểm (gốc). Đặt các góc ban đầu thành 0, sau đó xây dựng cạnh trái bằng cách sử dụng dịch chuyển trung điểm 1D, sao chép nó sang cạnh phải, sau đó thực hiện tương tự với đỉnh và đáy (cuối cùng, các góc vẫn phải bằng 0, nhưng các góc giữa có thể là bất cứ thứ gì) . Ngoài ra, bạn chỉ có thể đặt tất cả các điểm biên thành không. Bây giờ bạn đã có "khung" bản đồ, vì vậy khi chạy Diamond-Square, đừng chạm vào các điểm biên - chỉ cần sử dụng chúng.

tôi nói tiếp cận bởi vì bạn, điều này vẫn có thể không dẫn đến một kết quả mong muốn (tùy theo quan điểm của bạn), vì hoạt động của thuật toán Diamond-Square, theo mặc định, không xử lý khái niệm liên tục qua các ranh giới bao bọc. Vì vậy, nó có vẻ hơi giả tạo, IIRC. Nếu đây là một vấn đề, hãy tìm cách thực hiện một số cách làm mịn qua các ranh giới, có thể bằng cách chọn cực đại / cực tiểu cục bộ ngẫu nhiên ở hai bên của ranh giới và mở rộng chúng qua ranh giới hoặc xem xét các thuật toán khác để tạo địa hình của bạn. .. hoặc mất yêu cầu bọc.


1

Trong đoạn mã riêng của tôi (chỉ để tìm hiểu việc tạo địa hình theo thủ tục), thay vì cố gắng ghép hai bản đồ lại với nhau (ví dụ: hai bản đồ 64x64), tôi tạo một bản đồ lớn hơn (ví dụ: 128x128, bao bọc), sau đó vứt bỏ phần dưới cùng. Điều này để lại cho tôi một bản đồ 64x128 bao bọc theo chiều ngang nhưng không theo chiều dọc (đó là những gì tôi muốn).


1

Tôi đã nghĩ về điều này, nghĩa là tạo ra các chiều cao phù hợp bằng cách sử dụng hình vuông kim cương. Tôi không biết điều này sẽ hoạt động tốt như thế nào, vì tôi chưa bao giờ thử thực hiện nó, nhưng đây là định đề lý thuyết của tôi:

Đối với mọi điểm dọc theo một cạnh, nếu phần bản đồ liền kề tồn tại, hãy tạo điểm bằng với điểm cạnh liền kề.

Điều trông giống như bây giờ là bạn chỉ đặt các góc cho các góc của bản đồ liền kề, nhưng không theo dõi các điểm khác. Bởi vì mọi điểm khác là ngẫu nhiên, phần còn lại của cạnh không được đảm bảo để xếp hàng. Nhưng nếu bạn đã chèn một kiểm tra bổ sung có nội dung: "Tôi có phải là một cạnh không? Ok, liệu một cạnh liền kề trên một phần bản đồ khác có tồn tại không? Ok, hiện tại tôi đang có giá trị đó thay vì giá trị ngẫu nhiên này", bạn có thể để có được nó để xếp hàng chính xác.

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.