重制AdvanceWars第一步 -- 搞定地图

首先来聊下高级战争吧
Advance Wars,由任天堂旗下的Intelligent Systems开发的战棋游戏。
初作诞生于GBA上,后来继续跟进了高战2黑洞崛,而后在下一代掌机DS上也出了三代续作高战DS,以及后来不太一样的毁灭日。
本人的高中时代正值GBA横行天下的时候,那时候最喜欢的事情就是晚上躲在宿舍厕所偷偷玩GBA(不熄灯)。有时候还和小伙伴联机打GT2赛车马里奥等等,而高战...这种时间吃货能和小伙伴联机一把就实在太珍贵了。
鉴于高战这种类型的硬核战棋在日本地区不受欢迎,估计以后是不会再出续作了。不过成为程序员的一个好处就是,自己做做这种游戏还是可以的。
除去了战棋类游戏漫长的回合太拖时间这种共有的硬伤之外,无需练级无需养成,兵种相克,个性鲜明的CO和强大的CO Power,高战绝对的硬核战棋,好游戏呀。
高战的核心玩法就是下棋,那么最重要的就是棋盘,也就是地图的制作。
作为一个穷屌程序员,我是没有资金投入买美术的,所以先去抠点图来。
抠图的方法有很多,其实老外很多网站都有很多这些古老游戏的资源。开始的时候我选择了通过GBA模拟器VisualBoy来抠图,方法是百度来的。
抠了一堆类似下面的图片之后,实在是不耐烦了。


还是先做点功能吧,比如在Unity里面拖个地图出来。然而......
Tile的大小、接合、不同地形的过渡....这要做一个地图编辑器太痛苦了。尤其是不同地形的过渡。比如海水和平地之间,九宫格的接壤就有八个边,还有登陆用的浅滩怎么办?海水里面还有礁石tile...道路的转向、交汇,哦天呐,这种东西不是那个年代几乎所有瓷砖游戏都有的吗?不如去找找工具吧!
于是被安利到一个工具:TiledMapEditor。百度一下就找到了官网,还是个开源的工具,下载是免费的只是会引诱用户捐点小钱。真是好东西。
具体怎么被安利的请转移这里看:https://www.zhihu.com/question/25876314
可是我不会用,也没有瓷砖贴图...一穷二白。
此时一个神器从天而降,被基友安利得知一个大洋彼岸的高战死忠有制作这个高级战争专用地图编辑器!
链接: http://pan.baidu.com/s/1qYf9d2O 密码: g2gr



啊!牛逼!
刷刷,编辑完毕,导出保存~~等等!保存之后的地图文件怎么读呢?天呐,还有一堵墙,别说看E文了,连网站都打不开啊!
在获取不到更多的资料的情况下,面对着这个导出后的*.aws文件发呆,还能怎么办,读取试试看啦。
第一步,打开文件,我直接拖拽到了手头上最强大的工具VisualStudio2013里,然后出现下图这样的一堆十六进制:


当然,除了开头,其他人类是看不了的。慢慢分析,开头是文件的标识,是个字符串,很简单,每个文件都一样,是固定的。后面开始就不一样了,最后的结尾部分还是字符串。
那么解决格式问题的关键就是处理中间的那串。
这种文件是不可能再做加密之类的事情的,所以,好的消息是只要创建一个新的地图,不断比对,就能够知道文件的格式。
于是我创建了一个新的地图,保存,用VS打开。发现中间出现了一大片的0000和FFFF,因为地图是空的,很明显,这些地方就是地图上的单位填充点。
一张2D的地图,可以看成是一个二维数组,保存文件里面必然有这个二维数组在一维上的展开。
继续分析,高战的地图不像RPG,RPG通常分三层:地形建筑和单位,而高战上没有地形和建筑的分层必要,根据编辑器设计地图时候的方式可以看出应该是分为两层,一层是地形和建筑物,二层是移动单位。
然后看地图的大小,我创建了10x11和20x21的地图,发现0000和FFFF的数量放大了,如果上面分层的猜测没错,那么0000和FFFF分别就是每一层。同时改变的还有第11和第12字节,肉眼一看就知道这两个字节分别是地图的长度和宽度了。再数一下0000的数量和FFFF的数量,发现是一样的,而且正好是长*宽的倍数,也就是棋格的倍数,正好是棋格数*2Byte,也就是一个int16代表了一个地图单位。
那么格式就很明了啦~
前面10个字节是固定字符串标题,11是width,12是height,用byte来做长宽也就是说地图不可能大过255*255嘛。13暂时不明,不过后来分析出是坏境相关的设定(普通草地、沙漠、雪地)。接下来根据width*height读取地形单位,每两个字节就是一个单位,读完之后就是移动作战单位同样的数量也是2个字节一个。剩下的是地图信息。


从图中可以看出来地图信息是个字符串,但是中间夹杂着一些不是字符串的东西。这部分的信息是地图制作者加上去的,所以字符串的长度不像标题固定10字节,是会变得。
而划分字符串很简单通常都是两种方法:分隔符分隔,或是头部声明长度。这里用肉眼就能看出来是头部声明的长度了,头部有四个字节不能转字符串,但是算一下十进制正好就是字符串的长度,OK了,后面都是一样,有三个字符串。
看下解析的代码:

 1             string path = null;
 2             if (args.Length == 0)
 3             {
 4                 Debug.WriteLine("请输入地图文件路径:");
 5                 path = Console.ReadLine().Trim('\"');
 6             }
 7             else
 8             {
 9                 path = args[0];
10             }
11             var data = File.ReadAllBytes(path);
12             Debug.WriteLine("开始解析...");
13             Debug.WriteLine("------------------------------------");
14             int offset = 0;
15             string capition = Encoding.ASCII.GetString(data, offset, 10);
16             Debug.WriteLine("caption:{0}", capition, 1);
17             offset += 10;
18             int width = data[offset];
19             offset += 1;
20             int height = data[offset];
21             offset += 1;
22             Debug.WriteLine("width:{0}, height:{1}", width, height);
23             int tileset = data[offset];
24             offset += 1;
25             Debug.WriteLine("tileset:{0}", tileset);
26 
27             //terrain
28             for (int i = 0; i < width*height; i++)
29             {
30                 short terrain = BitConverter.ToInt16(data, offset);
31                 offset += 2;
32                 //if (i < 10)
33                 {
34                     Debug.WriteLine("terrain:\t{0:X4}\t{0}", terrain);
35                 }
36             }
37             //unit
38             for (int i = 0; i < width * height; i++)
39             {
40                 short unit = BitConverter.ToInt16(data, offset);
41                 offset += 2;
42                 if (i < 10)
43                 {
44                     //Debug.WriteLine("unit:\t{0:X4}\t{0}", unit);
45                 }
46             }
47             //info
48             int mapnameLength = BitConverter.ToInt32(data, offset);
49             offset += 4;
50             string mapname = Encoding.ASCII.GetString(data, offset, mapnameLength);
51             offset += mapnameLength;
52             Debug.WriteLine("mapname:{0}", mapname, 1);
53 
54             int authorLength = BitConverter.ToInt32(data, offset);
55             offset += 4;
56             string author = Encoding.ASCII.GetString(data, offset, authorLength);
57             offset += authorLength;
58             Debug.WriteLine("author:{0}", author, 1);
59 
60             int descriptionLength = BitConverter.ToInt32(data, offset);
61             offset += 4;
62             string description = Encoding.ASCII.GetString(data, offset, descriptionLength);
63             offset += descriptionLength;
64             Debug.WriteLine("description:{0}", description, 1);
65 
66             Debug.WriteLine("------------------------------------");
67             Debug.WriteLine("解析完毕!");

 


地图文件的格式就这么解决了,然后就是看每一个单位对应的编号了。
其实地形的编辑和最后贴图是不一样的,比如海水和陆地的接壤处拐弯处,其实地形只有海水和陆地之分,而贴图就会有四个弯角和纵向横向六种贴图。而贴图只要看看就好了,实际程序中需要参与计算的是海水/陆地的加成。马路桥梁管道等等都是一样。也就是说其实编号只分地形,不管最后在地图上的贴图是怎样。
下面整理出的就是建筑物表


地形表就更简单了,不区分阵营了。
这样,导出的aws文件配合地图png截屏就可以轻松地开发新棋盘啦~ 拿来主义真牛逼!
最后,检测下成果:


看unit的输出,和图中的单位位置一致的

posted @ 2016-06-29 19:48  陈惊蛰  阅读(1034)  评论(2编辑  收藏  举报