Vẽ thế giới trò chơi Isometric


178

Cách chính xác để vẽ gạch đẳng cự trong trò chơi 2D là gì?

Tôi đã đọc các tài liệu tham khảo (chẳng hạn như tài liệu này ) đề xuất các ô được hiển thị theo cách sẽ khoanh vùng từng cột trong biểu diễn mảng 2D của bản đồ. Tôi tưởng tượng rằng chúng nên được vẽ nhiều hơn theo kiểu kim cương, trong đó những gì được vẽ trên màn hình liên quan chặt chẽ hơn với mảng 2D sẽ trông như thế nào, chỉ cần xoay một chút.

Có những ưu điểm hay nhược điểm của một trong hai phương pháp?

Câu trả lời:


505

Cập nhật: Sửa thuật toán kết xuất bản đồ, thêm hình minh họa, thay đổi hình thức.

Có lẽ lợi thế cho kỹ thuật "zig-zag" để ánh xạ các ô tới màn hình có thể nói rằng các ô xytọa độ nằm trên trục dọc và trục ngang.

Phương pháp "Vẽ trong kim cương":

Bằng cách vẽ một bản đồ đẳng cự bằng cách sử dụng "vẽ trong một viên kim cương", mà tôi tin là chỉ hiển thị bản đồ bằng cách sử dụng một for-loop lồng nhau trên mảng hai chiều, chẳng hạn như ví dụ này:

tile_map[][] = [[...],...]

for (cellY = 0; cellY < tile_map.size; cellY++):
    for (cellX = 0; cellX < tile_map[cellY].size cellX++):
        draw(
            tile_map[cellX][cellY],
            screenX = (cellX * tile_width  / 2) + (cellY * tile_width  / 2)
            screenY = (cellY * tile_height / 2) - (cellX * tile_height / 2)
        )

Lợi thế:

Ưu điểm của phương pháp này là nó là một cái lồng đơn giản forvới logic khá thẳng về phía trước, hoạt động nhất quán trong tất cả các ô.

Bất lợi:

Một nhược điểm của cách tiếp cận đó là xytọa độ của các ô trên bản đồ sẽ tăng theo các đường chéo, điều này có thể gây khó khăn hơn cho việc ánh xạ trực quan vị trí trên màn hình vào bản đồ được biểu thị dưới dạng một mảng:

Hình ảnh bản đồ gạch

Tuy nhiên, sẽ có một khó khăn khi thực hiện mã ví dụ trên - thứ tự kết xuất sẽ khiến các ô được cho là nằm sau các ô nhất định được vẽ trên đầu các ô ở phía trước:

Kết quả hình ảnh từ thứ tự kết xuất không chính xác

Để sửa đổi vấn đề này, forthứ tự của vòng trong phải được đảo ngược - bắt đầu từ giá trị cao nhất và hiển thị về giá trị thấp hơn:

tile_map[][] = [[...],...]

for (i = 0; i < tile_map.size; i++):
    for (j = tile_map[i].size; j >= 0; j--):  // Changed loop condition here.
        draw(
            tile_map[i][j],
            x = (j * tile_width / 2) + (i * tile_width / 2)
            y = (i * tile_height / 2) - (j * tile_height / 2)
        )

Với cách khắc phục ở trên, việc hiển thị bản đồ cần được sửa:

Kết quả hình ảnh từ thứ tự kết xuất chính xác

Phương pháp "Zig-zag":

Lợi thế:

Có lẽ ưu điểm của phương pháp "zig-zag" là bản đồ được hiển thị có thể nhỏ gọn hơn một chút so với phương pháp "kim cương":

Cách tiếp cận Zig-zag để kết xuất có vẻ nhỏ gọn

Bất lợi:

Từ việc cố gắng thực hiện kỹ thuật zig-zag, nhược điểm có thể là khó hơn một chút để viết mã kết xuất bởi vì nó không thể được viết đơn giản như một for-loop lồng trên mỗi phần tử trong một mảng:

tile_map[][] = [[...],...]

for (i = 0; i < tile_map.size; i++):
    if i is odd:
        offset_x = tile_width / 2
    else:
        offset_x = 0

    for (j = 0; j < tile_map[i].size; j++):
        draw(
            tile_map[i][j],
            x = (j * tile_width) + offset_x,
            y = i * tile_height / 2
        )

Ngoài ra, có thể hơi khó khăn để cố gắng tìm ra tọa độ của ô do tính chất so le của thứ tự kết xuất:

Tọa độ trên một kết xuất thứ tự zig-zag

Lưu ý: Các hình minh họa có trong câu trả lời này được tạo ra bằng cách triển khai Java mã mã kết xuất gạch, với intmảng sau là bản đồ:

tileMap = new int[][] {
    {0, 1, 2, 3},
    {3, 2, 1, 0},
    {0, 0, 1, 1},
    {2, 2, 3, 3}
};

Các hình ảnh gạch là:

  • tileImage[0] -> Một hộp có một hộp bên trong.
  • tileImage[1] -> Một hộp đen.
  • tileImage[2] -> Một hộp màu trắng.
  • tileImage[3] -> Một hộp có một vật màu xám cao trong đó.

Lưu ý về chiều rộng và chiều cao của gạch

Các biến tile_widthtile_heightđược sử dụng trong các ví dụ mã ở trên đề cập đến chiều rộng và chiều cao của lát nền trong hình ảnh đại diện cho ô:

Hình ảnh hiển thị chiều rộng và chiều cao của gạch

Sử dụng kích thước của hình ảnh sẽ hoạt động, miễn là kích thước hình ảnh và kích thước ô phù hợp. Mặt khác, bản đồ ô có thể được hiển thị với các khoảng trống giữa các ô.


136
bạn thậm chí đã vẽ hình ảnh. Đó là nỗ lực.
zaratustra

Chúc mừng coobird. Tôi đang sử dụng phương pháp kim cương cho trò chơi hiện tại Tôi đang phát triển và nó đang hoạt động. Cảm ơn một lần nữa.
Benny Hallett

3
Còn nhiều lớp chiều cao thì sao? Tôi có thể vẽ chúng âm thầm, bắt đầu với mức thấp nhất và tiếp tục cho đến khi đạt mức cao nhất không?
NagyI

2
@DomenicDatti: Cảm ơn bạn vì những lời tốt đẹp của bạn :)
coobird

2
Điều này thật tuyệt. Tôi chỉ sử dụng phương pháp tiếp cận kim cương của bạn và trong trường hợp ai đó cần lấy lưới điện từ vị trí màn hình, tôi đã làm điều này : j = (2 * x - 4 * y) / tilewidth * 0.5; i = (p.x * 2 / tilewidth) - j;.
Kyr Dunenkoff

10

Dù bằng cách nào cũng hoàn thành công việc. Tôi giả sử rằng bằng cách ngoằn ngoèo, bạn có ý nghĩa như thế này: (số là thứ tự kết xuất)

..  ..  01  ..  ..
  ..  06  02  ..
..  11  07  03  ..
  16  12  08  04
21  17  13  09  05
  22  18  14  10
..  23  19  15  ..
  ..  24  20  ..
..  ..  25  ..  ..

Và bởi kim cương, ý bạn là:

..  ..  ..  ..  ..
  01  02  03  04
..  05  06  07  ..
  08  09  10  11
..  12  13  14  ..
  15  16  17  18
..  19  20  21  ..
  22  23  24  25
..  ..  ..  ..  ..

Phương pháp đầu tiên cần nhiều ô được hiển thị để toàn màn hình được vẽ, nhưng bạn có thể dễ dàng thực hiện kiểm tra ranh giới và bỏ qua mọi ô hoàn toàn ngoài màn hình. Cả hai phương pháp sẽ yêu cầu một số số giòn để tìm ra vị trí của ô 01. Cuối cùng, cả hai phương pháp đều gần bằng nhau về mặt toán học cần thiết cho một mức độ hiệu quả nhất định.


14
Tôi thực sự có nghĩa là cách khác xung quanh. Hình dạng kim cương (làm cho các cạnh của bản đồ trơn tru) và phương pháp zig-zag, để lại các cạnh nhọn
Benny Hallett

1

Nếu bạn có một số gạch vượt quá giới hạn kim cương của mình, tôi khuyên bạn nên vẽ theo thứ tự chiều sâu:

...1...
..234..
.56789.
..abc..
...d...

1

Câu trả lời của Coobird là chính xác, đầy đủ. Tuy nhiên, tôi đã kết hợp các gợi ý của anh ấy với những người từ một trang web khác để tạo mã hoạt động trong ứng dụng của tôi (iOS / Objective-C), mà tôi muốn chia sẻ với bất kỳ ai đến đây để tìm kiếm một thứ như vậy. Xin vui lòng, nếu bạn thích / bỏ phiếu cho câu trả lời này, hãy làm tương tự cho bản gốc; tất cả những gì tôi làm là "đứng trên vai những người khổng lồ".

Đối với thứ tự sắp xếp, kỹ thuật của tôi là thuật toán của họa sĩ đã sửa đổi: mỗi đối tượng có (a) độ cao của cơ sở (tôi gọi là "cấp độ") và (b) một X / Y cho "cơ sở" hoặc "chân" hình ảnh (ví dụ: cơ sở của avatar nằm dưới chân anh ấy, cơ sở của cây nằm ở gốc rễ; cơ sở của máy bay là hình ảnh trung tâm, v.v.) Sau đó, tôi chỉ sắp xếp từ thấp nhất đến cao nhất, rồi thấp nhất (cao nhất trên màn hình) đến cơ sở cao nhất Y, sau đó thấp nhất (ngoài cùng bên trái) đến cao nhất-X. Điều này làm cho gạch theo cách người ta mong đợi.

Mã để chuyển đổi màn hình (điểm) thành ô (ô) và trở lại:

typedef struct ASIntCell {  // like CGPoint, but with int-s vice float-s
    int x;
    int y;
} ASIntCell;

// Cell-math helper here:
//      http://gamedevelopment.tutsplus.com/tutorials/creating-isometric-worlds-a-primer-for-game-developers--gamedev-6511
// Although we had to rotate the coordinates because...
// X increases NE (not SE)
// Y increases SE (not SW)
+ (ASIntCell) cellForPoint: (CGPoint) point
{
    const float halfHeight = rfcRowHeight / 2.;

    ASIntCell cell;
    cell.x = ((point.x / rfcColWidth) - ((point.y - halfHeight) / rfcRowHeight));
    cell.y = ((point.x / rfcColWidth) + ((point.y + halfHeight) / rfcRowHeight));

    return cell;
}


// Cell-math helper here:
//      http://stackoverflow.com/questions/892811/drawing-isometric-game-worlds/893063
// X increases NE,
// Y increases SE
+ (CGPoint) centerForCell: (ASIntCell) cell
{
    CGPoint result;

    result.x = (cell.x * rfcColWidth  / 2) + (cell.y * rfcColWidth  / 2);
    result.y = (cell.y * rfcRowHeight / 2) - (cell.x * rfcRowHeight / 2);

    return result;
}

1

Bạn có thể sử dụng khoảng cách euclide từ điểm cao nhất và gần nhất với người xem, ngoại trừ điều đó không hoàn toàn đúng. Nó dẫn đến thứ tự sắp xếp hình cầu. Bạn có thể nói thẳng điều đó bằng cách nhìn từ xa. Xa hơn nữa độ cong trở nên phẳng. Vì vậy, chỉ cần thêm 1000 cho mỗi thành phần x, y và z để cho x ', y' và z '. Sắp xếp trên x '* x' + y '* y' + z '* z'.


0

Vấn đề thực sự là khi bạn cần vẽ một số ô / họa tiết giao nhau / kéo dài hai hoặc nhiều ô khác.

Sau 2 tháng (khó khăn) các vấn đề cá nhân của vấn đề, cuối cùng tôi đã tìm thấy và thực hiện một "bản vẽ hoàn trả chính xác" cho trò chơi cocos2d-js mới của mình. Giải pháp bao gồm lập bản đồ, cho mỗi ô (dễ bị), mà các sprite là "phía trước, phía sau, trên và phía sau". Sau khi làm điều đó, bạn có thể vẽ chúng theo "logic đệ quy".

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.