注:本系列教程每周一篇,旨在引导刚刚接触FLASH的新手通过实例进行游戏开发的学习。在过程中逐步说明涉及到的类及对应的使用方法。从一个光秃秃的方块开始,根据不同的控制方式、玩法产生不同的分支,最终完善成一个个可玩的游戏。希望对各位入门的朋友有所帮助!在教程涉及的各种处理方法,可能不够完善,也希望各位高手指正:)
转载请注名来源于天地会。
前篇勘误:在Monster.as中,第44行对计算频率进行控制时,if (date.time-_lastAction.time > _fps/1000)应为if (date.time-_lastAction.time > 1000/_fps)。感谢各位兄弟的提示!
第六篇 设置地图
在上一篇教程中。我们对游戏进行了美化,同时加入了简单的碰撞检测。在本篇中,我们将设置地图,先来看一下我们预期的效果:
像上图这样,如果像我们原来那样通过for循环去生成地图,那基本上写if和else就可以把我们累死了。我们需要寻找规律,来达到批量处理的目的。
从上图我们可以看出,所有的游戏对象都是比较整齐的摆放的。摆放的间距也基本上与游戏对象的宽度成比例。如果我们把可以通过的位置以0来表示,不能通过的位置以1来表示,那么,可以得到下图:
这让你想到了什么吗?是的,是数组,我们可以用数组来标识地图:
- private var mapconfig:Array = [
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
- [0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
- [0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
- [1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
- ];
在上面这个数组中,mapconfig[line]记录了第line行的全部地图元素,例如,当line为0的时候,也就是mapconfig[0]的元素“[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]”,可以看出,在这一行中,全部都是空位置。而当line为1的时候,mapconfig[1]的元素“[0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0]”告诉我们,中间有6个元素的位置是障碍物,无法通过。同样的,我们可以用2来表示敌人出现的位置,用3来表示自己的坦克出现的位置,于是,上面的数组就变成了现在这样
- private var mapconfig:Array = [
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0],
- [0, 2, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
- [0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
- [0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
- [1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0]
- ];
可以看出,在第一行的倒数第三个位置,出现了一个敌人,在第二行的第二个位置,出现了一个敌人,而自己则出现在最后一行的第五个元素。如果你喜欢用y来表示行,用x来表示列的话,那么第y行第x列的地图元素数据应该为mapconfig[y][x]。
好了,下面让我们来用程序实现对这个地图数据的解析。
我们新建一个类,继承自gameScene,暂时定义为D5Scene吧,当然,如果你喜欢其他的名字,可以自己定义:)
在这个类里,我们声明一个用来保存地图数据的变量
- /**
- * 地图数据
- */
- protected var _mapData:Array;
还有一个解析方法,我们需要对_mapData进行循环解析,把其中的数字转换成可以通过的位置,或者障碍物,或者敌人,或者我们自己的角色。
- /**
- * 配置地图
- *
- * @param arr 地图数据
- */
- public function setup(arr:Array):void
- {
- _mapData = arr;
- if (arr[0] == null || arr[0][0]==null) return;
- var y:uint = 0;
- var x:uint;
- for each(var line:Array in arr)
- {
- x = 0;
- for each(var data:uint in line)
- {
- if (data != 0)
- {
- }
- x++;
- }
- y++;
- }
- }
我们需要为setup传递入一个参数arr,即地图的配置文件,而接下来,arr应该是一个2维数组,也就是必须有arr[y][x]这样的结构。因此arr[0]和arr[0][0]都不应该为空,否则就是不符合格式要求的数据,程序将不予以解析。所以,我们在这里做了一个格式判断:if (arr[0] == null || arr[0][0]==null) return;。而接下来,则对数组中的全部数据进行了循环解析。如果发现data不为0,也就是不可通过的位置,则进行相关的处理。
到这里,我们已经可以分析到数组中的全部元素数据了,但是,我们现在得到的是0,1,2,3这样的数字,而不是最终的游戏对象。所以,我们需要有一个函数对这些数字来进行翻译:
- /**
- * 通过数字ID获取对应的元素
- *
- * @param id 地图数字
- * @return gameObject
- */
- protected function getObjById(id:uint):gameObject
- {
- var result:gameObject;
- switch(id)
- {
- case 1:
- result = new Stone(new stuff());
- result.part = 1;
- break;
- case 2:
- result = new Monster(new MonsterControler(), new Skin2());
- result.part = 2;
- break;
- case 3:
- result = new Player(new KeyController(), new Skin1());
- result.part = 3;
- break;
- }
- return result;
- }
代码都是我们熟悉的,只是根据不同的ID进行了区分而已,不再过多的描述。这样,我们可以在刚才的setup函数中,if (data != 0)后面做文章了,来看一下完整的setup代码:
- /**
- * 配置地图
- *
- * @param arr 地图数据
- */
- public function setup(arr:Array):void
- {
- _mapData = arr;
- if (arr[0] == null || arr[0][0]==null) return;
- var y:uint = 0;
- var x:uint;
- var obj:gameObject;
- for each(var line:Array in arr)
- {
- x = 0;
- for each(var data:uint in line)
- {
- if (data != 0)
- {
- obj = getObjById(data);
- if (obj != null)
- {
- addObject(obj);
- obj.x = obj.width * x;
- obj.y = obj.height * y;
- }
- }
- x++;
- }
- y++;
- }
- }
这里,我们认为所有的游戏对象均使用了相同的规格,因此,可以根据他们在地图元素中的下标来计算其在场景中的坐标。
最后,对游戏的主入口函数做出修改:
- private var mapconfig:Array = [
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0],
- [0, 2, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
- [0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
- [0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
- [1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0]
- ];
- public function Main()
- {
- var scene:D5Scene = new D5Scene(stage); // 声明游戏舞台
- scene.setup(mapconfig);
- // 生成基地
- var base:Base = new Base(new basicController(),new Skin_base());
- base.x = (stage.stageWidth-base.width)/2;
- base.y = stage.stageHeight-base.height;
- Global.base = base;
- scene.addObject(base);
- // 显示游戏舞台
- addChild(scene);
- }
代码看起来比原来精简多了吧。运行测试一下吧
小问题:现在基地没有放到场景里去,你可以自己修改程序,把基地也通过地图数组来进行管理
小提示:背景改为黑色后,是不是子弹看不见了?去子弹类里把填充色由0x000000改为0xffffff就可以了,白色的子弹就可以看清楚了。
最终效果:
main.swf (20.31 KB)
源代码:
Teach.rar (409.92 KB)
第七篇教程,也将是从零开始系列的最后一篇。我们将研究简单效果的实现思路,声音的播放,以及游戏道具的实现方法。之后,从零开始系列教程将结束,我们将引入位图缓冲等概念,开启新一系列的教程。