斜角地图逻辑原理解析和Isometric地图编辑器设计方案

当我要做的一款游戏,而无法即时地查看地图各图形元件的拼接效果,而导致不得不中止时,当时找过不少公开的地图编辑器,希望能拿来即用,但不是因为拼接效果不满意,就是因为生成的文件不是太满意。无奈之下决心自己动手写一个工具时,才知道有多少困难。

论坛里的相关帖子,基本上都试着各种关键词看了一遍,说实在的,感觉上,除了几个原来做过斜角地图编辑的,其余帖子里基本上是迷茫的
感觉坛子里像wangqiang99x,闪刀浪子,恋水泥人,wxsr等几位应该还是比较熟悉的,但看了一下他们的帖子,还是感觉有些不够,直到看过杜宇欣的文章,才算解决了斜角地图中的各个障碍

在前一篇斜角地图原理解释及斜角图形绘制实例细述已经对斜角地图的原理进行了细致的阐述。这里就不再重复了,只从斜角地图的逻辑原理方面进行解析。

关于地图中的图形元件的拼接原理,请参考这篇文章闪刀浪子的地图编辑器对应的地图解析类及教程的前半部分,或者只看这个图

因为所有的图片都是矩形的,所以拼接时必然要有所叠加,这就是图片的遮挡。

而要处理图片的遮挡关系,才能使所有这些图片构成的地图所呈现的画面显示出立体效果。
而这种遮挡关系就是地图景深的处理,那么景深的处理都会涉及哪些关键点呢,下面请一个个来看。

1、图片之间的遮挡,这是景深呈现的关键

2、图形元件所代表的意义,即处于某个位置的图形元件是山水建筑物,还是路面等,以标识游戏当中的角色是可以通行还是受阻碍的

3、图形元件是否允许与用户的交互,即一个图形元件放到地图上,如果就是代表一棵树,一片小山,而这些又仅仅是表征地图地形的一个障碍物,那么则不会影响用户的键盘鼠标消息

4、如何通过鼠标当前位置(或角色当前位置),来了解所处的是哪里(什么地形,会不会产生事件等)

5、如何通过网格中某个单元格的纵列位置,来获得屏幕坐标。比如角色在某一个位置,而房子在另一个位置,现在角色要走到房子里,这就要获取某个建筑物所在的坐标,以使角色行走到目标位置

6、碰撞处理和寻路等,如果是地国中有运动的角色等类物体,就必须处理这方面的问题,这暂时不属于地图逻辑原理,也不属于地图编辑器的设计,暂不展开讨论

为了表述方便,我专门截图制作了下面的一张图片来进行一些要素的标注
 

从上图可以看出,以等距地图网格对象的顶点top为坐标系原点T(0,0),以汉字撇捺两个方向的纵列为坐标系的两个坐标轴,即Pie轴与top-right方向平行,Na轴与top-left平行。

这样比其他文章里建立的u-v坐标系在使用时更容易理解,也计算时也更便捷。
在接下来的计算中,各单元格也与网格坐标一样,全部以其顶点坐标代表该单元格的位置(屏幕坐标x,y);
而建立的Pie-Na坐标系,仅是通过单元格相对原点T的撇向序号,捺向序号(Pie,Na)数值对来表示该单元格在此网格上的序列位置。

我们通过分析一下网格的两个单元格关系,就可以得出此坐标系下的计算公式。
看图中标注的两个绿色点M(1,2),N(3,1),我们首先看一下,M点相对原点T其屏幕坐标是什么关系
Mx = cellHalfWidth * (Mpie - Mna) = -1 * cellHalfWidth,即相对原点T向左半个单元格宽度
My = cellHalfHeight * (Mpie + Mna) = 3 * cellHalfHeight,即相对原点T向下3倍的半个单元格高度

我们再用此公式来套一下N点,看下是否如实
Nx = cellHalfWidth * (Npie - Nna) = 2 * cellhalfWidth,看下图中,是不是刚好相差一个单元格宽度?
Ny = cellHalfHeight * (Npie + Nna) = 4 * cellHalfHeight,看下图中,也刚好相差2个单元格高度。

好吧,再看点M和N之间又如何呢
Width(M-N) = cellHalfWidth * ((Npie -Mpie) - (Nna-Mna)) = cellHalfWidth * (2 - (-1)) = 3 * cellHalfWidth,再对照一下图中,嗯,刚好是3倍的半个单元格宽度
Height(M-N) = cellHalfHeight * ((Npie - Mpie) + (Nna - Mna)) = cellHalfHeight * (2 + (-1)) = 1 * cellHalfHeight,看下图中,这个也没错!

那么经过一番验证,我们的公式就可以确定没问题了,
即,在上述以网格顶点T为原点的Pie-Na坐标中,任一单元格的屏幕位置Q(x,y)都有如下公式:
Qx = cellHalfWidth * (Qpie - Qna) + Tx
Qy = cellHalfHeight * (Qpie + Qna) + Ty

Width(Q) = cellHalfWidth * (Qpie - Qna)        //含正负,在原点左侧为负,右则为正
Height(Q) = cellHalfHeight * (Qpie + Qna)      //肯定为正

对于任意两点U(ux,uy),V(vx,vy)都有如下公式:
Vx = cellHalfWidth * (Vpie - Vna) + Ux
Vy = cellHalfHeight * (Vpie + Vna) + Uy

V到U的距离为
Width(V-U) = cellHalfWidth * ((Vpie-Upie) - (Vna-Una))        //含正负,V在U点左侧为负,右则为正
Height(V-U) = cellHalfHeight * ((Vpie-Upie) + (Vna-Una))      //肯定为正

看起来,上下两个公式没多大不同,不过在使用时,可以一目了然,还是比较方便,所以不嫌麻烦都列出来了

这解决了上面所说的哪个问题了,通过单元格的序列位置求出其屏幕位置,是不是第5个问题解决了呢。

大海航行靠舵手,在斜角地图的逻辑中,有了这样一个公式的出现,就可以顺次搞定以上的1~5的问题了,下面就不这么罗嗦了。

4、通过任一点屏幕坐标,求所在单元格的序列位置
直接把上面公式一翻转,再根据前一篇(还不知道斜角地图原理和细节的看这里)中所述的单元格的宽=2*单元格的高
可得出如下公式:
参数点Q(x,y)为Cell中任一点,其与网格的顶点坐标的距离在X、Y轴方向长度分别是Width(Q),Height(Q);
Qpie = (2 * Height(Q) + Width(Q)) / (cellHalfWidth * 2)
Qna = (2 * Height(Q) - Width(Q)) / (cellHalfHeight * 2)


3、是否允许用户交互,我们只要给图形元件设置这样一个属性即可

2、图元类型,同理,我们只需标识一下此图元的类型即可

1、遮挡关系,现在再去看下图中的单元格序列坐标,就可以看到,凡是单元格序列坐标小的,必然被单元格序列坐标大的遮挡
再来看稍微复杂点的单元格之间的遮挡关系,比如M,N

如果图片的宽度仅为单元格顶点left-right,则像M,N这样的点,根本不会发生遮挡关系,因为Mright与Nleft相距半个单元格宽度的,但如果其宽度都超出left-right的宽度,并

且都又比较高,这时仍然会发生遮挡关系的,那么该如何处理这个遮挡关系呢,非常快速,加减法而已
(Npie - Mpie) + (Nna - Mna) > 0,则N挡M,小于零,则M挡N。

那么又必然会存在相等的,比如M(1,2)与Q(2,1)看起来是平行的,这时怎么办呢
像这类,根本就是完全平行,即使是在视觉效果上,所以,怎么处理都无所谓了,左挡右,或者右挡左,都非常合理,完全视自己喜好了

但是在代码中,一般是不需要这么进行3次加减法运算,多数都会拦截在对pie,na序列值的if语句处。
当然,如果是循环设置地图上所有的图元遮挡关系,pie,na的两个for循环中更是直接

说到这里,斜角地图的逻辑原理,就已经没什么东西了,再来看地图编辑器该如何来设计

通常,我们会为地图绘制一幅背景图的,然后我们可能在这个背景图上添加一些建筑物等物体(甚至包括地面,NPC等),让整个游戏世界看起来像是真实世界的映射。

我们首先来看一下会有哪些东西要出现在这个地图当中

1、地图背景

2、网格对象

3、放置的物体(建筑、地面、NPC等)

4、角色对象

5、有时也需要一个可通行路径对象

一般来说,地图这个大容器可能会被塞进去很多的对象,当非常多的可视对象,尤其是交互类对象时,会使得相关的一些处理,比如遮挡关系的处理,碰撞检测,寻路等非常耗费系统资源,所以我们在规划时,就要考虑到可能出现的这些问题

对于上述前三类对象,是必须放置到地图这个容器当中去的。

而第3项,放置的物体有可能会包含两类,一类是非交互性物体,一类是交互性物体;
而处理方式,一般也有两种方式,即放置进的物体都是非交互性物体,而交互性物体放置到地图外面的另一专门层里,并且使之与地图容器对象等大,这个层容器里面,全部是响应用户的一些对象,但都是看不到的,当鼠标或角色移动到此类对象范围内时,就表现出交互性的半透明效果叠在地图中同样位置的对象上面,因为可响应用户的对象在地图中远远少于非交互对象。但这样一来,寻路和角色在运动时的遮挡处理也复杂化了,因为既然把交互对象移出了地图容器,那么地图容器一般都会设为完全不响应用户,角色有属于自己的层对象来容纳,而遮挡关系就只能靠贴图算法来实现了,当同屏幕有数量非常多的角色对象时,而主程能力又很给力时,有个优秀的贴图算法,可以实现非常高的效率和承载能力

但是一般较为常用的方法,还是采用非交互性物体和交互性物体都放置在地图容器中,而且角色也同样放置在地图容器中,这种处理方案一般在遇到地图在有大量物体,并且角色数量在3~4位数时,通常会遇到瓶颈,CPU和内存飞速上涨,而且会出现画面卡顿的现象。但是一般的小游戏都可以满足了,只要尽量消减一下地图中的非交互性对象数量即可,但话又说回来,如果采用消减地图中非交互性对象的数量,则一定会采用通行路径的处理办法,因为地图上大量不可通行的位置都在地图背景图片中来表现来的

newscreen348447000.png (42.56 KB, 下载次数: 20)

newscreen348447000.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是实现9*9 2D菱形tiled地图的Unity代码: 1. 首先,创建一个空白的GameObject,并将其命名为“Grid”。 2. 在Grid对象上添加一个名为“TileMap”的新组件。这个组件将负责绘制我们的地图。 3. 在TileMap组件中,设置Tile Width和Tile Height为1,Tile Padding为0。 4. 在TileMap组件中,创建一个新的Tile Palette,并添加9张菱形图像。这些图像可以是你自己创建的,或者从互联网上找到的。 5. 在TileMap组件中,为每个图块定义一个Tile Type。给每个Tile Type起一个有意义的名称,例如“Grass”、“Water”、“Rock”等。 6. 在TileMap组件中,创建一个新的Tile Brush,并将其命名为“Diamond”。在Tile Brush中,选择一个菱形图像,然后将其旋转45度,以使其成为菱形。 7. 在TileMap组件中,将我们刚刚创建的Tile Brush设置为默认的Paint Brush。 8. 在TileMap组件中,使用Paint Brush在Editor视图中绘制地图。使用不同的Tile Type来表示不同的地形。例如,使用“Grass”来表示草地,使用“Water”来表示水等等。 9. 在TileMap组件中,将“Isometric”选项设置为true,以使我们的地图成为菱形tiled地图。 10. 在TileMap组件中,将“Map Width”和“Map Height”设置为9,以创建一个9*9的地图。 11. 在TileMap组件中,将“Map Data”设置为一个9*9的数组。使用不同的Tile Type ID来表示不同的图块。例如,使用0来表示“Grass”,使用1来表示“Water”等等。 12. 在场景中添加一个新的摄像机,并将其命名为“Main Camera”。 13. 在Main Camera对象上添加一个新的组件,并将其命名为“CameraController”。这个组件将负责控制摄像机的移动。 14. 在CameraController组件中,添加以下代码: ``` using UnityEngine; public class CameraController : MonoBehaviour { public float speed = 2.0f; private void Update() { float horizontal = Input.GetAxis("Horizontal"); float vertical = Input.GetAxis("Vertical"); transform.Translate(new Vector3(horizontal, vertical, 0) * speed * Time.deltaTime); } } ``` 15. 在场景中运行游戏,并使用WASD或方向键控制摄像机在地图上移动。 这样,我们就可以实现一个简单的9*9 2D菱形tiled地图。你可以根据自己的需要,进一步扩展和优化这个地图
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值