当前位置: 首页 >> 程序设计 >> 游戏开发 >> J2ME游戏程序开发实例详解
 

J2ME游戏程序开发实例详解

作者:      来源:zz     发表时间:2007-12-28     浏览次数:      字号:    

一、序言

昨天在网上闲逛,发现一篇讲解用delphi实现华容道游戏的文章,颇受启发.于是,产生了将华容道游戏移植到手机中去的冲动.现在手机游戏琳琅满目,不一而足,华容道的实现版本也很多.正巧不久前笔者对J2ME下了一番功夫,正想借这个机会小试牛刀。选用J2ME的原因还有一个就是目前Java开发大行其到,无线增殖业务迅猛发展,J2ME的应用日渐活跃起来,也希望我的这篇文章能够为J2ME知识的普及和开发团队的壮大推波助澜。由于长期受ISO规范的影响,这次小试牛刀我也打算遵照软件工程的要求,并采取瀑布式的开发模式来规划项目,也希望借此机会向各位没有机会参与正式项目开发的读者介绍一下软件开发的流程。

这里我们先定义项目组的人员体制(其实只有我一个人):技术调研、需求分析、概要设计、详细设计、编码、测试均有笔者一人担任;美工这里我找了个捷径,盗用网上现成的图片,然后用ACDSee把它由BMP转换成PNG格式(我出于讲座的目的,未做商业应用,应该不算侵权吧);至于发布工作,由于缺少OTA服务器,此项工作不做(但是我会介绍这步如何做)。

接下来,我们规划一下项目实现的时间表,以我个人经验,设想如下:技术调研用2天(这部分解决项目的可行性和重大技术问题,时间会长一些),需求分析用半天(毕竟有现成的东东可以参照,只要理清思路就行了,况且还有很多以前用过的设计模式和写好的代码),概要设计再用半天(有了需求,概要只不够是照方抓药),详细设计要用2天(这一步要把所有的问题想清楚,还要尽可能的准确描述出来),编码用2天(其实1天就够了,技术已经不是问题,多计划出一天来应付突发事件),测试用2天(测试应该至少占全部项目的四分之一,不过这个项目只是一个Demo,也太简单了),发布也要用上半天(尽管我们不去实际发布它,但是还要花点时间搞清楚应该如何做),最后就是项目总结和开庆功会(时间待定)。

二、利其器

“公欲善其事,必先利其器”,做项目之前第一步是前期调研.我们要做的华容道这个东东随处可见,我们要调研的是两个方面:

1. 游戏的内容:游戏本身很简单,就是有几个格子,曹操占据其中一个较大的格子,然后被几个格子包围,这些格子形状不一定相同,但是挡住了曹操移动的方向.游戏者需要挪动这些格子最终把曹操移动到一个指定的位置才算是过关.更具体的分析我们放在后面需求分析和概要设计中讨论。

2. 技术储备:谈到技术,这里简单介绍一下J2ME.Java有三个版本,分别是J2ME(微型版).J2SE(标准版).J2EE(企业版).J2ME是一个标准,采用3层结构设计.最低层是配置层(Configuration)也就是设备层,其上是简表层(Profile),再上是应用层(Application).MIDP就是移动信息设备简表,目前主流手机支持MIDP1.0,最新的是MIDP2.0,它比前一个版本增加了对游戏的支持,在javax.microedition.lcdui.game包中提供了一些类来处理游戏中的技术,比如我们后面会用到的Sprite类,它是用来翻转图片的.权衡再三,笔者决定使用MIDP2.0来做开发.首先需要安装一个J2ME的模拟器,我们就用Sun公司的WTK2.0,我觉得Sun的东西最权威.当然你也可以使用Nokia.Siemens或是Motolora等其他模拟器,但是他们的JDK不尽相同,写出来的程序移植是比较麻烦的.

Sun公司的WTK2.0可以到搜索引擎寻找下载,当然要想成功下载的前提是你要先注册成为Sun的会员(其实这样对你是有好处的).当下来之后就是按照提示一步一步的安装.安装好了之后,我们用一个"Hello World"程序开始你的J2ME之旅.我们启动WTK2.0工具集中的KToolBar,然后点击New Project按钮,在弹出的输入框中输入Project Name为HelloWorld,MIDlet Class Name为Hello,然后点击Create Project,开始生成项目,工具会弹出MIDP配置简表,这里接受生成的默认值(以后还可以修改)点击OK,工具提示我们把写好的Java源程序放到[WTK_HOME]\apps\HelloWorld\src目录之下.我们编辑如下代码,并保存在上述目录之下,文件名为Hello.java。

import javax.microedition.midlet.*; 
import javax.microedition.lcdui.*; 
public class Hello extends MIDlet 
{ 
private Display display; 
public Hello(){ 
display =Display.getDisplay(this); 
} 
public void startApp(){ 
TextBox t = new TextBox("Hello","Hello",256,0); 
display.setCurrent(t); 
} 
public void pauseApp(){ 
} 
public void destroyApp(boolean unconditional){ 
} 
}


保存好了之后,点击Build按钮,工具会为你编译程序,如无意外再点击Run按钮,会弹出一个手机界面,剩下的就不用我教了吧(用鼠标对手机按键一顿狂点)。呵呵,你的第一个J2ME程序已经OK了.什么?你还一点都没懂呢(真是厉害,不懂都能写出J2ME程序来,果然是高手).我这里主要是介绍WTK2.0工具的使用,程序并不是目的,不懂的话后面还会有详细的解说,这里只是带你上路.什么?你不懂Java!那也没有关系,后面我再讲得细一点。

跳过J2ME,我们先来讲点游戏的理论.具体到华容道这个游戏,主要有三个方面,贴图.游戏操作.逻辑判断.这里讲讲贴图,其他两方面放在概要设计和详细设计里讲.所谓的贴图,其实就是画图,就是在要显示图形的位置上输出一副图片,(要是牵扯到动画就要麻烦一些,可以使用TimerTask.Thread或Rannable之类的技术),这副图片可以是事先准备好的也可以是临时处理的.在J2ME中有一个Image类,专门用于管理图片,它有createImage()方法,可以直接读取图片文件(J2ME只支持PNG格式的图片),也可以截取已有的图片的一部分(这样我们可以把很多图片放在一起,然后一张一张的截下来,好处是节省存储空间和文件读取时间,对于手机这两者都是性能的瓶颈).

J2ME还有一个Graphics类,专门用于绘图,它有drawImage()方法,可以把一副图片在指定的位置上显示出来,它还有drawRect()方法和setColor()方法,这两个方法在后面我们进行游戏操作时就会用到,这里先交代一下.有了图片和绘图的方法,还需要知道把图画到谁身上,J2ME提供了一个Canvas类,字面意思就是画布,它有一个paint()方法用于刷新页面,还有一个repaint()方法用于调用paint()方法.听着有些糊涂是吧,不要紧,我来结合具体程序讲解一下.为了今后编程的方便,我们创建两个类Images和Draw,Images用于保存一些常量值和图片,Draw主要是用于画图,这两个类的源代码如下。

Images类的源代码如下:

package huarongroad; 
import javax.microedition.lcdui.*; 
import javax.microedition.lcdui.game.*; 
public class Images {//保存常量 
//绘图位置常量 
public static final int UNIT = 32;//方块的单位长度 
public static final int LEFT = 10;//画图的左边界顶点 
public static final int TOP = 9;//画图的上边界顶点 
//地图位置常量 
public static final int WIDTH = 4;//地图的宽度 
public static final int HEIGHT = 5;//地图的高度 
//地图标记常量 
public static final byte CAOCAO = (byte) ′a′; <A href="file://曹">file://曹</A>操的地图标记 
public static final byte MACHAO = (byte) ′b′;//马超的地图标记 
public static final byte HUANGZHONG = (byte) ′c′;//黄忠的地图标记 
public static final byte GUANYU = (byte) ′d′;//关羽的地图标记 
public static final byte ZHANGFEI = (byte) ′e′;//张飞的地图标记 
public static final byte ZHAOYUN = (byte) ′f′;//赵云的地图标记 
public static final byte ZU = (byte) ′g′;//卒的地图标记 
public static final byte BLANK = (byte) ′h′;//空白的地图标记 
public static final byte CURSOR = (byte) ′i′;//光标的地图标记 
//地图组合标记常量 
public static final byte DLEFT = (byte) ′1′; <A href="file://组">file://组</A>合图形左边标记 
public static final byte DUP = (byte) ′2′; <A href="file://组">file://组</A>合图形上边标记 
public static final byte DLEFTUP = (byte) ′3′; <A href="file://组">file://组</A>合图形左上标记 
//图片常量 
public static Image image_base;//基本图片 
public static Image image_Zhaoyun;//赵云的图片 
public static Image image_Caocao;//曹操的图片 
public static Image image_Huangzhong;//黄忠的图片 
public static Image image_Machao;//马超的图片 
public static Image image_Guanyu;//关羽的图片 
public static Image image_Zhangfei;//张飞的图片 
public static Image image_Zu;//卒的图片 
public static Image image_Blank;//空白的图片 
public static Image image_Frame;//游戏框架的图片 
public Images() {//构造函数 
} 
public static boolean init() {//初始化游戏中用到的图片 
try { 
image_base = Image.createImage("/huarongroad/BITBACK.png"); 
image_Frame = Image.createImage(image_base, 126, 0, 145, 177, 
Sprite.TRANS_NONE); 
//Sprite类是用来翻转图片的,是MIDP2.0新新增加的支持游戏的特性 
image_Zhaoyun = Image.createImage(image_base, 0, 0, UNIT, 2 * UNIT, 
Sprite.TRANS_NONE); 
image_Caocao = Image.createImage(image_base, UNIT, 0, 2 * UNIT, 
2 * UNIT, Sprite.TRANS_NONE); 
image_Huangzhong = Image.createImage(image_base, 3 * UNIT, 0, UNIT, 
2 * UNIT, 
Sprite.TRANS_NONE); 
image_Machao = Image.createImage(image_base, 0, 2 * UNIT, UNIT, 
2 * UNIT, 
Sprite.TRANS_NONE); 
image_Guanyu = Image.createImage(image_base, UNIT, 2 * UNIT, 
2 * UNIT, UNIT, 
Sprite.TRANS_NONE); 
image_Zhangfei = Image.createImage(image_base, 3 * UNIT, 2 * UNIT, 
UNIT, 2 * UNIT, 
Sprite.TRANS_NONE); 
image_Zu = Image.createImage(image_base, 0, 4 * UNIT, UNIT, UNIT, 
Sprite.TRANS_NONE); 
image_Blank = Image.createImage(image_base, 1 * UNIT, 4 * UNIT,UNIT, 
UNIT, 
Sprite.TRANS_NONE); 
return true; 
}catch (Exception ex) { 
return false; 
} 
} 
}


Draw类的源代码如下:

package huarongroad; 
import javax.microedition.lcdui.*; 
public class Draw { 
//绘制游戏中的图片 
public Draw(Canvas canvas) {//构造函数 
} 
public static boolean paint(Graphics g, byte img, int x, int y) { 
//在地图的x,y点绘制img指定的图片 
try { 
paint(g, img, x, y, Images.UNIT);//把地图x,y点转化成画布的绝对坐标,绘图 
return true; 
} 
catch (Exception ex) { 
return false; 
} 
} 
public static boolean paint(Graphics g, byte img, int x, int y, int unit) { 
try { 
switch (img) { 
case Images.CAOCAO://画曹操 
//变成绝对坐标,并做调整 
g.drawImage(Images.image_Caocao, Images.LEFT + x * unit, 
Images.TOP + y * unit, 
Graphics.TOP | Graphics.LEFT); 
break; 
case Images.GUANYU://画关羽 
g.drawImage(Images.image_Guanyu, Images.LEFT + x * unit, 
Images.TOP + y * unit, 
Graphics.TOP | Graphics.LEFT); 
break; 
case Images.HUANGZHONG://画黄忠 
g.drawImage(Images.image_Huangzhong, Images.LEFT + x * unit, 
Images.TOP + y * unit, 
Graphics.TOP | Graphics.LEFT); 
break; 
case Images.MACHAO://画马超 
g.drawImage(Images.image_Machao, Images.LEFT + x * unit, 
Images.TOP + y * unit, 
Graphics.TOP | Graphics.LEFT); 
break; 
case Images.ZHANGFEI://画张飞 
g.drawImage(Images.image_Zhangfei, Images.LEFT + x * unit, 
Images.TOP + y * unit, 
Graphics.TOP | Graphics.LEFT); 
break; 
case Images.ZHAOYUN://画赵云 
g.drawImage(Images.image_Zhaoyun, Images.LEFT + x * unit, 
Images.TOP + y * unit, 
Graphics.TOP | Graphics.LEFT); 
break; 
case Images.ZU://画卒 
g.drawImage(Images.image_Zu, Images.LEFT + x * unit, 
Images.TOP + y * unit, 
Graphics.TOP | Graphics.LEFT); 
break; 
case Images.BLANK://画空白 
g.drawImage(Images.image_Blank, Images.LEFT + x * unit, 
Images.TOP + y * unit, 
Graphics.TOP | Graphics.LEFT); 
break; 
case Images.CURSOR://画光标 
g.drawRect(Images.LEFT + x * unit, 
Images.TOP + y * unit,Images.UNIT,Images.UNIT); 
break; 
} 
return true; 
}catch (Exception ex) { 
return false; 
} 
} 
}


其中Images类存的是绘图位置常量(也就是在画图时每个格子的长度和相对坐标原点位置要进行的调整)、地图位置常量(地图的长、宽),地图标记常量(人物对应的记号),地图组合标记常量(后面会细说),图片常量(存放人物的图片);Draw类主要负责在制定的位置画出人物图片。下面我来说说Images类中的地图标记常量和地图组合标记常量。为了能够灵活的安排各个关面的布局,我们决定把游戏布局的信息存储在外部文件中,然后程序启动后把它读进来。

这样我们制定了一套存储图片的代码,这就是地图标记常量,如上面Images类中定义的Caocao(曹操)用a字符来表示,当程序读到a字符时就能将它转化成曹操对应的图片,并在读到a字符的位置上进行显示。但是从实际观察中我们发现所有的图片并不是统一大小的,有的占4个格子,有的占2个格子,还有的占1个格子,而且即便同是占两个格子的图片还有横、竖之分。有鉴于此,我们引入了地图组合标记常量,就是说在遇到占有多个格子的时候,值1(也就是Images.LEFT)表示它的左边是一个真正的地图标记,值2(也就是Images.UP)表示它的上边是一个真正的地图标记,值1(也就是Images.LEFTUP)表示它的左上边是一个真正的地图标记。地图组合标记常量其实就是用来占位置的,与实际显示无关,当后面我们将到移动时还会再来分析组合标记的使用。

Draw类主要是用来在画布上画出图形,它有两个paint方法,这是很常见的函数重载。但是程序中实际上只用到了4个参数的paint方法,它直接获得要画图片的相对坐标位置信息,然后调用5个参数的paint方法。5个参数的paint方法将相对坐标位置信息转换成绝对位置,并实际调用Graphics.drawImage()方法,将Images中的图片画了出来。这种实现方法的好处是灵活和便于扩展,但你需要画图的位置并不能够对应到格子中的相对坐标位置时,你就可以直接调用5个参数的paint方法,而不必再去修改这各类;但你添加新的图片时,只要在Images中增加对应的常量,然后向Draw中5个参数的paint方法添加一条处理就可以了。写到这里,两天的时间刚好用完。

[1] [2] [3]

责任编辑 webmaster

 
 
 
 
 
评论更多>>
 
 
 
发表
 
姓名: QQ:
性别: MSN:
E-mail: 主页:
评分: 1 2 3 4 5
评论内容:
验证码:
  
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •