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

J2ME游戏程序开发实例详解

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



六、编码

整个项目共有五个类,有四个类的代码前面已经介绍过了,而且是在其他项目中使用过的相对成熟的代码.现在只需全力去实现Displayable1类.Displayable1类的代码如下:

package huarongroad; 
import javax.microedition.lcdui.*; 
public class Displayable1 extends Canvas implements CommandListener { 
private int[] loc = new int[2]; <A href="file://光">file://光</A>标的当前位置,0是水平位置,1是竖直位置 
private int[] SelectArea = new int[4];//被选定的区域,即要移动的区域 
private int[] MoveArea = new int[4];//要移动到的区域 
private Map MyMap = new Map();//地图类 
private boolean selected;//是否已经选中要移动区域的标志 
private int level;//但前的关面 
public Displayable1() {//构造函数 
try { 
jbInit();//JBuilder定义的初始化函数 
}catch (Exception e) { 
e.printStackTrace(); 
} 
} 
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();//初始化游戏,读取地图,设置选择区域,清空要移动到的区域 
setCommandListener(this);//添加命令监听,这是Displayable的实例方法 
addCommand(new Command("Exit", Command.EXIT, 1));//添加“退出”按钮 
} 
public void commandAction(Command command, Displayable displayable) { 
//命令处理函数 
if (command.getCommandType() == Command.EXIT) {//处理“退出” 
MIDlet1.quitApp(); 
} 
} 
protected void paint(Graphics g) { 
//画图函数,用于绘制用户画面,即显示图片,勾画选中区域和要移动到的区域 
try { 
g.drawImage(Images.image_Frame, 0, 0, 
Graphics.TOP | Graphics.LEFT);//画背景 
MyMap.draw_map(g);//按照地图内容画图 
if ( this.selected ) 
g.setColor(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.setColor(255,255,255);//恢复画笔颜色 
if (this.selected) {//已经选中了要移动的区域 
g.setColor(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.setColor(255, 255, 255);//恢复画笔颜色 
} 
}catch (Exception ex) { 
} 
System.out.println(Runtime.getRuntime().freeMemory()); 
System.out.println(Runtime.getRuntime().totalMemory()); 
} 
private void setRange() { 
//设置移动后能够选中的区域 
//调整当前光标位置到地图的主位置,即记录人物信息的位置 
if (this.MyMap.Grid[this.loc[1]][this.loc[0]] == Images.DLEFT) { 
this.loc[0] -= 1;//向左调 
}else if (this.MyMap.Grid[this.loc[1]][this.loc[0]] == Images.DUP) { 
this.loc[1] -= 1;//向上调 
}else if (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];//设置光标的竖直位置 
//设置光标的宽度 
if (this.loc[0] + 1 < Images.WIDTH) { 
this.SelectArea[2] = this.MyMap.Grid[this.loc[1]][this.loc[0] + 1] != (byte) ′1′ ? 
1 : 2; 
}else { 
this.SelectArea[2] = 1; 
} 
//设置光标的高度 
if (this.loc[1] + 1 < Images.HEIGHT) { 
this.SelectArea[3] = this.MyMap.Grid[this.loc[1] + 1][this.loc[0]] != (byte) ′2′ ? 
1 : 2; 
}else { 
this.SelectArea[3] = 1; 
} 
} 
private boolean setMoveRange() { 
//设置要移动到的区域,能够移动返回true,否则返回false 
for (int i = 0; i < this.SelectArea[2]; i++) { 
for (int j = 0; j < this.SelectArea[3]; j++) { 
if (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)) { 
return 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]; 
return true; 
} 
private boolean isInRange(int x, int y) { 
//判断给定的(x,y)点是否在选定区域之内,x是水平坐标,y是竖直坐标 
if (x >= this.SelectArea[0] && 
x < this.SelectArea[0] + this.SelectArea[2] && 
y >= this.SelectArea[1] && 
y < this.SelectArea[1] + this.SelectArea[3]) { 
return true; 
}else { 
return false; 
} 
} 
private boolean isInRange2(int x, int y) { 
//判断给定的(x,y)点是否在要移动到的区域之内,x是水平坐标,y是竖直坐标 
if (x >= this.MoveArea[0] && 
x < this.MoveArea[0] + this.MoveArea[2] && 
y >= this.MoveArea[1] && 
y < this.MoveArea[1] + this.MoveArea[3]) { 
return true; 
}else { 
return false; 
} 
} 
protected void keyPressed(int keyCode) { 
//处理按下键盘的事件,这是Canvas的实例方法 
switch (getGameAction(keyCode)) {//将按键的值转化成方向常量 
case Canvas.UP://向上 
if (!this.selected) {//还没有选定要移动的区域 
if (this.loc[1] - 1 >= 0) {//向上还有移动空间 
this.loc[1]--;//向上移动一下 
setRange();//设置光标移动的区域,该函数能将光标移动到地图主位置 
repaint();//重新绘图 
} 
}else {//已经选定了要移动的区域 
if (this.loc[1] - 1 >= 0) {//向上还有移动空间 
this.loc[1]--;//向上移动一下 
if (setMoveRange()) {//能够移动,该函数能够设置要移动到的区域 
repaint();//重新绘图 
}else {//不能移动 
this.loc[1]++;//退回来 
} 
} 
} 
break; 
case Canvas.DOWN://向下 
if (!this.selected) {//还没有选定要移动的区域 
if (this.loc[1] + 1 < Images.HEIGHT) {//向下还有移动空间 
if (this.MyMap.Grid[this.loc[1] + 1][this.loc[0]] == 
Images.DUP){//该图片有两个格高 
this.loc[1]++;//向下移动一下 
if (this.loc[1] + 1 < Images.HEIGHT) {//向下还有 
<A href="file://移">file://移</A>动空间 
this.loc[1]++;//向下移动一下 
setRange();//设置光标移动的区域, 
<A href="file://该">file://该</A>函数能将光标移动到地图主位置 
repaint();//重新绘图 
}else {//向下没有移动空间 
this.loc[1]--;//退回来 
} 
}else {//该图片只有一个格高 
this.loc[1]++;//向下移动一下 
setRange();//设置光标移动的区域, 
<A href="file://该">file://该</A>函数能将光标移动到地图主位置 
repaint();//重新绘图 
} 
}else { 
} 
}else {//已经选定了要移动的区域 
if (this.loc[1] + 1 < Images.HEIGHT) {//向下还有移动空间 
this.loc[1]++;//向下移动一下 
if (setMoveRange()) {//能够移动,该函数能够设置要移动到的区域 
repaint();//重新绘图 
}else {//不能移动 
this.loc[1]--;//退回来 
} 
} 
} 
break; 
case Canvas.LEFT://向左 
if (!this.selected) {//还没有选定要移动的区域 
if (this.loc[0] - 1 >= 0) {//向左还有移动空间 
this.loc[0]--;//向左移动一下 
setRange();//设置光标移动的区域,该函数能将光标移动到地图主位置 
repaint();//重新绘图 
} 
}else {//已经选定了要移动的区域 
if (this.loc[0] - 1 >= 0) {//向左还有移动空间 
this.loc[0]--;//向左移动一下 
if (setMoveRange()) {//能够移动,该函数能够设置要移动到的区域 
repaint();//重新绘图 
}else {//不能移动 
this.loc[0]++;//退回来 
} 
} 
} 
break; 
case Canvas.RIGHT://向右 
if (!this.selected) {//还没有选定要移动的区域 
if (this.loc[0] + 1 < Images.WIDTH) {//向右还有移动空间 
if (this.MyMap.Grid[this.loc[1]][this.loc[0] + 1] == 
Images.DLEFT) {//该图片有两个格宽 
this.loc[0]++;//向右移动一下 
if (this.loc[0] + 1 < Images.WIDTH) {//向右还有 
<A href="file://移">file://移</A>动空间 
this.loc[0]++;//向右移动一下 
setRange();//设置光标移动的区域, 
<A href="file://该">file://该</A>函数能将光标移动到地图主位置 
repaint();//重新绘图 
}else {//向右没有移动空间 
this.loc[0]--;//退回来 
} 
}else {//该图片只有一个格宽 
this.loc[0]++;//向右移动一下 
setRange();//设置光标移动的区域, 
<A href="file://该">file://该</A>函数能将光标移动到地图主位置 
repaint();//重新绘图 
} 
}else { 
} 
}else {//已经选定了要移动的区域 
if (this.loc[0] + 1 < Images.WIDTH) {//向右还有移动空间 
this.loc[0]++;//向右移动一下 
if (setMoveRange()) {//能够移动,该函数能够设置要移动到的区域 
repaint();//重新绘图 
}else {//不能移动 
this.loc[0]--;//退回来 
} 
} 
} 
break; 
case Canvas.FIRE: 
if (this.selected) {//已经选定了要移动的区域 
Move();//将要移动的区域移动到刚选中的区域 
repaint();//重新绘图 
this.selected = false;//清除已选定要移动区域的标志 
if ( win()) { 
System.out.println("win"); 
} 
}else {//还没有选定要移动的区域 
if (this.MyMap.Grid[this.loc[1]][this.loc[0]] == 
Images.BLANK) {//要移到的位置是一个空白 
}else {//要移到的位置不是空白 
this.selected = true;//设置已选定要移动区域的标志 
} 
repaint();//重新绘图 
} 
break; 
} 
} 
private boolean win(){ 
<A href="file://判">file://判</A>断是否已经救出了曹操 
if ( this.MyMap.Grid[Images.HEIGHT - 2 ][Images.WIDTH - 3 ] == Images.CAOCAO ) 
return true; 
else 
return false; 
} 
private void PrintGrid(String a) { 
<A href="file://打">file://打</A>印当前地图的内容,用于调试 
System.out.println(a); 
for (int i = 0; i < Images.HEIGHT; i++) { 
for (int j = 0; j < Images.WIDTH; j++) { 
System.out.print( (char)this.MyMap.Grid[i][j]); 
} 
System.out.println(""); 
} 
} 
private void Move() { 
<A href="file://将">file://将</A>要移动的区域移动到刚选中的区域 
if (this.MoveArea[0] == -1 || this.MoveArea[1] == -1 || 
this.SelectArea[0] == -1 || this.SelectArea[1] == -1) {//没有选中区域 
}else {//已经选中了要移动的区域和要移动到的区域 
byte[][] temp = new byte[this.SelectArea[3]][this.SelectArea[2]]; 
<A href="file://复">file://复</A>制要移动的区域,因为这块区域可能会被覆盖掉 
for (int i = 0; i < this.SelectArea[2]; i++) { 
for (int j = 0; j < this.SelectArea[3]; j++) { 
temp[j][i] = 
this.MyMap.Grid[this.SelectArea[1] +j] 
[this.SelectArea[0] + i]; 
} 
} 
<A href="file://PrintGrid">file://PrintGrid</A>("1"); // 调试信息 
<A href="file://将">file://将</A>要移动的区域移动到刚选中的区域(即要移动到的区域) 
for (int i = 0; i < this.SelectArea[2]; i++) { 
for (int j = 0; j < this.SelectArea[3]; j++) { 
this.MyMap.Grid[this.MoveArea[1] + j] 
[this.MoveArea[0] + i] = temp[j][i]; 
} 
} 
<A href="file://PrintGrid">file://PrintGrid</A>("2");// 调试信息 
<A href="file://将">file://将</A>要移动的区域中无用内容置成空白 
for (int i = 0; i < this.SelectArea[3]; i++) { 
for (int j = 0; j < this.SelectArea[2]; j++) { 
if (!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; 
}else { 
} 
} 
} 
<A href="file://PrintGrid">file://PrintGrid</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,就完成了程序的编写.当然如果有错误还要修改和调试。

七、测试

作为一个真正的产品要经过单体测试、结合测试和系统测试。由于项目本身简单,而且大部分代码已经是相对成熟的,我们跳过单体测试;又由于笔者的实际环境所限,无法搞到Java手机,无法架设OTA服务器,因此我们也只能放弃系统测试。那么就让我们开始结合测试吧。测试之前要先出一个测试式样书,也就是测试的计划。我们将它简化一下,只测试如下几种情况:第一、对各种形状的区域的选择和移动;第二、临近边界区域的选择和移动;第三、同一区域的反复选择和反复移动;第四、非法选择和非法移动。有了测试的目标,接下来的工作就是用wtk2.0自带的Run MIDP Application工具进行测试。打开这个工具,加载huarongRoad的jad文件,程序就会自动运行,选择launch上MIDlet1这个程序,华容道游戏就会跃然屏幕之上,接下来的工作就是左三点.右三点,拇指扭扭,来做测试。测试过程中发现任何的问题,立刻发一个bug票给自己,然后就又是痛苦的调试和修正bug,如此如此。

八、发布

谈到发布,其实是个关键,再好的产品不能很好的发布出去也只是个产品而已,变不成商品也就得不到回报.由于笔者的条件所限,这里只能是纸上谈兵,不过还是希望能够使读者对这一过程有所了解(网上的资料也很多)。

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文件并加载它。剩下的工作就和在模拟器上操作是一样的了。

九、项目总结

至此,我们已经完成了一个J2ME游戏的全部开发过程,程序中涉及到了调研、分析、设计、编码、测试和发布等方面的问题,其实在实际的工作中还有很多更为具体的问题,毕竟技术只在软件开发过程中占据很有限的一部分,这里限于篇幅的限制无法一一具体展开。今后,笔者计划再写一篇使用J2ME开发手机屏保的文章,借此机会向读者展示J2ME动画技术;然后再写一篇J2ME网络应用的文章,做一个类似开心辞典那样的知识问答游戏,以便向读者展示J2ME的网络技术;待这两方面的技术交待清楚之后,我将引领读者制作一个稍大一些的游戏。 (T117)

[1] [2] [3]

责任编辑 webmaster

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