Thuật toán ốp lát bản đồ


153

Bản đô

Tôi đang tạo một game nhập vai dựa trên gạch bằng Javascript, sử dụng các độ cao nhiễu perlin, sau đó chỉ định loại gạch dựa trên chiều cao của nhiễu.

Các bản đồ cuối cùng trông giống như thế này (trong chế độ xem bản đồ nhỏ).

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

Tôi có một thuật toán khá đơn giản, trích xuất giá trị màu từ mỗi pixel trên ảnh và chuyển đổi nó thành một số nguyên (0-5) tùy thuộc vào tư thế của nó giữa (0-255) tương ứng với một ô trong từ điển ô vuông. Mảng 200x200 này sau đó được chuyển đến máy khách.

Công cụ sau đó xác định các ô từ các giá trị trong mảng và vẽ chúng vào khung vẽ. Vì vậy, tôi kết thúc với những thế giới thú vị có những đặc điểm nhìn thực tế: núi, biển, v.v.

Bây giờ điều tiếp theo tôi muốn làm là áp dụng một số loại thuật toán trộn có thể khiến các khối kết hợp liền mạch với hàng xóm của chúng, nếu hàng xóm không cùng loại. Bản đồ ví dụ ở trên là những gì người chơi nhìn thấy trong bản đồ nhỏ của họ. Trên màn hình họ thấy một phiên bản kết xuất của phần được đánh dấu bởi hình chữ nhật màu trắng; trong đó các ô được hiển thị với hình ảnh của chúng chứ không phải là các pixel màu đơn.

Đây là một ví dụ về những gì người dùng sẽ nhìn thấy trên bản đồ nhưng nó không cùng vị trí với chế độ xem ở trên cho thấy!

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

Theo quan điểm này, tôi muốn quá trình chuyển đổi xảy ra.

Thuật toán

Tôi đã đưa ra một thuật toán đơn giản sẽ đi ngang qua bản đồ trong chế độ xem và hiển thị một hình ảnh khác trên đầu mỗi ô, với điều kiện nó nằm cạnh một ô khác. (Không thay đổi bản đồ! Chỉ hiển thị một số hình ảnh bổ sung.) Ý tưởng của thuật toán là lập hồ sơ cho hàng xóm của ô hiện tại:

Một ví dụ về hồ sơ gạch

Đây là một kịch bản ví dụ về những gì động cơ có thể phải hiển thị, với ô hiện tại là ô được đánh dấu X.

Một mảng 3x3 được tạo và các giá trị xung quanh nó được đọc. Vì vậy, trong ví dụ này, mảng sẽ trông như thế nào.

[
    [1,2,2]
    [1,2,2]
    [1,1,2]
];

Ý tưởng của tôi sau đó là tìm ra một loạt các trường hợp cho các cấu hình gạch có thể. Ở mức độ rất đơn giản:

if(profile[0][1] != profile[1][1]){
     //draw a tile which is half sand and half transparent
     //Over the current tile -> profile[1][1]
     ...
}

Cung cấp kết quả này:

Kết quả

Mà hoạt động như một trình chuyển đổi từ [0][1]tới [1][1], nhưng không phải từ [1][1]đến [2][1], trong đó một hài cốt cạnh cứng. Vì vậy, tôi đã tìm ra rằng trong trường hợp đó, một góc gạch sẽ phải được sử dụng. Tôi đã tạo ra hai tấm sprite 3x3 mà tôi nghĩ sẽ chứa tất cả các kết hợp gạch có thể cần thiết. Sau đó, tôi đã sao chép điều này cho tất cả các ô có trong trò chơi (Các vùng màu trắng trong suốt). Điều này kết thúc là 16 gạch cho mỗi loại gạch (Các gạch trung tâm trên mỗi spritesheet không được sử dụng.)

CátCát2

Kết quả lý tưởng

Vì vậy, với các ô mới và thuật toán chính xác, phần ví dụ sẽ trông như thế này:

Chính xác

Mọi nỗ lực tôi đã thực hiện đều thất bại, luôn có một số lỗ hổng trong thuật toán và các mô hình kết thúc kỳ lạ. Tôi dường như không thể có được tất cả các trường hợp đúng và nhìn chung nó có vẻ như là một cách làm kém.

Một giải pháp?

Vì vậy, nếu bất cứ ai có thể cung cấp một giải pháp thay thế như làm thế nào tôi có thể tạo hiệu ứng này, hoặc hướng đi nào để viết thuật toán định hình, thì tôi sẽ rất biết ơn!


7
Hãy xem bài viết này và các bài viết được liên kết là tốt, đặc biệt là bài viết này . Bản thân blog chứa rất nhiều ý tưởng có thể đóng vai trò là điểm khởi đầu. Dưới đây là một cái nhìn tổng quan.
Darcara

bạn nên đơn giản hóa thuật toán của bạn. kiểm tra cái này: Two-Dimensional-Cellular-Automata
user1097361

Câu trả lời:


117

Ý tưởng cơ bản của thuật toán này là sử dụng bước tiền xử lý để tìm tất cả các cạnh và sau đó chọn gạch làm mịn chính xác theo hình dạng của cạnh.

Bước đầu tiên sẽ là tìm tất cả các cạnh. Trong ví dụ bên dưới các ô cạnh được đánh dấu X là tất cả các ô màu xanh lá cây với một ô màu nâu là một hoặc nhiều trong số tám ô lân cận của chúng. Với các loại địa hình khác nhau, điều kiện này có thể chuyển thành một ô vuông là một ô cạnh nếu nó có hàng xóm có số địa hình thấp hơn.

Gạch cạnh.

Khi tất cả các ô cạnh được phát hiện, điều tiếp theo cần làm là chọn gạch làm mịn phù hợp cho từng ô cạnh. Dưới đây là đại diện của tôi về gạch mịn của bạn.

Gạch trơn.

Lưu ý rằng thực tế không có nhiều loại gạch khác nhau. Chúng ta cần tám ô bên ngoài từ một trong các hình vuông 3x3 nhưng chỉ có bốn ô vuông góc khác vì các ô cạnh thẳng đã được tìm thấy trong hình vuông đầu tiên. Điều này có nghĩa là có tổng cộng 12 trường hợp khác nhau, chúng ta phải phân biệt giữa.

Bây giờ, nhìn vào một ô cạnh chúng ta có thể xác định đường biên quay theo cách nào bằng cách nhìn vào bốn ô lân cận gần nhất của nó. Đánh dấu một ô cạnh bằng X như trên, chúng ta có sáu trường hợp khác nhau sau đây.

Sáu trường hợp.

Những trường hợp này được sử dụng để xác định gạch làm mịn tương ứng và chúng ta có thể đánh số gạch làm mịn phù hợp.

Gạch trơn với số.

Vẫn có một lựa chọn a hoặc b cho mỗi trường hợp. Điều này phụ thuộc vào phía cỏ. Một cách để xác định điều này có thể là theo dõi hướng của ranh giới nhưng có lẽ cách đơn giản nhất để làm là chọn một ô bên cạnh và xem nó có màu gì. Hình ảnh dưới đây cho thấy hai trường hợp 5a) và 5b) có thể được phân biệt giữa bằng cách kiểm tra màu của ô trên cùng bên phải.

Chọn 5a hoặc 5b.

Bảng liệt kê cuối cùng cho ví dụ ban đầu sẽ trông như thế này.

Bảng liệt kê cuối cùng.

Và sau khi chọn gạch cạnh tương ứng, đường viền sẽ trông giống như thế này.

Kết quả cuối cùng.

Như một lưu ý cuối cùng tôi có thể nói rằng điều này sẽ hoạt động miễn là ranh giới có phần đều đặn. Chính xác hơn, gạch cạnh không có chính xác hai gạch cạnh vì hàng xóm của chúng sẽ phải được xử lý riêng. Điều này sẽ xảy ra đối với các ô cạnh trên cạnh của bản đồ sẽ có một hàng xóm cạnh duy nhất và cho các mảnh địa hình rất hẹp trong đó số lượng các ô cạnh lân cận có thể là ba hoặc thậm chí bốn.


1
Điều này là rất tốt và rất hữu ích cho tôi. Tôi đang xử lý một trường hợp trong đó một số gạch không thể chuyển trực tiếp sang các gạch khác. Ví dụ: gạch "bụi bẩn" có thể chuyển thành "cỏ nhẹ" và "cỏ nhẹ" có thể chuyển sang "cỏ trung bình". Tiled (mapeditor.org) thực hiện công việc tuyệt vời này bằng cách thực hiện một số loại tìm kiếm cây cho bàn chải địa hình; Tôi vẫn chưa thể tái tạo nó, mặc dù.
Clay

12

Hình vuông sau đây đại diện cho một tấm kim loại. Có một "lỗ thông hơi nhiệt" ở góc trên bên phải. Chúng ta có thể thấy làm thế nào khi nhiệt độ của điểm này không đổi, tấm kim loại hội tụ đến nhiệt độ không đổi tại mỗi điểm, nóng hơn một cách tự nhiên gần đỉnh:

tấm gia nhiệt

Vấn đề tìm nhiệt độ tại mỗi điểm có thể được giải quyết dưới dạng "Bài toán giá trị biên". Tuy nhiên, cách đơn giản nhất để giải nhiệt ở mỗi điểm là mô hình tấm như một lưới. Chúng tôi biết các điểm trên lưới ở nhiệt độ không đổi. Chúng tôi đặt nhiệt độ của tất cả các điểm chưa biết là nhiệt độ phòng (như thể lỗ thông hơi chỉ vừa được bật). Sau đó chúng tôi để nhiệt lan tỏa qua tấm cho đến khi chúng tôi đạt được sự hội tụ. Điều này được thực hiện bằng cách lặp: chúng tôi lặp qua từng điểm (i, j). Chúng tôi đặt điểm (i, j) = (điểm (i + 1, j) + điểm (i-1, j) + điểm (i, j + 1) + điểm (i, j-1)) / 4 [trừ khi điểm (i, j) có lỗ thông hơi nhiệt độ không đổi]

Nếu bạn áp dụng điều này cho vấn đề của mình, nó rất giống nhau, chỉ là màu trung bình thay vì nhiệt độ. Bạn có thể sẽ cần khoảng 5 lần lặp. Tôi đề nghị sử dụng lưới 400x400. Đó là 400x400x5 = ít hơn 1 triệu lần lặp sẽ nhanh. Nếu bạn chỉ sử dụng 5 lần lặp, có lẽ bạn sẽ không cần lo lắng về việc giữ bất kỳ điểm nào có màu không đổi, vì chúng sẽ không thay đổi quá nhiều so với ban đầu (thực tế chỉ có các điểm trong khoảng cách 5 từ màu có thể bị ảnh hưởng bởi màu). Mã giả:

iterations = 5
for iteration in range(iterations):
    for i in range(400):
        for j in range(400):
            try:
                grid[i][j] = average(grid[i+1][j], grid[i-1][j],
                                     grid[i][j+1], grid[i][j+1])
            except IndexError:
                pass

bạn có thể mở rộng thêm một chút không? Tôi tò mò, và tôi không thể hiểu lời giải thích của bạn. Làm thế nào để một người sử dụng giá trị màu trung bình sau khi bạn thực hiện các bước lặp?
Chii

1
Mỗi lưới điểm lưới [i] [j] có thể được vẽ vào khung vẽ dưới dạng một hình chữ nhật nhỏ (hoặc pixel riêng lẻ) có màu thích hợp.
vua robert

5

Ok, vì vậy, suy nghĩ đầu tiên là tự động hóa một giải pháp hoàn hảo cho vấn đề đòi hỏi một số phép toán nội suy khá phức tạp. Dựa trên thực tế là bạn đề cập đến hình ảnh gạch được kết xuất sẵn, tôi cho rằng giải pháp nội suy đầy đủ không được bảo hành ở đây.

Mặt khác như bạn đã nói, hoàn thiện bản đồ bằng tay sẽ dẫn đến một kết quả tốt ... nhưng tôi cũng cho rằng bất kỳ quy trình thủ công nào để khắc phục sự cố cũng không phải là một lựa chọn.

Đây là một thuật toán đơn giản không mang lại một kết quả hoàn hảo, nhưng điều đó rất bổ ích dựa trên nỗ lực thấp.

Thay vì cố gắng trộn MỌI ô cạnh, (có nghĩa là bạn cần biết kết quả của việc trộn các ô liền kề trước - nội suy hoặc bạn cần tinh chỉnh toàn bộ bản đồ nhiều lần và không thể dựa vào các ô được tạo trước) Tại sao không pha trộn gạch trong một mô hình bảng kiểm tra xen kẽ?

[1] [*] [2]
[*] [1] [*]
[1] [*] [2]

Tức là chỉ pha trộn các viên gạch được gắn dấu sao trong ma trận trên?

Giả sử rằng các bước duy nhất được phép về giá trị là một lần, bạn chỉ có một vài ô để thiết kế ...

A    [1]      B    [2]      C    [1]      D    [2]      E    [1]           
 [1] [*] [1]   [1] [*] [1]   [1] [*] [2]   [1] [*] [2]   [1] [*] [1]   etc.
     [1]           [1]           [1]           [1]           [2]           

Tổng cộng sẽ có 16 mẫu. Nếu bạn tận dụng sự đối xứng quay và phản xạ sẽ còn ít hơn nữa.

'A' sẽ là kiểu xếp gạch [1] đơn giản. 'D' sẽ là một đường chéo.

Sẽ có những bất đồng nhỏ ở các góc của gạch, nhưng những điều này sẽ không đáng kể so với ví dụ bạn đã đưa ra.

Nếu tôi có thể tôi sẽ cập nhật bài viết này với hình ảnh sau.


Điều này nghe có vẻ tốt, tôi sẽ thấy thú vị khi xem nó với một số hình ảnh để hiểu rõ hơn về ý của bạn.
Dan Prince

Tôi không thể đặt bất kỳ hình ảnh nào với nhau vì tôi không có phần mềm mà tôi nghĩ rằng tôi có ... Nhưng tôi đã suy nghĩ và đó không phải là một giải pháp tốt như nó có thể. Chắc chắn bạn có thể thực hiện chuyển đổi đường chéo, nhưng các chuyển đổi khác không thực sự được trợ giúp bởi thuật toán làm mịn này. Bạn thậm chí không thể đảm bảo rằng bản đồ của bạn sẽ chứa các chuyển tiếp KHÔNG 90 độ. Xin lỗi, tôi đoán đây là một chút buông xuống.
cầu toàn

3

Tôi đã chơi xung quanh với một cái gì đó tương tự như thế này, nó đã không kết thúc vì một số lý do; nhưng về cơ bản, nó sẽ lấy một ma trận 0 và 1, 0 làm mặt đất và 1 là một bức tường cho một ứng dụng tạo mê cung trong Flash. Vì AS3 tương tự như JavaScript, sẽ không khó để viết lại trong JS.

var tileDimension:int = 20;
var levelNum:Array = new Array();

levelNum[0] = [1, 1, 1, 1, 1, 1, 1, 1, 1];
levelNum[1] = [1, 0, 0, 0, 0, 0, 0, 0, 1];
levelNum[2] = [1, 0, 1, 1, 1, 0, 1, 0, 1];
levelNum[3] = [1, 0, 1, 0, 1, 0, 1, 0, 1];
levelNum[4] = [1, 0, 1, 0, 0, 0, 1, 0, 1];
levelNum[5] = [1, 0, 0, 0, 0, 0, 0, 0, 1];
levelNum[6] = [1, 0, 1, 1, 1, 1, 0, 0, 1];
levelNum[7] = [1, 0, 0, 0, 0, 0, 0, 0, 1];
levelNum[8] = [1, 1, 1, 1, 1, 1, 1, 1, 1];

for (var rows:int = 0; rows < levelNum.length; rows++)
{
    for (var cols:int = 0; cols < levelNum[rows].length; cols++)
    {
        // set up neighbours
        var toprow:int = rows - 1;
        var bottomrow:int = rows + 1;

        var westN:int = cols - 1;
        var eastN:int = cols + 1;

        var rightMax =  levelNum[rows].length;
        var bottomMax = levelNum.length;

        var northwestTile =     (toprow != -1 && westN != -1) ? levelNum[toprow][westN] : 1;
        var northTile =         (toprow != -1) ? levelNum[toprow][cols] : 1;
        var northeastTile =     (toprow != -1 && eastN < rightMax) ? levelNum[toprow][eastN] : 1;

        var westTile =          (cols != 0) ? levelNum[rows][westN] : 1;
        var thistile =          levelNum[rows][cols];
        var eastTile =          (eastN == rightMax) ? 1 : levelNum[rows][eastN];

        var southwestTile =     (bottomrow != bottomMax && westN != -1) ? levelNum[bottomrow][westN] : 1;
        var southTile =         (bottomrow != bottomMax) ? levelNum[bottomrow][cols] : 1;
        var southeastTile =     (bottomrow != bottomMax && eastN < rightMax) ? levelNum[bottomrow][eastN] : 1;

        if (thistile == 1)
        {
            var w7:Wall7 = new Wall7();
            addChild(w7);
            pushTile(w7, cols, rows, 0);

            // wall 2 corners

            if      (northTile === 0 && northeastTile === 0 && eastTile === 1 && southeastTile === 1 && southTile === 1 && southwestTile === 0 && westTile === 0 && northwestTile === 0)
            {
                var w21:Wall2 = new Wall2();
                addChild(w21);
                pushTile(w21, cols, rows, 270);
            }

            else if (northTile === 0 && northeastTile === 0 && eastTile === 0 && southeastTile === 0 && southTile === 1 && southwestTile === 1 && westTile === 1 && northwestTile === 0)
            {
                var w22:Wall2 = new Wall2();
                addChild(w22);
                pushTile(w22, cols, rows, 0);
            }

            else if (northTile === 1 && northeastTile === 0 && eastTile === 0 && southeastTile === 0 && southTile === 0 && southwestTile === 0 && westTile === 1 && northwestTile === 1)
            {
                var w23:Wall2 = new Wall2();
                addChild(w23);
                pushTile(w23, cols, rows, 90);
            }

            else if (northTile === 1 && northeastTile === 1 && eastTile === 1 && southeastTile === 0 && southTile === 0 && southwestTile === 0 && westTile === 0 && northwestTile === 0)
            {
                var w24:Wall2 = new Wall2();
                addChild(w24);
                pushTile(w24, cols, rows, 180);
            }           

            //  wall 6 corners

            else if (northTile === 1 && northeastTile === 1 && eastTile === 1 && southeastTile === 0 && southTile === 1 && southwestTile === 1 && westTile === 1 && northwestTile === 1)
            {
                var w61:Wall6 = new Wall6();
                addChild(w61);
                pushTile(w61, cols, rows, 0); 
            }

            else if (northTile === 1 && northeastTile === 1 && eastTile === 1 && southeastTile === 1 && southTile === 1 && southwestTile === 0 && westTile === 1 && northwestTile === 1)
            {
                var w62:Wall6 = new Wall6();
                addChild(w62);
                pushTile(w62, cols, rows, 90); 
            }

            else if (northTile === 1 && northeastTile === 1 && eastTile === 1 && southeastTile === 1 && southTile === 1 && southwestTile === 1 && westTile === 1 && northwestTile === 0)
            {
                var w63:Wall6 = new Wall6();
                addChild(w63);
                pushTile(w63, cols, rows, 180);
            }

            else if (northTile === 1 && northeastTile === 0 && eastTile === 1 && southeastTile === 1 && southTile === 1 && southwestTile === 1 && westTile === 1 && northwestTile === 1)
            {
                var w64:Wall6 = new Wall6();
                addChild(w64);
                pushTile(w64, cols, rows, 270);
            }

            //  single wall tile

            else if (northTile === 0 && northeastTile === 0 && eastTile === 0 && southeastTile === 0 && southTile === 0 && southwestTile === 0 && westTile === 0 && northwestTile === 0)
            {
                var w5:Wall5 = new Wall5();
                addChild(w5);
                pushTile(w5, cols, rows, 0);
            }

            //  wall 3 walls

            else if (northTile === 0 && eastTile === 1 && southTile === 0 && westTile === 1)
            {
                var w3:Wall3 = new Wall3();
                addChild(w3);
                pushTile(w3, cols, rows, 0);
            }

            else if (northTile === 1 && eastTile === 0 && southTile === 1 && westTile === 0)
            {
                var w31:Wall3 = new Wall3();
                addChild(w31);
                pushTile(w31, cols, rows, 90);
            }

            //  wall 4 walls

            else if (northTile === 0 && eastTile === 0 && southTile === 1 && westTile === 0)
            {
                var w41:Wall4 = new Wall4();
                addChild(w41);
                pushTile(w41, cols, rows, 0);
            }

            else if (northTile === 1 && eastTile === 0 && southTile === 0 && westTile === 0)
            {
                var w42:Wall4 = new Wall4();
                addChild(w42);
                pushTile(w42, cols, rows, 180);
            }

            else if (northTile === 0 && northeastTile === 0 && eastTile === 1 && southeastTile === 0 && southTile === 0 && southwestTile === 0 && westTile === 0 && northwestTile === 0)
            {
                var w43:Wall4 = new Wall4();
                addChild(w43);
                pushTile(w43, cols, rows, 270);
            }

            else if (northTile === 0 && northeastTile === 0 && eastTile === 0 && southeastTile === 0 && southTile === 0 && southwestTile === 0 && westTile === 1 && northwestTile === 0)
            {
                var w44:Wall4 = new Wall4();
                addChild(w44);
                pushTile(w44, cols, rows, 90);
            }

            //  regular wall blocks

            else if (northTile === 1 && eastTile === 0 && southTile === 1 && westTile === 1)
            {
                var w11:Wall1 = new Wall1();
                addChild(w11);
                pushTile(w11, cols, rows, 90);
            }

            else if (northTile === 1 && eastTile === 1 && southTile === 1 && westTile === 0)
            {
                var w12:Wall1 = new Wall1();
                addChild(w12);
                pushTile(w12, cols, rows, 270);
            }

            else if (northTile === 0 && eastTile === 1 && southTile === 1 && westTile === 1)
            {
                var w13:Wall1 = new Wall1();
                addChild(w13);
                pushTile(w13, cols, rows, 0);
            }

            else if (northTile === 1 && eastTile === 1 && southTile === 0 && westTile === 1)
            {
                var w14:Wall1 = new Wall1();
                addChild(w14);
                pushTile(w14, cols, rows, 180);
            }

        }
        // debug === // trace('Top Left: ' + northwestTile + ' Top Middle: ' + northTile + ' Top Right: ' + northeastTile + ' Middle Left: ' + westTile + ' This: ' + levelNum[rows][cols] + ' Middle Right: ' + eastTile + ' Bottom Left: ' + southwestTile + ' Bottom Middle: ' + southTile + ' Bottom Right: ' + southeastTile);
    }
}

function pushTile(til:Object, tx:uint, ty:uint, degrees:uint):void
{
    til.x = tx * tileDimension;
    til.y = ty * tileDimension;
    if (degrees != 0) tileRotate(til, degrees);
}

function tileRotate(tile:Object, degrees:uint):void
{
    // http://www.flash-db.com/Board/index.php?topic=18625.0
    var midPoint:int = tileDimension/2;
    var point:Point=new Point(tile.x+midPoint, tile.y+midPoint);
    var m:Matrix=tile.transform.matrix;
    m.tx -= point.x;
    m.ty -= point.y;
    m.rotate (degrees*(Math.PI/180));
    m.tx += point.x;
    m.ty += point.y;
    tile.transform.matrix=m;
}

Về cơ bản, điều này kiểm tra mọi ô xung quanh nó đi từ trái sang phải, từ trên xuống dưới và giả sử rằng các ô cạnh luôn là 1. Tôi cũng có quyền tự do xuất hình ảnh dưới dạng tệp để sử dụng làm khóa:

gạch ốp tường

Điều này là không đầy đủ và có lẽ là một cách khó khăn để đạt được điều này, nhưng tôi nghĩ nó có thể có ích.

Chỉnh sửa: Ảnh chụp màn hình kết quả của mã đó.

Tạo kết quả


1

Tôi muốn đề xuất một vài điều:

  • nó không quan trọng gạch "trung tâm" là gì, phải không? nó có thể là 2, nhưng nếu tất cả những cái khác là 1, nó sẽ hiển thị 1?

  • nó chỉ quan trọng các góc là gì, khi có sự khác biệt trong các hàng xóm ngay lập tức lên đầu hoặc bên. Nếu tất cả các hàng xóm ngay lập tức là 1, và một góc là 2, nó sẽ hiển thị 1.

  • Tôi có thể sẽ tính toán trước tất cả các kết hợp có thể có của các lân cận, tạo ra một mảng 8 chỉ số với bốn chỉ số đầu tiên cho biết các giá trị của các lân cận trên / dưới và thứ hai chỉ ra các đường chéo:

cạnh [N] [E] [S] [W] [NE] [SE] [SW] [NW] = bất cứ điều gì bù vào sprite

vì vậy trong trường hợp của bạn, [2] [2] [1] [1] [2] [2] [1] [1] = 4 (sprite thứ 5).

trong trường hợp này, [1] [1] [1] [1] sẽ là 1, [2] [2] [2] [2] sẽ là 2, và phần còn lại sẽ phải được giải quyết. Nhưng việc tìm kiếm một gạch cụ thể sẽ là tầm thường.

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.