2008-2009学年第一学期
《J2ME 程序设计》
游戏设计报告
第一章、开发平台与技术概述
1.1开发环境
操作系统:Microsoft Windows XP
程序语言:Java 2
开发包:Sun Java(TM) Wireless Toolkit 2.5.2 for CLDC
IDE :Eclipse 3.2
1.2WTK 、ECLIPSE 与JAM 的介绍
(1) WTK 的全称是Sun J2ME Wireless Toolkit —— Sun的无线开发工具包。这一工具包的设计目的是为了帮助开发人员简化j2me 的开发过程。使用其中的工具可以开发与 Java Technology for the Wireless Industry (JTWI, JSR 185) 规范兼容的设备上运行的j2me 应用程序。该工具箱包含了完整的生成工具、实用程序以及设备仿真器。到本文写作时为止可以获取有四个版本,分别是1.0.4, 2.0, 2.1和2.2。每个版本都包括英语,日语,简体中文,繁体中文4个语种包。
WTK 是Sun 提供的一个开发工具包。目前各大手机厂商往往把WTK 经过自身的简化与改装,推出适合自身产品,如SonyEricsson ,Nokia Developer’s suit等,都属于此种类型;而通过JBuilder, Eclipse等IDE ,J2ME 开发包工具可以被绑定在这些集成开发环境中,进一步提高开发效率。
(2) Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括 Java 开发工具(Java Development Tools,JDT )。
Eclipse 是一个开放源代码的软件开发项目,专注于为高度集成的工具开发提供一个全功能的、具有商业品质的工业平台。它主要由Eclipse 项目、Eclipse 工具项目和Eclipse 技术项目三个项目组成,具体包括四个部分组成——Eclipse Platform 、JDT 、CDT 和PDE.JDT 支持Java 开发、CDT 支持C 开发、PDE 用来支持插件开发,Eclipse Platform 则是一个开放的可扩展IDE ,提供了一个通用的开发平台。它提供建造块和构造并运行集成软件开发工具的基础。
1.3JA V A 语言介绍
(1)面向对象
面向对象其实是现实世界模型的自然延伸。现实世界中任何实体都可以看作是对象。对象之间通过消息相互作用。另外,现实世界中任何实体都可归属于某类事物,任何对象都是某一类事物的实例。如果说传统的过程式编程语言是以过程为中心以算法为驱动的话,面向对象的编程语言则是以对象为中心以消息为驱动。用公式表示,过程式编程语言为:程序=算法+数据;面向对象编程语言为:程序=对象+消息。
(2)平台无关性
Java 是平台无关的语言是指用Java 写的应用程序不用修改就可在不同的软硬件平台上运行。平台无关有两种:源代码级和目标代码级。C 和C++具有一定程度的源代码级平台无关,表明用C 或C++写的应用程序不用修改只需重新编译就可以在不同平台上运行。
(3)分布式
分布式包括数据分布和操作分布。数据分布是指数据可以分散在网络的不同主机上,操
作分布是指把一个计算分散在不同主机上处理。
(4)可靠性和安全性
Java 最初设计目的是应用于电子类消费产品,因此要求较高的可靠性。Java 虽然源于C++,但它消除了许多C++不可靠因素,可以防止许多编程错误。首先,Java 是强类型的语言,要求显式的方法声明,这保证了编译器可以发现方法调用错误,保证程序更加可靠;其次,Java 不支持指针,这杜绝了内存的非法访问;第三,Java 的自动单元收集防止了内存丢失等动态内存分配导致的问题;第四,Java 解释器运行时实施检查,可以发现数组和字符串访问的越界,最后,Java 提供了异常处理机制,程序员可以把一组错误代码放在一个地方,这样可以简化错误处理任务便于恢复。
(5)多线程
线程是操作系统的一种新概念,它又被称作轻量进程,是比传统进程更小的可并发执行的单位。
第二章 J2ME 及其体系结构概述
2.1 J2ME简介
现在JA V A-JA V A2 Platform 大致可分为J2SE 、J2EE 、J2ME 三类。J2SE 为JA V A-JA V A2 Platform 的标准版,通常在PC 上使用的JA V A 。J2EE 是在J2SE 的API 上,扩展了给企业使用EJB 与Servlet 等主要使用在服务器上的功能。而J2ME 则是面向家电和通信工具等微小设备。
J2ME 的目标是微小设备,这类设备有许多种类,在这些设备的J2ME 当中,定义了CDC (Connected Device Configuration ),CLDC(Connected Limited Device Configuration) 这两个Configuration 。
CDC 是以能用在个人网关、下一代移动电话、PDA (个人数字助理)、家电设备、POS 终端、车辆导航系统等上运行为前提设计出来的。CLDC ,是以能使用在移动电话、PDA (个人数字助理)、家电设备、POS 终端等上面为前提设计出来的。
2.2 J2ME 体系结构
配置和简表:配置定义了一个设计在一系列类似硬件上运行的Java 平台的能力。J2me 的目标是家电与嵌入式设备,这类设备有很多种类,对应这些设备的J2me 当中,定义了CDC (连接设备配置)和CLDC (受限接设备配置)两个配置,配置所提供的类函数只有Java.lang, Java.io,Java..util 包等J2SE 等核心类的子集。CLDC 是与手机相关的,它实际是定义一个设备制造商用来实现Java 运行环境的标准,第三方开发者只需要遵循这个标准。CLDC 主要影响Java 开发和软件发布的以下方面:
1)目标设备的特性;
2)安全模型:安全模型有两个部分,虚拟机和应用程序安全。虚拟机安全层的目的是保护下面的设备不受任何执行代码可能引起的损害。
2.3 移动信息设备简表概述
2.3.1 MIDP的目标硬件环境
1)显示 :96*54象素,1位颜色,大约1:1比例;
2)输入: (a)单手键盘或者小键盘;
(b)双手的QWERTV 键盘;
(c)触摸屏。
3)内存: (a)128KB非易失性内存,供MIDP 组件使用;
(b)8KB的非易失性内存,供应用程序生成的永久数据使用;
(c)32KB的易失性内存,供Java 堆使用。
4)网络: (a)双响无线网络,可间歇性连接;
(b)通常是很有限的带宽。
2.3.2 MIDP应用程序
在MIDP 上运行的Java 程序被称为MIDlet 。MIDlet 是服从一些关于它运行环境和程序打包的一些规则。其运行环境是启动MIDlet ,这是设备内置应用管理器的任务。应用程序能访问到下面的一些资源:(1)震动文件中的所有文件。(2)MIDlet 描叙文件的内容(JAD )。
2.3.3 CLDC和MIDP 库中的类
打包:MIDlet 应用一般采用震动文件的形式。这个文件应该包含运行应用程序需要的类和所有的资源文件,它也应该包含一个叫manifest.mf 的清单文件。保存在震动文件中的清单文件是一个简单的文本文件,包含属性值。
应用程序描叙(JADs )在移动电话网络上传送大量的数据,需要有一个描叙文件让用户在没有下载整个包之前来查看一下MIDlet 震动的细节。
第三章 手机游戏开发过程
3.1 贪吃蛇游戏的规则简介
贪吃蛇游戏的规则简介
在贪吃蛇游戏中,玩家操作由小方块连接而成的蛇,去吃随机散落在画面内的小方块,每吃一块就增加一小方块长度,要是撞壁以及撞自己的尾,就属于失败,如无失败则直到通关为止。
屏幕的长度的行向为11单位,纵向为18单位。在这个范围内,玩家通过操作方向键来控制蛇的运动方向。该游戏的最大特色是屏幕自适应,无论各种手机,PDA 的屏幕大小如何,该游戏总是能获得最佳的显示效果。
3.2 详细设计
本游戏的操作流程:用户在启动MIDlet 后,即进入游戏主画面,屏幕开始显示为欢迎画面。用户按下[启动]按钮后,就可以开始玩游戏。当用户想暂停时,再次按一下[启动]按钮开始,游戏就暂停了,在暂停的情况下再按[开始]按钮,游戏继续运行。任何时候按[EXIT]按钮退出,游戏MIDlet 都会终止。
图:游戏流程转换过程
3.2.1 代码设计
(1)游戏地图代码设计
游戏地图是蛇的活动范围和食物随机散落的范围,游戏的容器为行向为11单位,纵向为18单位,如下代码:
private final int iX = 10; //地图的开始坐标
private final int iY = 10; //
private final int SWIDTH = 16; //图标的宽度
private final int iCells = 240; //地图的列数
private final int iRows = 320; //地图的行数
private final int iBoxW = SWIDTH*iCells; //地图的宽
采用二维绘图工具:二维绘图工具drawLine 采用4个参数——直线起点的x 、y 坐标值和直线终点x 、y 的坐标值,例如:
graphics.drawLine (50,0,100,0);
这行代码会从位置(50,0)到(100,0)绘制一条直线。
绘制一个矩形是一个类似的过程,不同的只是需要用起点加上宽度和高度的方式来指定这个对象。可以绘制透明的或者填充的矩形,甚至可以绘制圆角的矩形。4个绘制矩形的方法是:drawRect 、drawRoundedRect 、fillRect 和fillRoundedRect 。
(2)贪吃蛇和食物的代码设计
屏幕的长度为行向为320单位,纵向为240单位;在这个范围内,玩家操作方向键控制蛇的运动方向。该游戏的最大特色是屏幕自适应,无论各种手机,PDA 的屏幕大小如何,该游戏总是能获得最佳的显示效果。
贪吃蛇最初由10个小正方形组成,小正方形是蛇的身体和游戏容器的组成部分。食物也由一块小正方形组成,并且随机散落在游戏框图的区域内,每次只出现唯一的一个,待玩家操作游戏完成一个任务后面,再出现下个食物,小蛇每吃一个食物就增加一个长度。实现代码如下:
public int getCell(){
return iCell;
}
public int getRow(){
return iRow;
}
public void show(){
sLabel.setVisible(true);
}
public boolean isVisible(){
return sLabel.isVisible();
}
public void hide(){
sLabel.setVisible(false);
}
protected void setPosition(int row, int cell){
iCell = cell;
iRow = row;
sLabel.setBounds(cell*SWIDTH, row*SWIDTH, SWIDTH, SWIDTH);
}
public void moveUp(){
if(iRow>0)
setPosition(--iRow,iCell);
else
setTouch();
}
public void moveDown(){
if(iRow
setPosition(++iRow,iCell);
else
setTouch();
}
public void moveLeft(){
if(iCell>0)
setPosition(iRow,--iCell);
else
setTouch();
}
public void moveRight(){
if(iCell
setPosition(iRow,++iCell);
else
setTouch();
}
static boolean getTouch(){
return isTouch;
}
static void setTouch(){
isTouch = true;
}
static void setImTouch(){
isTouch = false;
}
public Snake clone(){
Snake tem = new Snake();
tem.setPosition(this.getRow(),this.getCell());
return tem;
}
(3)操作控制代码设计
MIDP 的游戏设计,本质上就是用一个线程或者定时器产生重绘事件, 用线程和用户输入改变游戏状态。这个游戏也不例外,启动MIDlet 后,就立即生成一个重绘线程,该线程每隔50ms 绘制一次屏幕。当然,重绘时有一些优化措施,并不是屏幕上所有的像素都需要重绘,而是有所选择,比如游戏画布上那些已经固定下来的就不需重绘。游戏画布是一个
CommandListener, 可以接受用户键盘命令,控制蛇的左移,右移,下移,上移。其代码如下: public void move(){
hide();
if(aoSnakes[0].getRow()==Food.getRow()&&aoSnakes[0].getCell()==Food.getCell())
eat();
Snake tempSnakes[] = new Snake[iLen-1];
for(int i=0;i
tempSnakes[i] = aoSnakes[i].clone();
}
switch(heading){
case 37:
aoSnakes[0].moveLeft();
break;
case 38:
aoSnakes[0].moveUp();
break;
case 39:
aoSnakes[0].moveRight();
break;
case 40:
aoSnakes[0].moveDown();
break;
default:
break;
整个游戏的流程控制体现在游戏画布对象的paint()方法里。paint()根据当前的游戏状态,绘制出当时的游戏画面。欢迎画面和Game Over 画面的绘制相当简单,游戏暂停画面的绘制也相当容易,就是设立标志,让paint()执行的时候无需真正执行重绘动作。
第四章 程序的调试与运行
4.1游戏的调试
在手机游戏方面,游戏的控制和图片的设置是游戏的基本能力,为了方便玩家操作游戏,把玩家常用的操作键设为游戏最常用的控制键,并通过J2ME 简单有效的编码,使玩家可以重新设置自己的熟悉的键。本游戏不设立关卡,而是随着游戏积分的增加,游戏将自动增加游戏的难度,如加快游戏速度和随小蛇长度的增加,游戏的操作难度也会增大。如玩家进入游戏(如下图),操作小蛇,
图:游戏界面图
去吃随机散落在游戏区内的食物,每吃一个食物就增加小蛇一个单位长度,并随着长度的增加,游戏的速度将增加,而小蛇的长度增加也会增加玩家的操作难度。
如果玩家操作小蛇碰壁或者由于小蛇长度太长而蛇头撞到小蛇的身体任何部位,均属于失败,玩家得重新游戏开始(如下图)。但游戏将自动纪录你的得分,并且保存你的游戏最高得分。
图:游戏失败的两种情况
4.2 游戏的运行环境
目标硬件环境:
1:显示: 240*320象素;
2:输入:(a)单手键盘或者小键盘;
(b)双手的QWERTV 键盘;
(c)触摸屏。
3)内存 (a)128KB非易失性内存,供MIDP 组件使用;
(b)8KB的非易失性内存,供应用程序生成的永久数据使用;
(c)32KB的易失性内存,供Java 堆使用。
第五章 总结
通过本次J2ME 手机游戏设计,让我更深刻的了解程序设计的基本思想,巩固了J2ME 程序设计的基本知识。本次课程设计所开发的手机游戏,实现了手机游戏的一些基本和必需的功能。如游戏的界面设置,游戏动画的设置,玩家的控制键设置,游戏的保存和游戏纪录的保存,游戏的开始和暂停等功能。但游戏功能还存在着很多不足,首先,对游戏的动画设计不够熟悉,技巧性不强,界面的设置也不够美观。这些对于玩家来说是非常重要,将直接决定本游戏的生存能力。其次,有些功能不能实现。这次游戏的功能没能做到十全十美,是由于自身能力的不足和时间的限制,没有对游戏的开发进行进一步研究,还有很多地方需要改进。
致 谢
通过这次毕业设计开阔了我的视野,增添了自己挑战的信心。在实现的过程当中得到很多人的帮助,在此,要特别感谢我的指导老师耐心细致的指导,使我从中受益匪浅。 参考文献
[1] J2ME实用教程[M].北京:人民邮电出版社.2007.1
[2]J2ME游戏编程[M].北京:清华大学出版社.2005.11
[3]孙淑敏.Java2游戏开发北京[M].北京:清华大学出版社.2005
[4]史斌星.Java 基础编程贯通教材 [M]. 北京:清华大学出版社.2005
[5]杨绍方.Java 程序基础设计 [M]. 北京:科学出版社.2005
附部分代码:
SnakeMIDlet.java 文件
//文件名:SnakeMIDlet.java package snake;
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; private static final int KEY_UP private static final int KEY_DOWN private static final int KEY_LEFT private static final int KEY_RIGHT private static final int KEY_FIRE = 1; = 4; = 5;
= 2; = 3;
public class SnakeMIDlet extends MIDlet {
SnakeCanvas displayable = new public static int s_width = 0; SnakeCanvas();
public static int s_height = 0; public SnakeMIDlet() { public static long updates
= 0; public static Random rand;
Display.getDisplay(this ).setCurrent(displayab
le); private int maxRand = 1000; }
private int map_x = 10; public void startApp() {} private int map_y = 10; public void pauseApp() {} private int map_w = 16; public void destroyApp(boolean private int map_h = 16;
unconditional) {}
}
private int key_x = map_x / 2;
private int key_y = map_y / 2; cGame.java 文件
private int snake_w = 8; ////////////////////////////////////////////////////////////////////////////////
private int snake_h
= 8;
//
private int pos_x = map_x / 2; // cGame.java private int pos_y = map_y / 2; // private int aspect_x= 0; //
private int aspect_y= 1; // Author(s): Xu Qinghui private int snake_max = 50; // Create: 2008-12-1 private int snake_min = 5;
//////////////////////////////////////////////////////////////////////////////// private int snake_n
= snake_min;
package code; private int gameState = STATEPLAY;
private int level = 1;
import java.util.Random; private long sleepTime =300;
import javax.microedition.lcdui.*;
private int[] snake_x = //////////////////////////////////////////////////////////////////////////////// int[ snake_max ];
private int[] snake_y
=
class cGame extends Canvas implements Runnable int[ snake_max ]; { private static final int STATEPLAY = 0; private int[][] map;
private static final int STATELOST = 1;
private boolean isShowInfo = false;
private static final int STATEWIN
= 2;
private Font
font
=
new new
Font.getFont( 100, 200
);
fungus.life = 5;
}
private Thread thread;
Font.FACE_SYSTEM,
new
{
Font.STYLE_BOLD, Font.SIZE_LARGE );
imgItem[i]=Image.createImage("/pics/item_16_"+ }
}
Image temp Graphics gn;
=
i+".png");
=
private Image[] imgGameBg = new Image[4]; private Image[] imgItem private Gif[] private Gif[] private Gif cGame()
setFullScreenMode(true); s_width = getWidth(); s_height= getHeight(); rand = try {
gold dung fungus;
Image[3];
= new Gif[5]; = new Gif[2];
Image.createImage("/pics/bg_tile_0.png");
for( int i=0; i
= null;
temp = null; System.gc();
imgGameBg[i] gn
= =
i++ )
{
Image.createImage(16, 16); new
public void run() {
while( true ) {
try { { }
e.printStackTrace(); updates++; repaint(); serviceRepaints(); thread.sleep(sleepTime);
rePlay( level );
thread = new Thread(this); thread.start();
imgGameBg[i].getGraphics();
gn.drawImage(temp, -i*16, 0,
gn.LEFT|gn.TOP);
Random( System.currentTimeMillis() );
for( int i=0; i
for( int i=0; i
fungus = new Gif("fungus", 12, 25, 23 ); fungus.setFormTime (
new int[] { 2000, 150, 150, 150,
dung[i]= new Gif("dung", 6, 21, 30 ); dung[i].life = -2; // --life
gold[i]= new Gif("gold", 5, 19, 15 );
}catch(Exception e){ e.printStackTrace(); }
150, 200, 100, 200, 150, 200,
// --life +5
}
}catch(Exception e)
fungus.isShow = false;
for( int i=0; i
}
public void paint(Graphics g) {
g.setClip(0, 0, s_width, s_height); g.setColor(0x000000);
for( int i=0; i
for( int j=0; j
{
j*map_h, i*map_w, g.LEFT|g.TOP); } }
for( int i=0; i
for( int i=0; i
if( snake_n>20 ) { }
paintSnake( g );
if( isShowInfo || gameState != STATEPLAY ) {
g.setColor(0xFFFFFF);
fungus.isShow = true; fungus.paint( g ); dung[i].paint(g); gold[i].paint(g);
g.fillRect(0, 0, s_width, s_height);
g.drawLine(0,
i*map_h,
{ } }
g.setColor( 0xff0000 ); g.setFont( font );
g.drawString( "life:"+snake_n, 2, 2, 0 ); g.drawString( "level:"+level, 2, 18, 0 ); }
g.drawImage(imgGameBg[ map[i][j] ],
void paintSnake( Graphics g ) {
g.setColor(0x0000FF); {
snake_x[i] = snake_x[i-1]; snake_y[i] = snake_y[i-1]; }
snake_x[0] += aspect_x*8; snake_y[0] += aspect_y*8; g.setColor(0x6666FF); g.fillRect(snake_x[0]-snake_w/2, snake_y[0]-snake_h/2, snake_w, snake_h); if( { } { if(
)
isIntersect(snake_x[0],
snake_y[0],
snake_w, snake_h,
snake_x[i],
for( int i=snake_min; i
rePlay(level); snake_x[0]
||
snake_x[0]>s_width
||
snake_y[0]s_height )
g.fillRect(snake_x[i]-snake_w/2,
snake_y[i]-snake_h/2, snake_w, snake_h);
for( int i=snake_n; i>0; i-- )
g.setFont( font );
map_y*map_w, i*map_h);
for( int i=0; i
for( int i=0; i
//
g.drawLine(i*map_w, 0, i*map_w,
=== {
snake_y[i], snake_w, snake_h )
} }
rePlay(level);
x2, int y2, int w2, int h2) { if( { } else return false; return true; Math.abs(x2-x1)
(w1+w2)/2
&&
Math.abs(y2-y1)
for( int i=0; i
if( isIntersect(snake_x[0], snake_y[0],
snake_w, snake_h,
gold[i].pos_x,
gold[i].pos_y,
gold[i].w, gold[i].h )
) { addSnake( gold[i].life ); gold[i].setPos();
}
}
for( int i=0; i
if( isIntersect(snake_x[0], snake_y[0],
snake_w, snake_h,
dung[i].pos_x,
dung[i].pos_y, dung[i].w, d ung[i].h )
) { addSnake( dung[i].life ); dung[i].setPos(); } }
if( fungus.isShow && isIntersect(snake_x[0], snake_y[0],
snake_w, snake_h,
fungus.pos_x,
fungus.pos_y,
fungus.w, fungus.h )
) { addSnake( fungus.life ); // fungus.isShow =false; fungus.setPos();
}
}
boolean isIntersect(int x1,int y1, int w1, int h1, int
}
public void keyPressed(int key) {
key = Math.abs(key);
System.out.println("key="+key); switch( key ) { case KEY_NUM2: case KEY_UP: if( gameState != STATEPLAY )
break;
else { if( aspect_y
}
break;
case KEY_NUM8: case KEY_DOWN: if( gameState != STATEPLAY ) break;
else { if( aspect_y >= 0) { aspect_x = 0;
aspect_y = +1; }
}
break;
case KEY_NUM4: case KEY_LEFT: if( gameState != STATEPLAY )
break;
else { if( aspect_x
aspect_x = -1; }
}
break;
case KEY_NUM6: case KEY_RIGHT: if( gameState != STATEPLAY ) break;
else { if( aspect_x >= 0) { aspect_y = 0;
aspect_x = +1; }
}
break;
case KEY_FIRE: case KEY_NUM5: if( gameState == STATEPLAY ) { //
addSnake();
//
System.out.println("snake_n="+snake_n);
}
break;
case KEY_NUM1:
break;
case KEY_NUM3:
isShowInfo = !isShowInfo;
break;
case KEY_NUM0:
rePlay( level );
break;
}
this.repaint(); }
private void addSnake(int life) {
int s_n = snake_n; snake_n += life; if( snake_n >= snake_max ) { level++; rePlay(level);
}
else if( snake_n
{
rePlay(level); }
else if( life>0 ) { for( int i=s_n; i
}
}
public void rePlay( int level ) {
sleepTime = 300-level*20; map_x = s_height/16; map_y = s_width/16; key_x = map_x>>1; key_y
= map_y>>1;
gameState = STATEPLAY; map = new int[map_x][map_y]; isShowInfo = false;
snake_n
= snake_min;
aspect_x = 0; aspect_y = 0; try {
for( int i=0; i
for( int j=0; j
int r = rand.nextInt(maxRand); for( { }
if( r >k+1 ) { }
map[i][j] = k; int
k=0;
Image temp Graphics gn;
=
} }
Image.createImage("/pics/bg_tile_"+(level%2)+".png");
for( int i=0; i
= null;
temp = null; System.gc();
imgGameBg[i] gn
= =
i++ )
Image.createImage(16, 16); imgGameBg[i].getGraphics();
gn.drawImage(temp, -i*16, 0,
gn.LEFT|gn.TOP);
}catch(Exception e){ e.printStackTrace(); }
k
for( int i=0; i
snake_x[i] = s_width >>1;
snake_y[i] = s_height>>1;//-(i*snake_h);
2008-2009学年第一学期
《J2ME 程序设计》
游戏设计报告
第一章、开发平台与技术概述
1.1开发环境
操作系统:Microsoft Windows XP
程序语言:Java 2
开发包:Sun Java(TM) Wireless Toolkit 2.5.2 for CLDC
IDE :Eclipse 3.2
1.2WTK 、ECLIPSE 与JAM 的介绍
(1) WTK 的全称是Sun J2ME Wireless Toolkit —— Sun的无线开发工具包。这一工具包的设计目的是为了帮助开发人员简化j2me 的开发过程。使用其中的工具可以开发与 Java Technology for the Wireless Industry (JTWI, JSR 185) 规范兼容的设备上运行的j2me 应用程序。该工具箱包含了完整的生成工具、实用程序以及设备仿真器。到本文写作时为止可以获取有四个版本,分别是1.0.4, 2.0, 2.1和2.2。每个版本都包括英语,日语,简体中文,繁体中文4个语种包。
WTK 是Sun 提供的一个开发工具包。目前各大手机厂商往往把WTK 经过自身的简化与改装,推出适合自身产品,如SonyEricsson ,Nokia Developer’s suit等,都属于此种类型;而通过JBuilder, Eclipse等IDE ,J2ME 开发包工具可以被绑定在这些集成开发环境中,进一步提高开发效率。
(2) Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括 Java 开发工具(Java Development Tools,JDT )。
Eclipse 是一个开放源代码的软件开发项目,专注于为高度集成的工具开发提供一个全功能的、具有商业品质的工业平台。它主要由Eclipse 项目、Eclipse 工具项目和Eclipse 技术项目三个项目组成,具体包括四个部分组成——Eclipse Platform 、JDT 、CDT 和PDE.JDT 支持Java 开发、CDT 支持C 开发、PDE 用来支持插件开发,Eclipse Platform 则是一个开放的可扩展IDE ,提供了一个通用的开发平台。它提供建造块和构造并运行集成软件开发工具的基础。
1.3JA V A 语言介绍
(1)面向对象
面向对象其实是现实世界模型的自然延伸。现实世界中任何实体都可以看作是对象。对象之间通过消息相互作用。另外,现实世界中任何实体都可归属于某类事物,任何对象都是某一类事物的实例。如果说传统的过程式编程语言是以过程为中心以算法为驱动的话,面向对象的编程语言则是以对象为中心以消息为驱动。用公式表示,过程式编程语言为:程序=算法+数据;面向对象编程语言为:程序=对象+消息。
(2)平台无关性
Java 是平台无关的语言是指用Java 写的应用程序不用修改就可在不同的软硬件平台上运行。平台无关有两种:源代码级和目标代码级。C 和C++具有一定程度的源代码级平台无关,表明用C 或C++写的应用程序不用修改只需重新编译就可以在不同平台上运行。
(3)分布式
分布式包括数据分布和操作分布。数据分布是指数据可以分散在网络的不同主机上,操
作分布是指把一个计算分散在不同主机上处理。
(4)可靠性和安全性
Java 最初设计目的是应用于电子类消费产品,因此要求较高的可靠性。Java 虽然源于C++,但它消除了许多C++不可靠因素,可以防止许多编程错误。首先,Java 是强类型的语言,要求显式的方法声明,这保证了编译器可以发现方法调用错误,保证程序更加可靠;其次,Java 不支持指针,这杜绝了内存的非法访问;第三,Java 的自动单元收集防止了内存丢失等动态内存分配导致的问题;第四,Java 解释器运行时实施检查,可以发现数组和字符串访问的越界,最后,Java 提供了异常处理机制,程序员可以把一组错误代码放在一个地方,这样可以简化错误处理任务便于恢复。
(5)多线程
线程是操作系统的一种新概念,它又被称作轻量进程,是比传统进程更小的可并发执行的单位。
第二章 J2ME 及其体系结构概述
2.1 J2ME简介
现在JA V A-JA V A2 Platform 大致可分为J2SE 、J2EE 、J2ME 三类。J2SE 为JA V A-JA V A2 Platform 的标准版,通常在PC 上使用的JA V A 。J2EE 是在J2SE 的API 上,扩展了给企业使用EJB 与Servlet 等主要使用在服务器上的功能。而J2ME 则是面向家电和通信工具等微小设备。
J2ME 的目标是微小设备,这类设备有许多种类,在这些设备的J2ME 当中,定义了CDC (Connected Device Configuration ),CLDC(Connected Limited Device Configuration) 这两个Configuration 。
CDC 是以能用在个人网关、下一代移动电话、PDA (个人数字助理)、家电设备、POS 终端、车辆导航系统等上运行为前提设计出来的。CLDC ,是以能使用在移动电话、PDA (个人数字助理)、家电设备、POS 终端等上面为前提设计出来的。
2.2 J2ME 体系结构
配置和简表:配置定义了一个设计在一系列类似硬件上运行的Java 平台的能力。J2me 的目标是家电与嵌入式设备,这类设备有很多种类,对应这些设备的J2me 当中,定义了CDC (连接设备配置)和CLDC (受限接设备配置)两个配置,配置所提供的类函数只有Java.lang, Java.io,Java..util 包等J2SE 等核心类的子集。CLDC 是与手机相关的,它实际是定义一个设备制造商用来实现Java 运行环境的标准,第三方开发者只需要遵循这个标准。CLDC 主要影响Java 开发和软件发布的以下方面:
1)目标设备的特性;
2)安全模型:安全模型有两个部分,虚拟机和应用程序安全。虚拟机安全层的目的是保护下面的设备不受任何执行代码可能引起的损害。
2.3 移动信息设备简表概述
2.3.1 MIDP的目标硬件环境
1)显示 :96*54象素,1位颜色,大约1:1比例;
2)输入: (a)单手键盘或者小键盘;
(b)双手的QWERTV 键盘;
(c)触摸屏。
3)内存: (a)128KB非易失性内存,供MIDP 组件使用;
(b)8KB的非易失性内存,供应用程序生成的永久数据使用;
(c)32KB的易失性内存,供Java 堆使用。
4)网络: (a)双响无线网络,可间歇性连接;
(b)通常是很有限的带宽。
2.3.2 MIDP应用程序
在MIDP 上运行的Java 程序被称为MIDlet 。MIDlet 是服从一些关于它运行环境和程序打包的一些规则。其运行环境是启动MIDlet ,这是设备内置应用管理器的任务。应用程序能访问到下面的一些资源:(1)震动文件中的所有文件。(2)MIDlet 描叙文件的内容(JAD )。
2.3.3 CLDC和MIDP 库中的类
打包:MIDlet 应用一般采用震动文件的形式。这个文件应该包含运行应用程序需要的类和所有的资源文件,它也应该包含一个叫manifest.mf 的清单文件。保存在震动文件中的清单文件是一个简单的文本文件,包含属性值。
应用程序描叙(JADs )在移动电话网络上传送大量的数据,需要有一个描叙文件让用户在没有下载整个包之前来查看一下MIDlet 震动的细节。
第三章 手机游戏开发过程
3.1 贪吃蛇游戏的规则简介
贪吃蛇游戏的规则简介
在贪吃蛇游戏中,玩家操作由小方块连接而成的蛇,去吃随机散落在画面内的小方块,每吃一块就增加一小方块长度,要是撞壁以及撞自己的尾,就属于失败,如无失败则直到通关为止。
屏幕的长度的行向为11单位,纵向为18单位。在这个范围内,玩家通过操作方向键来控制蛇的运动方向。该游戏的最大特色是屏幕自适应,无论各种手机,PDA 的屏幕大小如何,该游戏总是能获得最佳的显示效果。
3.2 详细设计
本游戏的操作流程:用户在启动MIDlet 后,即进入游戏主画面,屏幕开始显示为欢迎画面。用户按下[启动]按钮后,就可以开始玩游戏。当用户想暂停时,再次按一下[启动]按钮开始,游戏就暂停了,在暂停的情况下再按[开始]按钮,游戏继续运行。任何时候按[EXIT]按钮退出,游戏MIDlet 都会终止。
图:游戏流程转换过程
3.2.1 代码设计
(1)游戏地图代码设计
游戏地图是蛇的活动范围和食物随机散落的范围,游戏的容器为行向为11单位,纵向为18单位,如下代码:
private final int iX = 10; //地图的开始坐标
private final int iY = 10; //
private final int SWIDTH = 16; //图标的宽度
private final int iCells = 240; //地图的列数
private final int iRows = 320; //地图的行数
private final int iBoxW = SWIDTH*iCells; //地图的宽
采用二维绘图工具:二维绘图工具drawLine 采用4个参数——直线起点的x 、y 坐标值和直线终点x 、y 的坐标值,例如:
graphics.drawLine (50,0,100,0);
这行代码会从位置(50,0)到(100,0)绘制一条直线。
绘制一个矩形是一个类似的过程,不同的只是需要用起点加上宽度和高度的方式来指定这个对象。可以绘制透明的或者填充的矩形,甚至可以绘制圆角的矩形。4个绘制矩形的方法是:drawRect 、drawRoundedRect 、fillRect 和fillRoundedRect 。
(2)贪吃蛇和食物的代码设计
屏幕的长度为行向为320单位,纵向为240单位;在这个范围内,玩家操作方向键控制蛇的运动方向。该游戏的最大特色是屏幕自适应,无论各种手机,PDA 的屏幕大小如何,该游戏总是能获得最佳的显示效果。
贪吃蛇最初由10个小正方形组成,小正方形是蛇的身体和游戏容器的组成部分。食物也由一块小正方形组成,并且随机散落在游戏框图的区域内,每次只出现唯一的一个,待玩家操作游戏完成一个任务后面,再出现下个食物,小蛇每吃一个食物就增加一个长度。实现代码如下:
public int getCell(){
return iCell;
}
public int getRow(){
return iRow;
}
public void show(){
sLabel.setVisible(true);
}
public boolean isVisible(){
return sLabel.isVisible();
}
public void hide(){
sLabel.setVisible(false);
}
protected void setPosition(int row, int cell){
iCell = cell;
iRow = row;
sLabel.setBounds(cell*SWIDTH, row*SWIDTH, SWIDTH, SWIDTH);
}
public void moveUp(){
if(iRow>0)
setPosition(--iRow,iCell);
else
setTouch();
}
public void moveDown(){
if(iRow
setPosition(++iRow,iCell);
else
setTouch();
}
public void moveLeft(){
if(iCell>0)
setPosition(iRow,--iCell);
else
setTouch();
}
public void moveRight(){
if(iCell
setPosition(iRow,++iCell);
else
setTouch();
}
static boolean getTouch(){
return isTouch;
}
static void setTouch(){
isTouch = true;
}
static void setImTouch(){
isTouch = false;
}
public Snake clone(){
Snake tem = new Snake();
tem.setPosition(this.getRow(),this.getCell());
return tem;
}
(3)操作控制代码设计
MIDP 的游戏设计,本质上就是用一个线程或者定时器产生重绘事件, 用线程和用户输入改变游戏状态。这个游戏也不例外,启动MIDlet 后,就立即生成一个重绘线程,该线程每隔50ms 绘制一次屏幕。当然,重绘时有一些优化措施,并不是屏幕上所有的像素都需要重绘,而是有所选择,比如游戏画布上那些已经固定下来的就不需重绘。游戏画布是一个
CommandListener, 可以接受用户键盘命令,控制蛇的左移,右移,下移,上移。其代码如下: public void move(){
hide();
if(aoSnakes[0].getRow()==Food.getRow()&&aoSnakes[0].getCell()==Food.getCell())
eat();
Snake tempSnakes[] = new Snake[iLen-1];
for(int i=0;i
tempSnakes[i] = aoSnakes[i].clone();
}
switch(heading){
case 37:
aoSnakes[0].moveLeft();
break;
case 38:
aoSnakes[0].moveUp();
break;
case 39:
aoSnakes[0].moveRight();
break;
case 40:
aoSnakes[0].moveDown();
break;
default:
break;
整个游戏的流程控制体现在游戏画布对象的paint()方法里。paint()根据当前的游戏状态,绘制出当时的游戏画面。欢迎画面和Game Over 画面的绘制相当简单,游戏暂停画面的绘制也相当容易,就是设立标志,让paint()执行的时候无需真正执行重绘动作。
第四章 程序的调试与运行
4.1游戏的调试
在手机游戏方面,游戏的控制和图片的设置是游戏的基本能力,为了方便玩家操作游戏,把玩家常用的操作键设为游戏最常用的控制键,并通过J2ME 简单有效的编码,使玩家可以重新设置自己的熟悉的键。本游戏不设立关卡,而是随着游戏积分的增加,游戏将自动增加游戏的难度,如加快游戏速度和随小蛇长度的增加,游戏的操作难度也会增大。如玩家进入游戏(如下图),操作小蛇,
图:游戏界面图
去吃随机散落在游戏区内的食物,每吃一个食物就增加小蛇一个单位长度,并随着长度的增加,游戏的速度将增加,而小蛇的长度增加也会增加玩家的操作难度。
如果玩家操作小蛇碰壁或者由于小蛇长度太长而蛇头撞到小蛇的身体任何部位,均属于失败,玩家得重新游戏开始(如下图)。但游戏将自动纪录你的得分,并且保存你的游戏最高得分。
图:游戏失败的两种情况
4.2 游戏的运行环境
目标硬件环境:
1:显示: 240*320象素;
2:输入:(a)单手键盘或者小键盘;
(b)双手的QWERTV 键盘;
(c)触摸屏。
3)内存 (a)128KB非易失性内存,供MIDP 组件使用;
(b)8KB的非易失性内存,供应用程序生成的永久数据使用;
(c)32KB的易失性内存,供Java 堆使用。
第五章 总结
通过本次J2ME 手机游戏设计,让我更深刻的了解程序设计的基本思想,巩固了J2ME 程序设计的基本知识。本次课程设计所开发的手机游戏,实现了手机游戏的一些基本和必需的功能。如游戏的界面设置,游戏动画的设置,玩家的控制键设置,游戏的保存和游戏纪录的保存,游戏的开始和暂停等功能。但游戏功能还存在着很多不足,首先,对游戏的动画设计不够熟悉,技巧性不强,界面的设置也不够美观。这些对于玩家来说是非常重要,将直接决定本游戏的生存能力。其次,有些功能不能实现。这次游戏的功能没能做到十全十美,是由于自身能力的不足和时间的限制,没有对游戏的开发进行进一步研究,还有很多地方需要改进。
致 谢
通过这次毕业设计开阔了我的视野,增添了自己挑战的信心。在实现的过程当中得到很多人的帮助,在此,要特别感谢我的指导老师耐心细致的指导,使我从中受益匪浅。 参考文献
[1] J2ME实用教程[M].北京:人民邮电出版社.2007.1
[2]J2ME游戏编程[M].北京:清华大学出版社.2005.11
[3]孙淑敏.Java2游戏开发北京[M].北京:清华大学出版社.2005
[4]史斌星.Java 基础编程贯通教材 [M]. 北京:清华大学出版社.2005
[5]杨绍方.Java 程序基础设计 [M]. 北京:科学出版社.2005
附部分代码:
SnakeMIDlet.java 文件
//文件名:SnakeMIDlet.java package snake;
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; private static final int KEY_UP private static final int KEY_DOWN private static final int KEY_LEFT private static final int KEY_RIGHT private static final int KEY_FIRE = 1; = 4; = 5;
= 2; = 3;
public class SnakeMIDlet extends MIDlet {
SnakeCanvas displayable = new public static int s_width = 0; SnakeCanvas();
public static int s_height = 0; public SnakeMIDlet() { public static long updates
= 0; public static Random rand;
Display.getDisplay(this ).setCurrent(displayab
le); private int maxRand = 1000; }
private int map_x = 10; public void startApp() {} private int map_y = 10; public void pauseApp() {} private int map_w = 16; public void destroyApp(boolean private int map_h = 16;
unconditional) {}
}
private int key_x = map_x / 2;
private int key_y = map_y / 2; cGame.java 文件
private int snake_w = 8; ////////////////////////////////////////////////////////////////////////////////
private int snake_h
= 8;
//
private int pos_x = map_x / 2; // cGame.java private int pos_y = map_y / 2; // private int aspect_x= 0; //
private int aspect_y= 1; // Author(s): Xu Qinghui private int snake_max = 50; // Create: 2008-12-1 private int snake_min = 5;
//////////////////////////////////////////////////////////////////////////////// private int snake_n
= snake_min;
package code; private int gameState = STATEPLAY;
private int level = 1;
import java.util.Random; private long sleepTime =300;
import javax.microedition.lcdui.*;
private int[] snake_x = //////////////////////////////////////////////////////////////////////////////// int[ snake_max ];
private int[] snake_y
=
class cGame extends Canvas implements Runnable int[ snake_max ]; { private static final int STATEPLAY = 0; private int[][] map;
private static final int STATELOST = 1;
private boolean isShowInfo = false;
private static final int STATEWIN
= 2;
private Font
font
=
new new
Font.getFont( 100, 200
);
fungus.life = 5;
}
private Thread thread;
Font.FACE_SYSTEM,
new
{
Font.STYLE_BOLD, Font.SIZE_LARGE );
imgItem[i]=Image.createImage("/pics/item_16_"+ }
}
Image temp Graphics gn;
=
i+".png");
=
private Image[] imgGameBg = new Image[4]; private Image[] imgItem private Gif[] private Gif[] private Gif cGame()
setFullScreenMode(true); s_width = getWidth(); s_height= getHeight(); rand = try {
gold dung fungus;
Image[3];
= new Gif[5]; = new Gif[2];
Image.createImage("/pics/bg_tile_0.png");
for( int i=0; i
= null;
temp = null; System.gc();
imgGameBg[i] gn
= =
i++ )
{
Image.createImage(16, 16); new
public void run() {
while( true ) {
try { { }
e.printStackTrace(); updates++; repaint(); serviceRepaints(); thread.sleep(sleepTime);
rePlay( level );
thread = new Thread(this); thread.start();
imgGameBg[i].getGraphics();
gn.drawImage(temp, -i*16, 0,
gn.LEFT|gn.TOP);
Random( System.currentTimeMillis() );
for( int i=0; i
for( int i=0; i
fungus = new Gif("fungus", 12, 25, 23 ); fungus.setFormTime (
new int[] { 2000, 150, 150, 150,
dung[i]= new Gif("dung", 6, 21, 30 ); dung[i].life = -2; // --life
gold[i]= new Gif("gold", 5, 19, 15 );
}catch(Exception e){ e.printStackTrace(); }
150, 200, 100, 200, 150, 200,
// --life +5
}
}catch(Exception e)
fungus.isShow = false;
for( int i=0; i
}
public void paint(Graphics g) {
g.setClip(0, 0, s_width, s_height); g.setColor(0x000000);
for( int i=0; i
for( int j=0; j
{
j*map_h, i*map_w, g.LEFT|g.TOP); } }
for( int i=0; i
for( int i=0; i
if( snake_n>20 ) { }
paintSnake( g );
if( isShowInfo || gameState != STATEPLAY ) {
g.setColor(0xFFFFFF);
fungus.isShow = true; fungus.paint( g ); dung[i].paint(g); gold[i].paint(g);
g.fillRect(0, 0, s_width, s_height);
g.drawLine(0,
i*map_h,
{ } }
g.setColor( 0xff0000 ); g.setFont( font );
g.drawString( "life:"+snake_n, 2, 2, 0 ); g.drawString( "level:"+level, 2, 18, 0 ); }
g.drawImage(imgGameBg[ map[i][j] ],
void paintSnake( Graphics g ) {
g.setColor(0x0000FF); {
snake_x[i] = snake_x[i-1]; snake_y[i] = snake_y[i-1]; }
snake_x[0] += aspect_x*8; snake_y[0] += aspect_y*8; g.setColor(0x6666FF); g.fillRect(snake_x[0]-snake_w/2, snake_y[0]-snake_h/2, snake_w, snake_h); if( { } { if(
)
isIntersect(snake_x[0],
snake_y[0],
snake_w, snake_h,
snake_x[i],
for( int i=snake_min; i
rePlay(level); snake_x[0]
||
snake_x[0]>s_width
||
snake_y[0]s_height )
g.fillRect(snake_x[i]-snake_w/2,
snake_y[i]-snake_h/2, snake_w, snake_h);
for( int i=snake_n; i>0; i-- )
g.setFont( font );
map_y*map_w, i*map_h);
for( int i=0; i
for( int i=0; i
//
g.drawLine(i*map_w, 0, i*map_w,
=== {
snake_y[i], snake_w, snake_h )
} }
rePlay(level);
x2, int y2, int w2, int h2) { if( { } else return false; return true; Math.abs(x2-x1)
(w1+w2)/2
&&
Math.abs(y2-y1)
for( int i=0; i
if( isIntersect(snake_x[0], snake_y[0],
snake_w, snake_h,
gold[i].pos_x,
gold[i].pos_y,
gold[i].w, gold[i].h )
) { addSnake( gold[i].life ); gold[i].setPos();
}
}
for( int i=0; i
if( isIntersect(snake_x[0], snake_y[0],
snake_w, snake_h,
dung[i].pos_x,
dung[i].pos_y, dung[i].w, d ung[i].h )
) { addSnake( dung[i].life ); dung[i].setPos(); } }
if( fungus.isShow && isIntersect(snake_x[0], snake_y[0],
snake_w, snake_h,
fungus.pos_x,
fungus.pos_y,
fungus.w, fungus.h )
) { addSnake( fungus.life ); // fungus.isShow =false; fungus.setPos();
}
}
boolean isIntersect(int x1,int y1, int w1, int h1, int
}
public void keyPressed(int key) {
key = Math.abs(key);
System.out.println("key="+key); switch( key ) { case KEY_NUM2: case KEY_UP: if( gameState != STATEPLAY )
break;
else { if( aspect_y
}
break;
case KEY_NUM8: case KEY_DOWN: if( gameState != STATEPLAY ) break;
else { if( aspect_y >= 0) { aspect_x = 0;
aspect_y = +1; }
}
break;
case KEY_NUM4: case KEY_LEFT: if( gameState != STATEPLAY )
break;
else { if( aspect_x
aspect_x = -1; }
}
break;
case KEY_NUM6: case KEY_RIGHT: if( gameState != STATEPLAY ) break;
else { if( aspect_x >= 0) { aspect_y = 0;
aspect_x = +1; }
}
break;
case KEY_FIRE: case KEY_NUM5: if( gameState == STATEPLAY ) { //
addSnake();
//
System.out.println("snake_n="+snake_n);
}
break;
case KEY_NUM1:
break;
case KEY_NUM3:
isShowInfo = !isShowInfo;
break;
case KEY_NUM0:
rePlay( level );
break;
}
this.repaint(); }
private void addSnake(int life) {
int s_n = snake_n; snake_n += life; if( snake_n >= snake_max ) { level++; rePlay(level);
}
else if( snake_n
{
rePlay(level); }
else if( life>0 ) { for( int i=s_n; i
}
}
public void rePlay( int level ) {
sleepTime = 300-level*20; map_x = s_height/16; map_y = s_width/16; key_x = map_x>>1; key_y
= map_y>>1;
gameState = STATEPLAY; map = new int[map_x][map_y]; isShowInfo = false;
snake_n
= snake_min;
aspect_x = 0; aspect_y = 0; try {
for( int i=0; i
for( int j=0; j
int r = rand.nextInt(maxRand); for( { }
if( r >k+1 ) { }
map[i][j] = k; int
k=0;
Image temp Graphics gn;
=
} }
Image.createImage("/pics/bg_tile_"+(level%2)+".png");
for( int i=0; i
= null;
temp = null; System.gc();
imgGameBg[i] gn
= =
i++ )
Image.createImage(16, 16); imgGameBg[i].getGraphics();
gn.drawImage(temp, -i*16, 0,
gn.LEFT|gn.TOP);
}catch(Exception e){ e.printStackTrace(); }
k
for( int i=0; i
snake_x[i] = s_width >>1;
snake_y[i] = s_height>>1;//-(i*snake_h);