Answers:
更新:纠正了地图渲染算法,添加了更多插图,更改了格式。
也许可以说将瓦片映射到屏幕的“之字形”技术的优势在于,瓦片x
和y
坐标位于垂直轴和水平轴上。
“画钻石”的方法:
通过使用“在菱形中绘制”绘制等距图,我相信这是指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)
)
优点:
该方法的优势在于它是一个简单的嵌套for
循环,具有相当简单的逻辑,可在所有图块中一致地工作。
坏处:
这种方法的一个缺点是,地图上图块的x
和y
坐标将以对角线增加,这可能使得将屏幕上的位置直观地映射到以数组表示的地图上更加困难:
但是,实现上面的示例代码将有一个陷阱-渲染顺序将导致应该位于某些图块后面的图块被绘制在前面的图块之上:
为了解决此问题,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)
)
通过上述修复,应该更正地图的呈现方式:
“之字形”方法:
优点:
“之字形”方法的优点也许在于,呈现的地图可能看起来比“钻石”方法在垂直方向上更加紧凑:
坏处:
从尝试实现之字形技术开始,缺点可能是编写渲染代码有点困难,因为它不能像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
)
另外,由于渲染顺序的交错性质,尝试计算图块的坐标可能会有些困难:
注意:此答案中包含的插图是使用所呈现的图块渲染代码的Java实现创建的,其中以下int
数组为地图:
tileMap = new int[][] {
{0, 1, 2, 3},
{3, 2, 1, 0},
{0, 0, 1, 1},
{2, 2, 3, 3}
};
磁贴图像为:
tileImage[0] ->
一个内有盒子的盒子。tileImage[1] ->
黑匣子。tileImage[2] ->
一个白盒子。tileImage[3] ->
一个带有高灰色物体的盒子。关于瓷砖宽度和高度的注意事项
上面的代码示例中使用的变量tile_width
和tile_height
是指表示图块的图像中图块的宽度和高度:
只要图像尺寸和图块尺寸匹配,就可以使用图像的尺寸。否则,可以使用图块之间的间隙来渲染图块地图。
j = (2 * x - 4 * y) / tilewidth * 0.5; i = (p.x * 2 / tilewidth) - j;
。
无论哪种方式都可以完成工作。我假设通过之字形表示的意思是这样的:(数字是渲染的顺序)
.. .. 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的位置。最后,在一定效率下,两种方法在数学上大致相等。
Coobird的答案是正确,完整的答案。但是,我将他的提示与其他站点的提示相结合,以创建适用于我的应用程序(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;
}
您可以使用距观看者最高和最近的点的欧几里得距离,除非那不太正确。结果为球形排序。您可以从更远的地方看清楚。更远处的曲率变得平坦。因此,只需在x,y和z分量中分别添加1000,即可得出x',y'和z'。在x'* x'+ y'* y'+ z'* z'上排序。
真正的问题是当您需要绘制与两个或多个其他图块相交/跨越的图块/子图时。
经过2个月的艰苦的个人分析,我终于为我的新cocos2d-js游戏找到并实现了“正确的渲染图”。解决方案包括为每个图块(敏感图)映射哪些精灵为“正面,背面,顶部和背面”。完成后,您可以按照“递归逻辑”绘制它们。