J2ME游戏开发实例讲解(4) Map类的代码如下: package huarongroad; import java.io.InputStream; import javax.microedition.lcdui.*; public class Map { //处理游戏的地图,负责从外部文件加载地图数据,存放地图数据,并按照地图数据绘制地图 public byte Grid[][];//存放地图数据 public Map() {//构造函数,负责初始化地图数据的存储结构 this.Grid = new byte[Images.HEIGHT][Images.WIDTH]; //用二维数组存放地图数据,注意第一维是竖直坐标,第二维是水平坐标 } public int[] read_map(int i) { <A href="file://从">file://从</A>外部文件加载地图数据,并存放在存储结构中,返回值是光标点的位置 //参数是加载地图文件的等级 int[] a = new int[2];//光标点的位置,0是水平位置,1是竖直位置 try { InputStream is = getClass().getResourceAsStream( "/huarongroad/level".concat(String.valueOf(i))); if (is != null) { for (int k = 0; k < Images.HEIGHT; k++) { for (int j = 0; j < Images.WIDTH; j++) { this.Grid[k][j] = (byte) is.read(); if ( this.Grid[k][j] == Images.CURSOR ) { //判断出光标所在位置 a[0] = j;//光标水平位置 a[1] = k;//光标竖直位置 this.Grid[k][j] = Images.BLANK;//将光标位置设成空白背景 } } is.read();//读取回车(13),忽略掉 is.read();//读取换行(10),忽略掉 } is.close(); }else { //读取文件失败 a[0] = -1; a[1] = -1; } }catch (Exception ex) { //打开文件失败 a[0] = -1; a[1] = -1; } return a; } public boolean draw_map(Graphics g) { //调用Draw类的静态方法,绘制地图 try { for (int i = 0; i < Images.HEIGHT; i++) { for (int j = 0; j < Images.WIDTH; j++) { Draw.paint(g, this.Grid[i][j], j, i);//绘制地图 } } return true; }catch (Exception ex) { return false; } } } 对于像华容道这样的小型地图可以直接用手工来绘制地图的内容,比如: fa1c 2232 bd1e 2gg2 gihg 但是,如果遇到像坦克大战或超级玛莉那样的地图,就必须另外开发一个地图编辑器了(我会在后续的文章中介绍用vb来开发一个地图编辑器)。 五、详细设计 详细设计是程序开发过程中至关重要的一个环节,好在我们在前面的各个阶段中已经搭建好了项目所需的一些工具,现在这个阶段中我们只需集中精力设计好Displayable1中的逻辑。(两天的时间当然不只干这点活,还要把其他几个类的设计修改一下) Displayable1这个类负责处理程序的控制逻辑。首先,它需要有表示当前关面的变量level、表示当前光标位置的变量loc、表示要移动区域的变量SelectArea、表示要移动到的区域的变量MoveArea、表示是否已有区域被选中而准备移动的变量Selected和Map类的实例MyMap。然后,我们根据用户按不同的键来处理不同的消息,我们要实现keyPressed()函数,在函数中我们处理按键的上下左右和选中(Fire),这里的处理需要我展开来讲一讲,后面我很快会把这一部分详细展开。 接下来,是实现paint()函数,我们打算在这一部分中反复的重画背景、地图和选择区域,这个函数必须处理好区域被选中之后的画笔颜色的切换,具体讲就是在没有选中任何区域时要用黑色画笔,当选重要移动的区域时使用绿色画笔,当选择要移动到的区域时改用红色画笔(当然附加一张流程图是必不可少的)。 再下面要实现的setRange()函数和setMoveRange()函数,这两个函数用来设置要移动的区域和要移动到的区域,我的思路就是利用前面在Images类中介绍过的地图组合标记常量,当移动到地图组合标记常量时,根据该点地图中的值做逆向变换找到相应的地图标记常量,然后设置相应的loc、SelectArea和MoveArea,其中setMoveRange()函数还用到了一个辅助函数isInRange(),isInRange()函数是用来判断给定的点是否在已选中的要移动的区域之内,如果isInRange()的返回值是假并且该点处的值不是空白就表明要移动到的区域侵犯了其他以被占用的区域。有了setRange()和setMoveRange()函数,Move()函数就水到渠成了,Move()函数将要移动的区域移动到要移动到的区域,在移动过程中分为三步进行: 第一.复制要移动的区域; 第二.将复制出的要移动区域复制到要移动到的区域(这两步分开进行的目的是防止在复制过程中覆盖掉要移动的区域); 第三.用isInRange2()判断给定的点是否在要移动到的区域内,将不在要移动到的区域内的点设置成空白。 下面我们详细的分析一下keyPressed()函数的实现方法:首先,keyPressed()函数要处理按键的上下左右和选中(Fire),在处理时需要用Canvas类的getGameAction函数来将按键的键值转换成游戏的方向,这样可以提高游戏的兼容性(因为不同的J2ME实现,其方向键的键值不一定是相同的)。 接下来,分别处理四个方向和选中.当按下向上时,先判断是否已经选定了要移动的区域(即this.selected是否为真),如果没有选中要移动区域则让光标向上移动一格,然后调用setRange()函数设置选择要移动的区域,再调用repaint()函数刷新屏幕,否则如果已经选中了要移动的区域,就让光标向上移动一格,然后调用setMoveRange()函数判断是否能够向上移动已选中的区域,如果能移动就调用repaint()函数刷新屏幕,如果不能移动就让光标向下退回到原来的位置。 当按下向下时,先判断是否已经选定了要移动的区域,如果没有选中要移动的区域则判断当前所处的区域是否为两个格高,如果是两个格高则向下移动两格,如果是一个格高则向下移动一格,接着再调用setRange()函数设置选择要移动的区域,而后调用repaint()函数刷新屏幕,否则如果已经选中了要移动的区域,就让光标向下移动一格,然后调用setMoveRange()函数判断是否能够向下移动已选中的区域,如果能移动就调用repaint()函数刷新屏幕,如果不能移动就让光标向上退回到原来的位置.按下向左时情况完全类似向上的情况,按下向右时情况完全类似向下的情况,因此这里不再赘述,详细情况请参见程序的源代码。 当按下选中键时,先判断是否已经选中了要移动的区域,如果已经选中了要移动的区域就调用Move()函数完成由要移动的区域到要移动到的区域的移动过程,接着调用repaint()函数刷新屏幕,然后将已选择标记置成false,继续调用win()函数判断是否完成了任务,否则如果还没有选定要移动的区域则再判断当前选中区域是否为空白,如果不是空白就将选中标记置成true,然后刷新屏幕.这里介绍一个技巧,在开发程序遇到复杂的逻辑的时候,可以构造一格打印函数来将所关心的数据结构打印出来以利调试,这里我们就构造一个PrintGrid()函数,这个函数纯粹是为了调试之用,效果这得不错.至此我们完成了编码前的全部工作。 (未完待续)
|