瓦片地图(Tiled Map)系列文章:ui
前段时间在作游戏的地图编辑功能,咱们是在一个斜45度视角的场景上,对地图上的建筑或装饰物进行添加、移动、移除等基本操做,并且位置的改变是以网格做为最小操做单位的。本渣是用Staggered Tiled Map实现的,与垂直视角的Tiled Map不一样,斜45度视角处理起来相对麻烦些,此次就聊聊其中一些跟数学相关的有趣问题。code
<!-- more -->blog
不解释,有图有真相XD排序
'Orthogonal Tiled Map'游戏
'Isometric Tiled Map'图片
'Hexagonal Tiled Map'ip
‘Hexagonal Tiled Map’资源
地图编辑的一个基础功能即是判断当前被编辑的建筑或装饰物的位置的合法性。这种合法性检查主要有两方面:一是不能放置在地图上被禁止编辑的区域(例如地图上的河流、山坡),这能够经过在Tiled Map上在相应区域作上标记,判断建筑所在的区域是否有该标记就能够了;二是不能与其余建筑或装饰物重叠,这即是这里主要要讨论的问题了。
在实际作位置判断时,咱们并不是按照每一个建筑或装饰物的图片实际轮廓,而是把它们都对应到Tiled Map上一块以网格线为边的区域上——在斜45度视角下,这样的区域就是一个平行四边形。所以,建筑或装饰物是否重叠的问题便转化为在Tiled Map上的两个平行四边形是否相交的问题。再作进一步简化,咱们发现这其实只须要判断任一平行四边形的四个顶点的瓦片(tile)是否落在另外一个平行四边形内部就能够了。
看到这里,你也许已经发现:这不就是中学里简单的代数问题吗?判断点是否在平行四边形内,只须要知道平行四边形四条边所在直线的方程和点的坐标,便迎刃而解。没错,Tiled Map里每一块瓦片区域有本身的坐标,咱们只须要把一块瓦片的坐标当作点的坐标,直线方程和点的坐标就有了。但斜45度角Staggered Tiled Map的有趣之处在于,即使把瓦片当作点,获得的并非一个常见的平面直角坐标系。
下面咱们仍是经过图片来看看Staggered Tiled Map的坐标:
为了方便起见,咱们把向下做为y轴正方向,咱们能够发现上面的坐标(x, y)有着以下规律:
-- columnNum表示列数 -- columnNum = 1, 2, 3, ... x = (columnNum - 1) % 2 -- rowNum表示行数 -- rowNum = 1, 2, 3, ... y = rowNum - 1
看上去好像不复杂,但要列出直线方程呢?好比如下两种可做为平行四边形瓦片区域的边的直线:
一时半会懵逼了吧?使问题复杂的正是这些公式须要判断奇偶性,能不能把奇偶性判断拿掉呢?固然能够,作坐标转换就能够了,让咱们先看一张直观的坐标转换结果图:
这至关于作了以下的坐标转换:
f[(x, y)] = (x * 2, y) if y mod 2 = 0 f[(x, y)] = (x * 2 + 1, y) if y mod 2 = 1
合并成一条公式也就是:
f[(x, y)] = (x * 2 + y mod 2, y)
如今是否是简单得多了?上面两条直线方程(由于这样的直线斜率是肯定的,只须要知道直线上一点的坐标就能够肯定直线方程)分别是:
-- linear equation of the row line containing the point of tileCoord local function rowEquation(tileCoord) return tileCoord.x * 2 + tileCoord.y % 2 + tileCoord.y end
-- linear equation of the column line containing the point of tileCoord local function columnEquation(tileCoord) return tileCoord.x * 2 + tileCoord.y % 2 - tileCoord.y end
这个时候你应该已经发现,咱们能够用以前简单的代数问题解法来解决这一问题了:已知点的坐标tileCoord
和一个区域region
四个顶点的坐标(分别为region.top
、region.bottom
、region.left
、region.right
,事实上只须要知道其中两个点就能够了),判断点是否在区域内,只须要作不等式判断便可:
local function containsTile(region, tileCoord) return (rowEquation(tileCoord) >= rowEquation(region.top)) and (rowEquation(tileCoord) <= rowEquation(region.bottom)) and (columnEquation(tileCoord) <= columnEquation(region.top)) and (columnEquation(tileCoord) >= columnEquation(region.bottom))
你也许会问,为何不直接定义一套方便计算的坐标系统?为什么要用Staggered Tiled Map原有的坐标系统去作变换呢?这是由于前面所提到的作标记的非法编辑区域是采用原有的坐标系统的,采用同一套坐标系统加简单的坐标转换处理比起采用两套坐标系统,在实现上和维护上的成本更低。
地图编辑的另外一个基础需求是要处理好建筑及装饰物之间的遮挡关系。这个问题能够转化为建筑或装饰物的显示层级的排序。可是问题又来了,如何比较任意两个建筑或装饰物的显示前后顺序呢?特别是它们还有可能隔得很远,并无显示上的重叠区域?
这个问题其实没有固定答案,本渣也只是根据咱们系统的实际状况定了一套排序规则。在本渣的规则中,建筑或装饰物的显示层级只与它们对应的平行四边形区域有关。本渣用每个的平行四边形左侧和右侧瓦片的坐标点获得一条直线,并把平行四边形对角线交点做为基准点。当比较两个建筑或装饰物的显示前后时,先判断两者平行四边形的宽度(两条相邻边的tile数量),短的求基准点,长的求直线。若是点在直线下方,则点所对应的建筑或装饰物在前方;反之则在后方。
固然,本渣这套规则实际能显示正确还有赖于美术大大们提供的建筑或装饰物图片资源的状况哈,若是建筑或装饰物的轮廓宽度超过它们的平行四边形区域,仍是可能会出现显示奇怪的地方的。