j2me开发实例:j2me游戏开发例子讲解

J2ME再现华容道
.序言
  昨天在网上闲逛发现篇讲解用delphi实现华容道游戏文章颇受启发.于是产生了将华容道游戏移植到手机中去冲动.现在手机游戏琳琅满目而足华容道实现版本也很多.正巧不久前笔者对J2ME下了番功夫正想借这个机会小试牛刀.选用J2ME作为开发语言还有个原因就是目前Java开发大行其到无限增殖业务迅猛发展J2ME应用日渐活跃起来也希望我这篇文章能够为J2ME知识普及和开发团队(Team)壮大推波助澜.由于长期受ISO规范标准影响这次小试牛刀我也打算遵照软件Software工程要求并采取瀑布式开发模式来规划项目也希望借此机会向各位没有机会参和正式项目开发读者介绍下软件Software开发流程.
    这里我们先定义项目组人员体制(其实只有我个人):技术调研、需求分析、概要设计、详细设计、编码、测试均有笔者人担任;美工这里我找了个捷径盗用网上现成图片然后用ACDSee把它由BMP转换成PNG格式(我出于讲座未做商业应用应该不算侵权吧);至于发布工作由于缺少OTA服务器此项工作不做(但是我会介绍这步如何做)
    接下来我们规划下项目实现时间表以我个人经验设想如下:技术调研用2天(这部分解决项目可行性和重大技术问题时间会长些)需求分析用半天(毕竟有现成东东可以参照只要理清思路就行了况且还有很多以前用过设计模式和写好代码)概要设计再用半天(有了需求概要只不够是照方抓药)详细设计要用2天(这步要把所有问题想清楚还要尽可能准确描述出来)编码用2天(其实1天就够了技术已经不是问题多计划出天来应付突发事件)测试用2天(测试应该至少占全部项目 4分的不过这个项目只是个Demo也太简单了)发布也要用上半天(尽管我们不去实际发布它但是还要花点时间搞清楚应该如何做)最后就是项目整理总结和开庆功会(时间待定)
2.利其器
   "公欲善其事必先利其器"做项目的前第步是前期调研.我们要做华容道这个东东随处可见我们要调研是两个方面:
  1.游戏内容:游戏本身很简单就是有几个格子曹操占据其中个较大格子然后被几个格子包围这些格子形状不定相同但是挡住了曹操移动方向.游戏者需要挪动这些格子最终把曹操移动到个指定位置才算是过关.更具体分析我们放在后面需求分析和概要设计中讨论.
  2.技术储备:谈到技术这里简单介绍下J2ME.Java有 3个版本分别是J2ME(微型版).J2SE(标准版).J2EE(企业版).J2ME是个标准采用3层结构设计.最低层是配置层(Configuration)也就是设备层其上是简表层(Profile),再上是应用层(Application).MIDP就是移动信息设备简表目前主流手机支持MIDP1.0最新是MIDP2.0,它比前个版本增加了对游戏支持在javax.microedition.lcdui.game包中提供了些类来处理游戏中技术比如我们后面会用到Sprite类它是用来翻转图片.权衡再 3笔者决定使用MIDP2.0来做开发.首先需要安装个J2ME模拟器我们就用Sun公司WTK2.0我觉得Sun东西最权威.当然你也可以使用Nokia.Siemens或是Motolora等其他模拟器但是他们JDK不尽相同写出来移植是比较麻烦.Sun公司WTK2.0可以到<A href="http://here/下">http://here/下</A>载当然要想成功下载前提是你要先注册成为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 Hello extends MIDlet
{
private Display display;
public Hello{
display =Display.getDisplay(this);
}
public void startApp{
TextBox t = TextBox("Hello","Hello",256,0);
display.Current(t);
}
public void pauseApp{
}
public void destroyApp(boolean unconditional){
}
}
保存好了的后点击Build按钮工具会为你编译如无意外再点击Run按钮会弹出个手机界面剩下就不用我教了吧(用鼠标对手机按键顿狂点).呵呵个J2ME已经OK了.什么?你还点都没懂呢(真是厉害不懂都能写出J2ME果然是高手).我这里主要是介绍WTK2.0工具使用并不是目不懂话后面还会有详细解说这里只是带你上路.什么?你不懂Java!那也没有关系后面我再讲得细点.
  跳过J2ME我们先来讲点游戏理论.具体到华容道这个游戏主要有 3个方面贴图.游戏操作.逻辑判断.这里讲讲贴图其他两方面放在概要设计和详细设计里讲.所谓贴图其实就是画图就是在要显示图形位置上输出副图片(要是牵扯到动画就要麻烦可以使用TimerTask.Thread或Rannable的类技术)这副图片可以是事先准备好也可以是临时处理.在J2ME中有个Image类,专门用于管理图片它有createImage思路方法可以直接读取图片文件(J2ME只支持PNG格式图片)也可以截取已有图片部分(这样我们可以把很多图片放在然后截下来好处是节省存储空间和文件读取时间对于手机这两者都是性能瓶颈).J2ME还有个Graphics类专门用于绘图它有drawImage思路方法可以把副图片在指定位置上显示出来它还有drawRect思路方法和Color思路方法这两个思路方法在后面我们进行游戏操作时就会用到这里先交代下.有了图片和绘图思路方法还需要知道把图画到谁身上J2ME提供了个Canvas类字面意思就是画布它有个pa思路方法用于刷新页面还有个repa思路方法用于pa思路方法.听着有些糊涂是吧不要紧我来结合具体讲解下.为了今后编程方便我们创建两个类Images和Draw,Images用于保存些常量值和图片Draw主要是用于画图这两个类源代码如下
Images类源代码如下:
package huarongroad;

import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;

public Images {//保存常量
//绘图位置常量
public final UNIT = 32;//方块单位长度
public final LEFT = 10;//画图左边界顶点
public final TOP = 9;//画图上边界顶点
//地图位置常量
public final WIDTH = 4;//地图宽度
public final HEIGHT = 5;//地图高度
//地图标记常量
public final CAOCAO = () 'a'; <A href="file://曹">file://曹</A>操地图标记
public final MACHAO = () 'b';//马超地图标记
public final HUANGZHONG = () 'c';//黄忠地图标记
public final GUANYU = () 'd';//关羽地图标记
public final ZHANGFEI = () 'e';//张飞地图标记
public final ZHAOYUN = () 'f';//赵云地图标记
public final ZU = () 'g';//卒地图标记
public final BLANK = () 'h';//空白地图标记
     public final CURSOR = () 'i';//光标地图标记
//地图组合标记常量
public final DLEFT = () '1'; <A href="file://组">file://组</A>合图形左边标记
public final DUP = () '2';  <A href="file://组">file://组</A>合图形上边标记
public final DLEFTUP = () '3'; <A href="file://组">file://组</A>合图形左上标记
     //图片常量
public Image image_base;//基本图片
public Image image_Zhaoyun;//赵云图片
public Image image_Caocao;//曹操图片
public Image image_Huangzhong;//黄忠图片
public Image image_Machao;//马超图片
public Image image_Guanyu;//关羽图片
public Image image_Zhangfei;//张飞图片
public Image image_Zu;//卒图片
public Image image_Blank;//空白图片
public Image image_Frame;//游戏框架图片

public Images {//构造
}

public 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);

true;
}catch (Exception ex) {
false;
}
}
}
Draw类源代码如下:
package huarongroad;

import javax.microedition.lcdui.*;

public Draw {
//绘制游戏中图片
public Draw(Canvas canvas) {//构造
}

public boolean pa(Graphics g, img, x, y) {
//在地图x,y点绘制img指定图片
try {
pa(g, img, x, y, Images.UNIT);//把地图x,y点转化成画布绝对坐标绘图
true;
}
catch (Exception ex) {
false;
}
}

public boolean pa(Graphics g, img, x, y, unit) {
try {
switch (img) {
Images.CAOCAO://画曹操
//变成绝对坐标并做调整
g.drawImage(Images.image_Caocao, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
;
Images.GUANYU://画关羽
g.drawImage(Images.image_Guanyu, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
;
Images.HUANGZHONG://画黄忠
g.drawImage(Images.image_Huangzhong, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
;
Images.MACHAO://画马超
g.drawImage(Images.image_Machao, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
;
Images.ZHANGFEI://画张飞
g.drawImage(Images.image_Zhangfei, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
;
Images.ZHAOYUN://画赵云
g.drawImage(Images.image_Zhaoyun, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
;
Images.ZU://画卒
g.drawImage(Images.image_Zu, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
;
Images.BLANK://画空白
g.drawImage(Images.image_Blank, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
;
                 Images.CURSOR://画光标
g.drawRect(Images.LEFT + x * unit,
Images.TOP + y * unit,Images.UNIT,Images.UNIT);
;
}
true;
}catch (Exception ex) {
false;
}
}
}
其中Images类存是绘图位置常量(也就是在画图时每个格子长度和相对坐标原点位置要进行调整)、地图位置常量(地图长、宽)地图标记常量(人物对应记号)地图组合标记常量(后面会细说)图片常量(存放人物图片);Draw类主要负责在制定位置画出人物图片下面我来说说Images类中地图标记常量和地图组合标记常量为了能够灵活安排各个关面布局我们决定把游戏布局信息存储在外部文件中然后启动后把它读进来这样我们制定了套存储图片代码这就是地图标记常量如上面Images类中定义Caocao(曹操)用a来表示读到a时就能将它转化成曹操对应图片并在读到a位置上进行显示但是从实际观察中我们发现所有图片并不是统大小占4个格子占2个格子还有占1个格子而且即便同是占两个格子图片还有横、竖的分有鉴于此我们引入了地图组合标记常量就是说在遇到占有多个格子时候值1(也就是Images.LEFT)表示它左边是个真正地图标记值2(也就是Images.UP)表示它上边是个真正地图标记值1(也就是Images.LEFTUP)表示它左上边是个真正地图标记地图组合标记常量其实就是用来占位置和实际显示无关当后面我们将到移动时还会再来分析组合标记使用
Draw类主要是用来在画布上画出图形它有两个pa思路方法这是很常见重载但是中实际上只用到了4个参数pa思路方法它直接获得要画图片相对坐标位置信息然后5个参数pa思路方法5个参数pa思路方法将相对坐标位置信息转换成绝对位置并实际Graphics.drawImage思路方法将Images中图片画了出来这种实现思路方法好处是灵活和便于扩展但你需要画图位置并不能够对应到格子中相对坐标位置时你就可以直接5个参数pa思路方法而不必再去修改这各类;但你添加新图片时只要在Images中增加对应常量然后向Draw中5个参数pa思路方法添加条处理就可以了
    写到这里两天时间刚好用完
3、需求分析
    这部分叫做需求分析听起来挺吓人其实就是搞清楚我们要做什么做成什么样那些不做下面我引领着大家共同来完成这步骤首先我们要做个华容道游戏华容道故事这里不再赘述了但其中人物在这里限定如上面Images类里定义我们这个版本只提供曹操(Caocao)、关羽(Guanyu)、张飞(Zhangfei)、赵云(Zhaoyun)、黄忠(Huangzhong)、马超(Machao)和卒(Zu)我们这里也限定下游戏操作思路方法:首先要通过方向键选择个要移动区域(就是张图片)被选择区域用黑色方框框住;选好后按Fire键(就是确定键)将这块区域选中被选中区域用绿色方框框住;然后选择要移动到区域此时用红色方框框住被选择区域;选好要移动到区域的后按Fire键将要移动区域(图片)移到要移动到区域并去掉绿色和红色方框这里需要强调概念有选择区域、选中区域、要移动区域和要移动到区域这 4个概念请读者注意区分当然也应当把这部分记入数据字典的中为了使文章重点突出(介绍如何制作个J2ME收集游戏)我们这里限定些和本主题无关内容暂不去实现:过关的后动画(实现时要用到TimerTask或Thread类后续系列文章中我会详细介绍动画方面知识)、关面的间切换(其实很简单当完成任务的后重新再做边)、暂停和保存等操作(这部分内容介绍资料很多我也写不出什么新东东来难免抄袭故此免掉)
    需求分析基本完成离下午还有段时间马上动手用ACDSee把从网上找来BMP文件调整其大小为271*177(我这个图片是两个部分合在所以比手机实际屏幕大了)另存为PNG格式半天时间刚刚好不但搞清楚了要做东东还把要用图片准备好了
4、概要设计
    概要设计是从需求分析过渡到详细设计桥梁和纽带部分中我们确定项目实现思路方法和模块划分我们决定将整个项目分成 5个部分分别是前面介绍Images、Draw还有Map和Displayable1和MIDlet1Images和Draw类功能简单、结构固定因此很多项目我们都使用这两各类这里直接拿来改改就能用了前面已经介绍过这里不再赘述Map类是用来从外部文件读入地图然后保存在的中这部分内容是我们在本阶段讨论重点Displayable1是个继承了Canvas类画布它用来处理主要控制逻辑和部分控制逻辑所需辅助主要应该包括用来绘图pa、用来控制操作keyPressed、用来控制选择区域Range、用来控制选择要移动到区域MoveRange、用来移动选中区域Move和判断是否完成任务win更具体分析我们放到详细设计中去细化MIDlet1实际上就是个控制整个J2ME应用控制其实也没有什么可特别它和我们前面介绍"Hello World"大同小异这里就不展开来说了后面会贴出它全部代码
    Map类主要应该有个Grid 2维用来存放华容道地图还应该有个read_map用来从外部文件读取地图内容填充Grid数据结构再就是要有个draw_map用来把Grid数据结构中地图内容转换成图片显示出来(当然要Draw类pa思路方法)说到读取外部文件笔者知道有两种思路方法:种是传统定义个InputStream对象然后用getClass.getResourceAsStream思路方法取得输入流然后再从输入流中取得外部文件内容例如
InputStream is = getClass.getResourceAsStream("/filename");
(is != null) {
     a = () is.read;
}
这里请注意文件名中根路径是相对于便以后文件放置位置而不是源文件(java)第 2种思路方法是使用onnector.openInputStream思路方法然后打开协议是Resource但是这种思路方法笔者反复尝试都没能调通报告是缺少Resource协议估计第 2种思路方法用到J2ME某些扩展类包此处不再深究由于以前已经做过些类似华容道这样地图这里直接给出Map类代码后面就不再详细解释Map类了以便于我们可以集中精力处理Displayable1中逻辑Map类代码如下:
package huarongroad;

import java.io.InputStream;
import javax.microedition.lcdui.*;

public Map {
//处理游戏地图负责从外部文件加载地图数据存放地图数据并按照地图数据绘制地图

public Grid;//存放地图数据

public Map {//构造负责化地图数据存储结构
this.Grid = [Images.HEIGHT][Images.WIDTH];
//用 2维存放地图数据注意第维是竖直坐标第 2维是水平坐标
}

public read_map( i) {
        <A href="file://从">file://从</A>外部文件加载地图数据并存放在存储结构中返回值是光标点位置
//参数是加载地图文件等级
         a = [2];//光标点位置0是水平位置1是竖直位置
try {
InputStream is = getClass.getResourceAsStream(
"/huarongroad/level".concat(String.valueOf(i)));
(is != null) {
for ( k = 0; k < Images.HEIGHT; k) {
for ( j = 0; j < Images.WIDTH; j) {
this.Grid[k][j] = () is.read;
                         ( 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;
} {
//读取文件失败
a[0] = -1;
a[1] = -1;
}
}catch (Exception ex) {
//打开文件失败
a[0] = -1;
a[1] = -1;
}
         a;
}

public boolean draw_map(Graphics g) {
//Draw类静态思路方法绘制地图
try {
for ( i = 0; i < Images.HEIGHT; i) {
for ( j = 0; j < Images.WIDTH; j) {
Draw.pa(g, this.Grid[i][j], j, i);//绘制地图
}
}
true;
}catch (Exception ex) {
false;
}
}
}
    对于像华容道这样小型地图可以直接用手工来绘制地图内容比如:
fa1c
2232
bd1e
2gg2
gihg
但是如果遇到像坦克大战或超级玛莉那样地图就必须另外开发个地图编辑器了(我会在后续文章中介绍用vb来开发个地图编辑器)
看看时间刚刚好有过了半天休息,休息明天再见
5、详细设计
    详细设计是开发过程中至关重要个环节好在我们在前面各个阶段中已经搭建好了项目所需些工具现在这个阶段中我们只需集中精力设计好Displayable1中逻辑(两天时间当然不只干这点活还要把其他几个类设计修改下)
    Displayable1这个类负责处理控制逻辑首先它需要有表示当前关面变量level、表示当前光标位置变量loc、表示要移动区域变量SelectArea、表示要移动到区域变量MoveArea、表示是否已有区域被选中而准备移动变量Selected和Map类例子MyMap然后我们根据用户按区别键来处理区别消息我们要实现keyPressed中我们处理按键上下左右和选中(Fire)这里处理需要我展开来讲后面我很快会把这部分详细展开接下来是实现pa我们打算在这部分中反复重画背景、地图和选择区域这个必须处理好区域被选中的后画笔颜色切换具体讲就是在没有选中任何区域时要用黑色画笔当选重要移动区域时使用绿色画笔当选择要移动到区域时改用红色画笔(当然附加张流程图是必不可少)再下面要实现RangeMoveRange这两个用来设置要移动区域和要移动到区域思路就是利用前面在Images类中介绍过地图组合标记常量当移动到地图组合标记常量时根据该点地图中值做逆向变换找到相应地图标记常量然后设置相应loc、SelectArea和MoveArea,其中MoveRange还用到了个辅助isInRange,isInRange是用来判断给定点是否在已选中要移动区域的内,如果isInRange返回值是假并且该点处值不是空白就表明要移动到区域侵犯了其他以被占用区域有了RangeMoveRangeMove就水到渠成了,Move将要移动区域移动到要移动到区域,在移动过程中分为 3步进行:第.复制要移动区域,第 2.将复制出要移动区域复制到要移动到区域(这两步分开进行是防止在复制过程中覆盖掉要移动区域),第 3.用isInRange2判断给定点是否在要移动到区域内,将不在要移动到区域内点设置成空白.
    下面我们详细分析下keyPressed实现思路方法:首先,keyPressed要处理按键上下左右和选中(Fire),在处理时需要用Canvas类getGameAction来将按键键值转换成游戏方向,这样可以提高游戏兼容性(区别J2ME实现,其方向键键值不定是相同).接下来,分别处理 4个方向和选中.当按下向上时,先判断是否已经选定了要移动区域(即this.selected是否为真),如果没有选中要移动区域则让光标向上移动格,然后Range设置选择要移动区域,再repa刷新屏幕,否则如果已经选中了要移动区域,就让光标向上移动格,然后MoveRange判断是否能够向上移动已选中区域,如果能移动就repa刷新屏幕,如果不能移动就让光标向下退回到原来位置.当按下向下时,先判断是否已经选定了要移动区域,如果没有选中要移动区域则判断当前所处区域是否为两个格高,如果是两个格高则向下移动两格,如果是个格高则向下移动格,接着再Range设置选择要移动区域,而后repa刷新屏幕,否则如果已经选中了要移动区域,就让光标向下移动格,然后MoveRange判断是否能够向下移动已选中区域,如果能移动就repa刷新屏幕,如果不能移动就让光标向上退回到原来位置.按下向左时情况完全类似向上情况,按下向右时情况完全类似向下情况,因此这里不再赘述,详细情况请参见源代码.当按下选中键时,先判断是否已经选中了要移动区域,如果已经选中了要移动区域就Move完成由要移动区域到要移动到区域移动过程,接着repa刷新屏幕,然后将已选择标记置成false,继续win判断是否完成了任务,否则如果还没有选定要移动区域则再判断当前选中区域是否为空白,如果不是空白就将选中标记置成true,然后刷新屏幕.这里介绍个窍门技巧,在开发遇到复杂逻辑时候,可以构造格打印来将所关心数据结构打印出来以利调试,这里我们就构造个PrGrid,这个纯粹是为了调试的用,效果这得不错.至此我们完成了编码前全部工作.
    看看时间还早,我在这里简单介绍下MIDlet1类,这个类和前面介绍"Hello World"中内容大同小异,先看代码:



其中startApp.pauseApp和destroyApp 3个时MIDlet类 3个抽象思路方法必须要被重载边,他们控制着个J2ME开始.暂停和结束.而其中Displayable1 就是我们前面提到Displayable1个例子,用来负责显示用户界面和控制逻辑.这个类般都是写成这样.
    两天时间稍稍还有些富裕,今天可以早放工.
6.编码
    整个项目共有 5个类,有 4个类代码前面已经介绍过了,而且是在其他项目中使用过相对成熟代码.现在只需全力去实现Displayable1类.Displayable1类代码如下:
package huarongroad;

import javax.microedition.lcdui.*;

public Displayable1 extends Canvas implements CommandListener {

    private loc = [2]; <A href="file://光">file://光</A>标当前位置0是水平位置1是竖直位置
    private SelectArea = [4];//被选定区域即要移动区域
    private MoveArea = [4];//要移动到区域
    private Map MyMap = Map;//地图类
    private boolean selected;//是否已经选中要移动区域标志
    private level;//但前关面
    public Displayable1 {//构造
try {
    jbInit;//JBuilder定义
}catch (Exception e) {
    e.prStackTrace;
}
    }
    private void Init_game{
//化游戏读取地图设置选择区域清空要移动到区域
this.loc = MyMap.read_map(this.level);//读取地图文件并返回光标位置
//0为水平位置1为竖直位置
this.SelectArea[0] = this.loc[0];//化选中区域
        this.SelectArea[1] = this.loc[1];
        this.SelectArea[2] = 1;
        this.SelectArea[3] = 1;
        this.MoveArea[0] = -1;//化要移动到区域
        this.MoveArea[1] = -1;
        this.MoveArea[2] = 0;
        this.MoveArea[3] = 0;
}
    private void jbInit throws Exception {//JBuilder定义
        <A href="file://初">file://初</A>始化例子变量
        this.selected = false;//设置没有被选中要移动区域
this.level = 1;
        Images.init;//化图片常量
Init_game;//化游戏读取地图设置选择区域清空要移动到区域
CommandListener(this);//添加命令监听这是Displayable例子思路方法
addCommand( Command("Exit", Command.EXIT, 1));//添加“退出”按钮
    }

    public void commandAction(Command command, Displayable displayable) {
//命令处理
(command.getCommandType Command.EXIT) {//处理“退出”
    MIDlet1.quitApp;
}
    }

    protected void pa(Graphics g) {
//画图用于绘制用户画面即显示图片勾画选中区域和要移动到区域
try {
    g.drawImage(Images.image_Frame, 0, 0,
Graphics.TOP | Graphics.LEFT);//画背景
            MyMap.draw_map(g);//按照地图内容画图
     ( this.selected )
g.Color(0,255,0);//如果被选中改用绿色画出被选中区域
    g.drawRect(this.SelectArea[0] * Images.UNIT + Images.LEFT,
               this.SelectArea[1] * Images.UNIT + Images.TOP,
       this.SelectArea[2] * Images.UNIT,
       this.SelectArea[3] * Images.UNIT);//画出选择区域
                                                 <A href="file://如">file://如</A>果被选中就用绿色
                                                         <A href="file://否">file://否</A>则使用黑色
            g.Color(255,255,255);//恢复画笔颜色
     (this.selected) {//已经选中了要移动区域
g.Color(255, 0, 255);//改用红色
g.drawRect(this.MoveArea[0] * Images.UNIT + Images.LEFT,
   this.MoveArea[1] * Images.UNIT + Images.TOP,
   this.MoveArea[2] * Images.UNIT,
   this.MoveArea[3] * Images.UNIT);//画出要移动到区域
            g.Color(255, 255, 255);//恢复画笔颜色
    }
}catch (Exception ex) {
}
.out.prln(Runtime.getRuntime.freeMemory);
.out.prln(Runtime.getRuntime.totalMemory);
    }

    private void Range {
//设置移动后能够选中区域
//调整当前光标位置到地图主位置即记录人物信息位置
(this.MyMap.Grid[this.loc[1]][this.loc[0]] Images.DLEFT) {
    this.loc[0] -= 1;//向左调
} (this.MyMap.Grid[this.loc[1]][this.loc[0]] Images.DUP) {
    this.loc[1] -= 1;//向上调
} (this.MyMap.Grid[this.loc[1]][this.loc[0]] Images.DLEFTUP) {
    this.loc[0] -= 1;//向左调
    this.loc[1] -= 1;//向上调
}
this.SelectArea[0] = this.loc[0];//设置光标水平位置
this.SelectArea[1] = this.loc[1];//设置光标竖直位置
//设置光标宽度
(this.loc[0] + 1 < Images.WIDTH) {
    this.SelectArea[2] = this.MyMap.Grid[this.loc[1]][this.loc[0] + 1] != () '1' ?
   1 : 2;
} {
    this.SelectArea[2] = 1;
}
//设置光标高度
(this.loc[1] + 1 < Images.HEIGHT) {
    this.SelectArea[3] = this.MyMap.Grid[this.loc[1] + 1][this.loc[0]] != () '2' ?
          1 : 2;
} {
    this.SelectArea[3] = 1;
}
    }

    private boolean MoveRange {
//设置要移动到区域能够移动返回true,否则返回false
for ( i = 0; i < this.SelectArea[2]; i) {
   for ( j = 0; j < this.SelectArea[3]; j) {
        (this.loc[1] + j >= Images.HEIGHT ||
   this.loc[0] + i >= Images.WIDTH ||
   (!isInRange(this.loc[0] + i, this.loc[1] + j) &&
    this.MyMap.Grid[this.loc[1] + j][this.loc[0] + i] !=
    Images.BLANK)) {
       false;
}
   }
}
this.MoveArea[0] = this.loc[0];
this.MoveArea[1] = this.loc[1];
this.MoveArea[2] = this.SelectArea[2];
this.MoveArea[3] = this.SelectArea[3];
true;
    }

    private boolean isInRange( x, y) {
//判断给定(xy)点是否在选定区域的内x是水平坐标y是竖直坐标
(x >= this.SelectArea[0] &&
    x < this.SelectArea[0] + this.SelectArea[2] &&
    y >= this.SelectArea[1] &&
    y < this.SelectArea[1] + this.SelectArea[3]) {
true;
} {
false;
}
    }

    private boolean isInRange2( x, y) {
//判断给定(xy)点是否在要移动到区域的内x是水平坐标y是竖直坐标
(x >= this.MoveArea[0] &&
    x < this.MoveArea[0] + this.MoveArea[2] &&
    y >= this.MoveArea[1] &&
         y < this.MoveArea[1] + this.MoveArea[3]) {
true;
} {
false;
}
    }

    protected void keyPressed( keyCode) {
//处理按下键盘事件这是Canvas例子思路方法
switch (getGameAction(keyCode)) {//将按键值转化成方向常量
         Canvas.UP://向上
(!this.selected) {//还没有选定要移动区域
         (this.loc[1] - 1 >= 0) {//向上还有移动空间
this.loc[1]--;//向上移动
Range;//设置光标移动区域能将光标移动到地图主位置
repa;//重新绘图
    }
} {//已经选定了要移动区域
     (this.loc[1] - 1 >= 0) {//向上还有移动空间
this.loc[1]--;//向上移动
(MoveRange) {//能够移动能够设置要移动到区域
    repa;//重新绘图
} {//不能移动
    this.loc[1];//退回来
}
    }
}
;
     Canvas.DOWN://向下
(!this.selected) {//还没有选定要移动区域
     (this.loc[1] + 1 < Images.HEIGHT) {//向下还有移动空间
(this.MyMap.Grid[this.loc[1] + 1][this.loc[0]]
    Images.DUP){//该图片有两个格高
this.loc[1];//向下移动
(this.loc[1] + 1 < Images.HEIGHT) {//向下还有
                              <A href="file://移">file://移</A>动空间
    this.loc[1];//向下移动
    Range;//设置光标移动区域
       <A href="file://该">file://该</A>能将光标移动到地图主位置
    repa;//重新绘图
} {//向下没有移动空间
    this.loc[1]--;//退回来
}
} {//该图片只有个格高
this.loc[1];//向下移动
Range;//设置光标移动区域
           <A href="file://该">file://该</A>能将光标移动到地图主位置
repa;//重新绘图
}
    } {
         }
} {//已经选定了要移动区域
     (this.loc[1] + 1 < Images.HEIGHT) {//向下还有移动空间
this.loc[1];//向下移动
(MoveRange) {//能够移动能够设置要移动到区域
    repa;//重新绘图
} {//不能移动
    this.loc[1]--;//退回来
}
    }
}
;
     Canvas.LEFT://向左
(!this.selected) {//还没有选定要移动区域
             (this.loc[0] - 1 >= 0) {//向左还有移动空间
this.loc[0]--;//向左移动
Range;//设置光标移动区域能将光标移动到地图主位置
repa;//重新绘图
    }
} {//已经选定了要移动区域
     (this.loc[0] - 1 >= 0) {//向左还有移动空间
this.loc[0]--;//向左移动
(MoveRange) {//能够移动能够设置要移动到区域
    repa;//重新绘图
} {//不能移动
    this.loc[0];//退回来
}
    }
}
;
     Canvas.RIGHT://向右
(!this.selected) {//还没有选定要移动区域
         (this.loc[0] + 1 < Images.WIDTH) {//向右还有移动空间
(this.MyMap.Grid[this.loc[1]][this.loc[0] + 1]
        Images.DLEFT) {//该图片有两个格宽
this.loc[0];//向右移动
(this.loc[0] + 1 < Images.WIDTH) {//向右还有
                              <A href="file://移">file://移</A>动空间
        this.loc[0];//向右移动
    Range;//设置光标移动区域
       <A href="file://该">file://该</A>能将光标移动到地图主位置
    repa;//重新绘图
} {//向右没有移动空间
    this.loc[0]--;//退回来
}
} {//该图片只有个格宽
this.loc[0];//向右移动
Range;//设置光标移动区域
           <A href="file://该">file://该</A>能将光标移动到地图主位置
repa;//重新绘图
}
    } {
        }
} {//已经选定了要移动区域
     (this.loc[0] + 1 < Images.WIDTH) {//向右还有移动空间
this.loc[0];//向右移动
(MoveRange) {//能够移动能够设置要移动到区域
    repa;//重新绘图
} {//不能移动
    this.loc[0]--;//退回来
}
    }
}
;
     Canvas.FIRE:
(this.selected) {//已经选定了要移动区域
        Move;//将要移动区域移动到刚选中区域
    repa;//重新绘图
    this.selected = false;//清除已选定要移动区域标志
                     ( win) {
.out.prln("win");
    }
} {//还没有选定要移动区域
     (this.MyMap.Grid[this.loc[1]][this.loc[0]]
Images.BLANK) {//要移到位置是个空白
    } {//要移到位置不是空白
this.selected = true;//设置已选定要移动区域标志
    }
                    repa;//重新绘图
                }
;
}
    }

    private boolean win{
        <A href="file://判">file://判</A>断是否已经救出了曹操
( this.MyMap.Grid[Images.HEIGHT - 2 ][Images.WIDTH - 3 ] Images.CAOCAO )
             true;
        
             false;
    }

    private void PrGrid(String a) {
        <A href="file://打">file://打</A>印当前地图内容用于调试
.out.prln(a);
for ( i = 0; i < Images.HEIGHT; i) {
    for ( j = 0; j < Images.WIDTH; j) {
.out.pr( (char)this.MyMap.Grid[i][j]);
    }
    .out.prln("");
}
    }

    private void Move {
        <A href="file://将">file://将</A>要移动区域移动到刚选中区域
(this.MoveArea[0] -1 || this.MoveArea[1] -1 ||
       this.SelectArea[0] -1 || this.SelectArea[1] -1) {//没有选中区域
} {//已经选中了要移动区域和要移动到区域
     temp = [this.SelectArea[3]][this.SelectArea[2]];
        <A href="file://复">file://复</A>制要移动区域这块区域可能会被覆盖掉
    for ( i = 0; i < this.SelectArea[2]; i) {
for ( j = 0; j < this.SelectArea[3]; j) {
      temp[j][i] =
this.MyMap.Grid[this.SelectArea[1] +j]
[this.SelectArea[0] + i];
}
    }
    <A href="file://PrGrid">file://PrGrid</A>("1"); // 调试信息
    <A href="file://将">file://将</A>要移动区域移动到刚选中区域(即要移动到区域)
    for ( i = 0; i < this.SelectArea[2]; i) {
for ( j = 0; j < this.SelectArea[3]; j) {
    this.MyMap.Grid[this.MoveArea[1] + j]
[this.MoveArea[0] + i] = temp[j][i];
}
    }
    <A href="file://PrGrid">file://PrGrid</A>("2");// 调试信息
    <A href="file://将">file://将</A>要移动区域中无用内容置成空白
    for ( i = 0; i < this.SelectArea[3]; i) {
  for ( j = 0; j < this.SelectArea[2]; j) {
     (!isInRange2(this.SelectArea[0] + j,
            this.SelectArea[1] + i)) {//该点是不在要移动到
                                      <A href="file://">file://</A>区域的内需置空
                        this.MyMap.Grid[this.SelectArea[1] + i]
[this.SelectArea[0] + j] = Images.BLANK;
    } {
        }
}
    }
    <A href="file://PrGrid">file://PrGrid</A>("3");// 调试信息
    this.SelectArea[0] = this.MoveArea[0];//重置选中位置水平坐标
    this.SelectArea[1] = this.MoveArea[1];//重置选中位置竖直坐标
            this.MoveArea[0] = -1;//清空要移动到位置
            this.MoveArea[1] = -1;//清空要移动到位置
    this.MoveArea[2] = 0;//清空要移动到位置
            this.MoveArea[3] = 0;//清空要移动到位置
}
    }
}
    代码相关分析,在详细设计阶段已经讲过,代码中有比较相近注释,请读者自行研读分析.将全部代码写好,用wtk2.0自带Ktoolbar工具建立个工程,接下来把去不源文件放到正确位置下,然后点击build,再点run,就完成了编写.当然如果有还要修改和调试.
    经过两天痛苦煎熬,终于有了眉目,编码阶段初战告捷.
7、测试
     作为个真正产品要经过单体测试、结合测试和系统测试由于项目本身简单,而且大部分代码已经是相对成熟,我们跳过单体测试;又由于笔者实际环境所限,无法搞到Java手机,无法架设OTA服务器,因此我们也只能放弃系统测试那么就让我们开始结合测试吧测试的前要先出个测试式样书,也就是测试计划我们将它简化下,只测试如下几种情况:第、对各种形状区域选择和移动;第 2、临近边界区域选择和移动;第 3、同区域反复选择和反复移动;第 4、非法选择和非法移动有了测试目标,接下来工作就是用wtk2.0自带Run MIDP Application工具进行测试打开这个工具,加载huarongRoadjad文件,就会自动运行,选择launch上MIDlet1这个,华容道游戏就会跃然屏幕的上,接下来工作就是左 3点.右 3点,拇指扭扭,来做测试测试过程中发现任何问题,立刻发个bug票给自己,然后就又是痛苦调试和修正bug,如此如此
    两日过后,日渐成熟起来,笔者也日渐消瘦下去,还好熬了过来
8.发布
    谈到发布,其实是个关键,再好产品不能很好发布出去也只是个产品而已,变不成商品也就得不到回报.由于笔者条件所限,这里只能是纸上谈兵,不过还是希望能够使读者对这过程有所了解(网上资料也很多)
    J2ME发布般都是通过OTA(Over The Air),你只需要台有公网IP主机和个普通web Server就可以了(尽管要求很低,但笔者还是没有)这里我们以apache为例介绍下OTA服务配置首先是安装好了apache服务器然后在conf目录下找到mime.types文件在该文件中加入如下两行
application/java-archive                          jar
text/vnd.sun.j2me.app-descriptor                  jad
然后重起apache服务器就可以了接下来工作就是修改jad文件中MIDlet-Jar-URL:后面参数将它改为URL绝对路径即<A href="http://***/">http://***/</A>huarongroad.jar(其中***是你域名或IP地址)在下面就是用java手机下载jad文件它会自动部署相应jar文件并加载它剩下工作就和在模拟器上操作是
9、项目整理总结
    至此我们已经完成了个J2ME游戏全部开发过程中涉及到了调研、分析、设计、编码、测试和发布等方面问题其实在实际工作中还有很多更为具体问题毕竟技术只在软件Software开发过程中占据很有限部分这里限于篇幅限制无法具体展开今后笔者计划再写篇使用J2ME开发手机屏保文章借此机会向读者展示J2ME动画技术;然后再写篇J2ME网络应用文章个类似开心辞典那样知识问答游戏以便向读者展示J2ME网络技术;待这两方面技术交待清楚的后我将引领读者制作个稍大游戏
    通过本文笔者尽力向大家展示J2ME游戏开发全貌但限于水平和经验问题文中不当的处还望广大读者不吝指教联系方式MSN:[email protected].
Tags:  j2me游戏开发源代码 j2me游戏开发 j2me移动开发与实例 j2me开发实例

延伸阅读

最新评论

发表评论