3D方法实现2D斜视角地图
一、斜视角地图介绍
2D斜视角地图的游戏多了去了,例如《暗黑》、《突袭》、《模拟人生》、《盟军敢死队》。这些游戏之所以采用这种视角的地图,那就是开发公司能够回答的了的问题了。
这里介绍暴雪的《魔兽争霸3》,就是类似《模拟人生2》的那种地图制作方法。
玩过看过《魔兽争霸3》的朋友问了,它不是3D地图吗?这里简单说明一下,其实我认为《魔兽争霸3》的地图就是2D的,之所以看上去像3D的效果,只是貌似神合罢了,实际上是用3D的方法,来实现的2D场景。我反倒是认为《盟军敢死队》看上去虽然是2D效果的地图,但是实际上,它确是用2D方法来实现3D场景。(题外话:看到此处,如果你仍然认为《魔兽争霸3》是3D地图的,是因为你我看问题的角度不同,请不要往下看了,就此打住好了,会越看越迷糊的。呵呵)
二、3D实现2D斜视角地图的好处
A、 可以全方位转来转去,调整视角,视觉效果酷一点。而且晚上白天的变化处理也很轻松。
B、 遮挡关系处理简单,纯2D的画,总是要处理物件之间的关系,有点复杂。
C、 微软的DX新版本已经不建议使用DDraw 了。
D、 程序方面比纯2D来实现,代码量少且简单。美术方面工作量要少。整体费用上开源节流阿。
三、3D实现2D斜视角地图的坏处
E~Z都是,这里就不一一介绍了。因为主要还是介绍好处,坏处说多了,偏离主题了。嘿嘿嘿。
四、3D实现2D斜视角地图的制作
既然是用3D方法来构建2D的地图,你至少要先了解关于D3D或者OGL的一些乱七八糟的函数,以及一些基本3D知识。
A、 建立网格地表
好比是256*256个网格吧,每个网格是2个三角成组成,就是看上去一马平川的那种,如图:
我放了一个小人在地图中,可以看出这张地图大小还算不错。等待今后全部完成该这张地图后,当让真正程序调用该地图时,若在P4-2.5G的机器上,总耗时不会超过1秒钟。如果有时间优化的话,应该还能够快一些。用过这个地图编辑器的朋友应该知道,该地图文件平均在250k左右的大小就能够描述了。
需要分为两个步骤解释,
1、 为这些网格建立一个结构来描述,大体上应该是这样的
struct T_TERRAIN{
float fx,fy,fz;//存储每个网格点的位置和高度,高度初始化为0
float fNormalx,fNormaly,fNormalz;//存储每个网格点的法线数据
};
T_TERRALN* pTerrain = new T_TERRALN[257*257];//注意为什么是257呢,因为在<wc3>编辑器中,建立一张256*256的地图,实际上就是建立一个257*257大小的地图。当然没有必要这样做,我这里只是便于统一变量,避免混淆而已。
存储顺序是左下角为x=0,y=0,横向(宽度)方向。每个格子的跨度为128.0f
第一个步骤结束,这就拥有了一个基本地形数据,当然你可以现在就用这些数据将其按照网格方式渲染出来。当然如果使用实体方式的话,就是白板一块了,如果想表现如图所示,接着进入下一步,给网格贴图。
这里需要解释一下为什么地形中我们需要高度z呢?不是说好了是2D地图吗?用z就是让地图今后有高低起伏的效果,实际上z在游戏中没有任何用途,例如游戏中两个人的距离判断,只是需要x和y,不管z的事情。
2、 为地形贴图,先展示以下我们需要什么样的图素呢?
实际上这是一张带通道的tga 图片,为了方便理解,我转换成2张了,第一张是效果图片,第二张是通道图片。图片大小为512*256,每64*64为一个网格使用的图素。其中右边一片是为了表示地图好看,随机抽取的图素,当然如果需要考虑贴图所占用的资源,右边的也可以不要。
从通道贴图,很容易看出,这些贴图意味着它将贴在什么位置,做什么用的。见图:
诺大的一个“六”字,应该能够分辨出它的对应关系了吧。例如“六”字的那个点的右上角对应通道从上往下从左往右,坐标为03的贴图。其他就不用一一解释了。
这些贴图你当然可以制作成为一个个分解后的贴图,不过就显得有些浪费了。暴雪之所以将其做成一张图片,当然出了节省外,还有当地图地表自动拼接的时候,贴图间相互叠加融合,这样的摆放方法就显得格外精巧,煞费苦心了。
现在知道了这些地表图素的样式了,我们就需要给那些顶点付UV值了。
Struct T_UV{
Float fu,fv;
};
T_UV* pUV = new T_UV[257*257*N];
注意这里为什么需要*N呢?这个N是什么东西呢?这个还是放在以下的章节来说吧[滞后问题1]。我们先假定N是1,这样就好理解了吧。
然后给这些顶点设置相应的uv 值。
至此,这些数据足以渲染一张平板地图生成了。“六”字贴图的这样不算哦。
B、 让地图看高低起伏起来
就是托拽这些顶点,修改这些顶点的高度数据Z,注意千万不要修改x和y,因为x和y永远是固定的。
以下介绍一个简单的方法,只拉动其中的一个顶点。如图:
也分为2步骤
1、 确定是打算提升地表高度呢?还是打算降低地表高度。
2、 在场景中拾取这个点,找到这个点,然后左右晃动鼠标,拼命的修改z的值,+/-一直到这个点的极限。注意在这种地图编辑器中,该点不是无限拉高或者降低的。这里的极限高度是周围8个点的平均高度的128.0f,也就是一个步长了。
注意,修改顶点的同时,不要忘记计算法线了。现在开始将该地图各个点的高度一通乱改,重新再看这个地图,就变成这样了。[注意我这里是重新生成的]
C、 地表贴图。[上文提到的那个滞后问题1解]
上文提到关于uv 的那个N的变量问题,即
T_UV* pUV = new T_UV[257*257*N];
《wc3》的地表变化多样(一般是7-8种,但是理论上无穷无尽的),然后拼凑起来的如上图,经过分析,我们可以知道,地图没个顶点最多有可能需要保存4种效果的图素uv 。为什么我这里用N了,N表示可能有多少种贴图。我这样做只是方便解释。正式代码时候,可千万别用这种存储方式,超级浪费了。
比如,0123的网格里面用了四张图素,假定为ABCD四张贴图,实际上在画这个网格的时候,一共需要画4遍。
就是A贴图画这个2个三角形,B贴图画这个2个三角形,C贴图画这个2个三角形,D贴图画这个2个三角形。
在<wc3>中,是需要根据贴图的顺序来画网格的,就是说假定游戏中用到了8种地表贴图,顺序安排是ABCDEFGH,画的时候,一种种的按照顺序来画。你也可以将其理解为photoshop 中层的概念。
当然在渲染地图的时候,需要剪裁,就是不在摄像机视角范围内的就不要画了。此种地图我觉得用四叉树就可以了,简单又方便。当然还有其他更多更好剪裁的方法,根据需要,自己决定吧。
D、 摆放物品
每个物件就是模型文件,将其放在合适的xy 上就可以了,z是根据地表的z计算得出的。
在摆放物件的时候,还需要生成障碍点,就是标示那些地方能走哪些地方不能走的标志。
Struct T_OBPOINT{
Uint8 btWalk;
};
Uint8* pbtWalk = new uint8[257*4*257*4];
之所以*4是因为<wc3>的障碍点设置是将每个网格的细分长宽各4。这样做有一个好处,就是游戏中不需要和物件进行3D碰撞检测了。至于每个物品的障碍范围是在配置文件中写好的。
E、 砌墙凿洞
这个和放置物品几乎是一样的,不过这个墙这个东西,这个需要考察美术人人员的能力了。我抓了几个模型做图解。可以不难理解,这个和地表图素的做法几乎一致。
注意当地表4个顶点不一致的时候,这些墙的模型顶点也是是需要进行转换的。就是依次根据4个点作为影响因子对墙的模型顶点进行转换。就好比布被风吹动的那种效果方法一样,网上有很多这样的资料介绍。