绘制等距游戏世界

Ben*_*ett 177 2d isometric

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

我已经阅读了一些参考文献(例如这篇文章),它们建议以一种在地图的二维数组表示中对每一列进行锯齿形的方式渲染图块.我想它们应该以钻石方式绘制得更多,其中被绘制到屏幕上的内容更接近于2D阵列的外观,只是旋转了一点.

两种方法都有优缺点吗?

coo*_*ird 496

更新:更正了地图渲染算法,添加了更多插图,更改了格式化.

也许用于将瓷砖映射到屏幕的"之字形"技术的优点可以说是瓷砖xy坐标在垂直和水平轴上.

"绘制钻石"方法:

通过使用"绘制钻石"绘制等轴测图,我相信这只是for通过在二维数组上使用嵌套-loop 来渲染地图,例如:

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)
        )
Run Code Online (Sandbox Code Playgroud)

优点:

该方法的优点是它是一个简单的嵌套for-loop,具有相当直接的逻辑,可以在所有磁贴中一致地工作.

坏处:

这种方法的一个缺点是地图上的图块的坐标xy坐标将在对角线上增加,这可能使得将屏幕上的位置可视地映射到表示为数组的地图更加困难:

瓷砖地图的图像

但是,实现上面的示例代码会有一个陷阱 - 渲染顺序将导致应该在某些拼贴后面的拼贴画在前面拼贴的顶部:

不正确的渲染顺序导致的图像

为了修正这个问题,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)
        )
Run Code Online (Sandbox Code Playgroud)

通过上述修复,应该更正地图的渲染:

从正确的渲染顺序得到的图像

"Zig-zag"方法:

优点:

也许"锯齿形"方法的优点是渲染的地图可能看起来比"钻石"方法更紧凑一些:

Zig-zag渲染方法看起来很紧凑

坏处:

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

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
        )
Run Code Online (Sandbox Code Playgroud)

此外,由于渲染顺序的交错特性,尝试找出图块的坐标可能有点困难:

协调Z字形顺序渲染

注意:本答案中包含的插图是使用呈现的磁贴渲染代码的Java实现创建的,以下int数组作为映射:

tileMap = new int[][] {
    {0, 1, 2, 3},
    {3, 2, 1, 0},
    {0, 0, 1, 1},
    {2, 2, 3, 3}
};
Run Code Online (Sandbox Code Playgroud)

平铺图像是:

  • tileImage[0] -> 一个盒子里面有一个盒子.
  • tileImage[1] -> 黑匣子.
  • tileImage[2] -> 一个白色的盒子.
  • tileImage[3] -> 一个高灰色物体的盒子.

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

的变量tile_widthtile_height它们在上面的代码示例中使用指代表区块图像中的地砖的宽度和高度:

显示瓷砖宽度和高度的图像

只要图像尺寸和图块尺寸匹配,使用图像的尺寸就可以工作.否则,可以使用图块之间的间隙来渲染图块地图.

  • 你甚至画了画.这是努力. (133认同)
  • 多个高度层怎么样?我可以从最低级别开始绘制它们并继续直到达到最高级别吗? (3认同)
  • @DomenicDatti:谢谢你的客气话:) (2认同)
  • 这太棒了.我只是使用你的钻石方法,并且,如果有人需要从屏幕位置获得网格坐标,我这样做:`j =(2*x - 4*y)/ tilewidth*0.5; i =(px*2/tilewidth) - j;`. (2认同)

zar*_*tra 10

无论哪种方式完成工作.我假设zigzag你的意思是这样的:(数字是渲染的顺序)

..  ..  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  ..  ..
Run Code Online (Sandbox Code Playgroud)

钻石你的意思是:

..  ..  ..  ..  ..
  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
..  ..  ..  ..  ..
Run Code Online (Sandbox Code Playgroud)

第一种方法需要渲染更多的图块,以便绘制整个屏幕,但您可以轻松地进行边界检查并完全跳出屏幕上的任何图块.这两种方法都需要一些数字运算来找出tile 01的位置.最后,两种方法在一定效率水平所需的数学方面大致相等.

  • 我实际上是指另一种方式.钻石形状(使地图的边缘保持平滑)和锯齿形方法,留下边缘spikey (14认同)