绘制等距游戏世界

在2D 游戏中绘制等距图的正确方法是什么?

我已经阅读了一些参考文献(比如 这个) ,它们建议在地图的2D 数组表示中以一种将每列呈锯齿形的方式呈现平铺。我想它们应该以菱形方式绘制,在这种方式下,被绘制到屏幕上的东西与2D 阵列的外观更加相关,只是稍微旋转一下。

这两种方法各有利弊吗?

130184 次浏览

更新: 修正了地图绘制算法,增加了更多的插图,改变了格式。

也许“之字形”技术将瓷砖映射到屏幕的优势可以说是瓷砖的 xy坐标在垂直和水平轴上。

“在钻石上画画”的方法:

通过使用“在菱形中绘制”来绘制等距映射,我相信这是指通过在二维数组上使用嵌套的 for循环来绘制映射,如下例所示:

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

优点:

这种方法的优点是,它是一个简单的嵌套 for循环,逻辑相当直接,可以在所有平铺图中始终如一地工作。

缺点:

这种方法的一个缺点是,地图上瓷砖的 xy坐标将增加对角线,这可能会使屏幕上的位置更难以直观地映射到以数组表示的地图:

Image of tile map

然而,实现上面的示例代码会有一个陷阱——呈现顺序会导致应该位于某些特定瓷砖后面的瓷砖被绘制在前面的瓷砖的顶部:

Resulting image from incorrect rendering order

为了修正这个问题,内部 for循环的顺序必须颠倒过来——从最高值开始,然后向更低的值渲染:

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

以上修正后,地图的渲染应该更正:

Resulting image from correct rendering order

“之字形”方法:

优点:

也许“之字形”方法的优势在于,渲染后的地图可能看起来比“菱形”方法更加垂直紧凑:

Zig-zag approach to rendering seems compact

缺点:

从尝试实现 zig-zag 技术来看,缺点可能是编写渲染代码有点困难,因为它不能像在数组中的每个元素上编写嵌套的 for循环那样简单:

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
)

另外,由于渲染顺序的交错特性,尝试确定一块瓷砖的坐标可能有点困难:

Coordinates on a zig-zag order rendering

注意: 这个答案中包含的插图是用提供的瓦片渲染代码的 Java 实现创建的,以下 int数组作为映射:

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

瓷砖图案如下:

  • 一个盒子,里面有一个盒子。
  • 一个黑匣子。
  • 白盒子。
  • 一个盒子,里面有一个高高的灰色物体。

关于瓷砖宽度和高度的一点注记

以上代码示例中使用的变量 tile_widthtile_height是指图像中代表地砖的地砖的宽度和高度:

Image showing the tile width and height

只要图像的尺寸和瓷砖的尺寸匹配,使用图像的尺寸就可以。否则,可以使用瓦片之间的间隙呈现瓦片映射。

不管怎样都能完成任务。我假设之字形的意思是这样的: (数字是渲染的顺序)

..  ..  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  ..  ..

你说的钻石是指:

..  ..  ..  ..  ..
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
..  ..  ..  ..  ..

第一种方法需要渲染更多的贴片,以便绘制全屏,但是您可以轻松地进行边界检查并跳过屏幕外的所有贴片。这两种方法都需要一些数字运算来找出瓦片01的位置。最后,这两种方法在一定程度的效率所需的数学方面大致相同。

如果你有一些瓷砖超出了菱形的范围,我建议按深度顺序绘制:

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

库伯德的回答是正确的,完整的。然而,我将他的提示与另一个网站的提示结合起来,创建了可以在我的应用程序(iOS/Objective-C)中工作的代码,我想与来这里寻找这样一个东西的人分享。如果你喜欢/赞成这个答案,请对原作也这样做; 我所做的只是“站在巨人的肩膀上”

至于排序顺序,我的技术是一个修改过的画家算法: 每个对象有(a)基础的高度(我称之为“水平”)和(b)一个 X/Y 的“基础”或“脚”的图像(例如: 阿凡达的基础是在他的脚; 树的基础是在它的根; 飞机的基础是中心图像,等等)然后我只是排序最低到最高的水平,然后最低(最高的屏幕上)到最高的基础-Y,然后最低(最左)到最高的基础-X。这使得瓷砖呈现出人们所期望的样子。

将屏幕(点)转换为平铺(单元格)和背面的代码:

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

真正的问题是当您需要绘制一些瓷砖/精灵交叉/跨越两个或更多其他瓷砖。

经过2个月(艰难)的个人分析问题,我终于发现并实现了一个“正确的渲染绘图”为我的新 cocos2d-js 游戏。 解决方案在于映射,对于每个瓷砖(易感) ,哪个精灵是“前,后,顶部和后面”。 一旦这样做了,你就可以按照“递归逻辑”来绘制它们。

你可以从最高点和最接近观察者的地方使用欧几里得度量,但这并不完全正确。它的结果是球形排序顺序。你可以通过远距离观察来理清这一点。在更远的地方,曲率变得平坦。所以只要给 x,y 和 z 的每个分量加上1000就可以得到 x’,y’和 z’。X’* x’+ y’* y’+ z’* z’上的排序。