2005年毕业论文----J2ME手机游戏开发高计

时间:2021-10-13 14:13:19
一  前  言
1.1 摘要
J2ME(Java 2 Micro Edition) 是近年来随着各种不同设备,尤其是移动通信设备的飞速发展而诞生的一项新的开发技术。它定位在消费性电子产品的应用上,对设备的智能化、多样化,提供了革命性的解决方案,并因其“Write Once, run anywhere”的Java特性而提高开发的效率。
随着手机的日益普及、Java功能在移动设备上的实现,Java应用程序产生的手机增值服务逐渐体现出其影响力,对丰富人们的生活内容、提供快捷的资讯起着不可忽视的作用。本论文着眼于J2ME技术的应用,开发一款可商用的手机游戏程序——扑克豪斯。本程序的界面和运作方式于台式游戏豪斯,将老少皆宜的经典作品移植到手机上来,为更流行的硬件平台提供应用软件。
本论文介绍了手机的软硬件特性、J2ME的相关技术及本程序的结构分析和具体功能的实现。
关键字:J2ME,手机游戏, Java,扑克豪斯

ABSTRACT
J2ME is a kind of fast developing technology implemented on various devices especially mobile communication equipments. It focuses on application for consumptive electronic products, providing revolutionary solution to the intelligentization and diversification of the equipment. It improve the efficiency of the development process thanks to its “Write Once, run anywhere” nature.
When cell phone is getting ever more popular nowadays, with the implementation of Java technology on mobile equipment, increment of capital on communication service exhibits its force on people’s everyday life, providing them ever fast information just in hand. This dissertation focuses on implementation of J2ME technology and has developed a commercial game suite run on mobile phones—PokerMachine. This application inherits many characters of the old fashioned game Battle City which developed by Nintendo in 1980s. It transports the classical product to mobile phones, offering corresponding software for such more popular hardware platform.
This dissertation introduces the characteristic of Nintendo FC machine and its software, J2ME and its relative technology as well as this program’s structure and analysis of implementation in details. 
Key words: J2ME, mobile game, Java, tank, Battle City.


1.2引  言
手机游戏成绩斐然,成为IT产业中增长最快的部分之一.纵观IT产业的历史,计算机游戏已经成为技术创新背后的动者渴望更加强大的硬件计算能力;渴望不受不同的软件的限制??无论是将图形强制在人工智能(AI)上 还是网络安全性。游戏开发者和玩家常常是前沿计算机技术的最早的采用者。由于他们的创新天性,游戏不再是由大型采购公司控制的技术产品??游戏领域总是有充足的空间给那此
在这方面有天分的单个创新者。随着技术对游戏经验的促进,游戏市场持续增长。商业IT革命为我们节省了时间力之一。计算机游戏和金钱,从而提高了我们的工作水平,我们因此花费更多的时间和金钱在游戏上,来改善生活的整体质量。甚至在当今困难的经济时代,游戏仍保持快速增长,他仍然是盈利行业的组成部分。Java开发者应该注意到这里蕴藏着的巨大的商机。


1.3手机游戏特征 

     手机游戏的盈利主要是由于它们的涉及面很广。手机已经与现代生活方式紧紧地结合在一起。他们是最普遍携带的个人用品中仅次于钥匙和钱包的东西。传统的台式机游戏将目标锁定在低级趣味的人和青少年身上,而手机游戏则每个人都可以访问的到??随时,随地。尽管每个手机游戏都不贵,但是巨大的使用量(如:每人每星期一个新游戏)将使得这个市场商机无限并且有利可图。但是,对于开发者来说,将控制台游戏迁移到手机游戏工程很大因为他们所面向的对象、生活方式和分布式模型都有着极大的区别。让我们从手机游戏的需求谈起吧。 
 
  作者: 222.248.37.*  2005-6-21 13:48   回复此发言  
 
2 2005年毕业论文----J2ME手机游戏开发高计(原创)
 
一个成功的手机游戏大多具有以下特征:
1:易于学习: 既然手机游戏面向的是普通消费者而不是计算机高手,那么他们不可能深入的学习游戏技巧。消费者不会花几个小时去研究一个3美元的手动操作的游戏。所以游戏必须是一下载就可以玩的。保持游戏的简单是最基本的要求。 
2:可中断性: 多任务处理是手机生活方式的基本特征。手机用户常常在任务(如等一个电子邮件或者等车)之间有一小段时间。而游戏、日历管理、通讯和工作数据访问使用的是同一个设备。所以一个好的手机游戏应该提供短时间的娱乐功能,并且允许用户在游戏和工作模式之间顺利切换。
3:基于订阅:手机游戏的盈利成功取决于他们巨大的使用量。一开始开发和设计每个游戏都是昂贵的。如果一个手机游戏开发者要赚钱的话,重要的是:同一个游戏引擎,多个标题,基本的故事情节类似。基于订阅的游戏是不断产生收入的最好方法。
4:丰富的社会交互: 不管一个游戏设计得多好,只要玩家找到了它的根本模式或者玩完了所有的游戏路径很快就会厌烦这个游戏。对于一个基于订阅的游戏,重要的是与别的玩家合作以增强所玩游戏的智力和随机性。在今天纷繁复杂的多玩家游戏中具有丰富社会交互的游戏证明是成功的。 
5:利用手机技术的优点: 巨额的手机技术研发费用都花在提高设备和网络的可用性和可靠性上面。因此,手机设备硬件和网络协议与桌面/控制台世界(如全球定位系统(GPS)扩展、条形码扫描仪、和短消息服务(SMS)/多媒体信息服务(MMS)通讯)有着非常大的差别。好的手机游戏应该利用那些更新的设备特征和网络基础设备的优点。 
6:无违法内容:既然所有年龄/性别的人群都玩手机游戏并且常常在公共/工作场合,你应该避免明显的暴力或者色情内容。 
1.4手机游戏的类型 
基于以上标准,最有可能成功的手机游戏就是那些临时小游戏,它可为广泛的玩家在所有的社交场合提供娱乐。特别是,下列类型的游戏将是明天最具有杀伤力的游戏: 
1:多玩家游戏: 这些游戏可以提供手机访问以便建立在线游戏团体。或者,他们利用手机专有特征如多媒体通讯和基于位置的服务,从而创建完全不同的社会结构。好的游戏设计和管理是这种类型游戏成功的关键。 
2:基于内容的游戏:这些游戏传送版权的多媒体内容,如名人的图片、视频片断、音频立体声、和私人的体育节目,给那些音乐/电影/体育迷们。它成功的关键就是提供值得订阅的高品质的内容流。 
3:高冲击的视频游戏:PC 和台式游戏已经从2D 进化到3D。手机游戏也会遵循这种趋势。现在已经出现手机3D工具箱和听筒。但是,3D是非常资源集中型的。他们要求开发者和图形设计者都是专家级的。专业的商店最有可能开发这些游戏。 
在手机游戏的早期,大多数开发者都是想要玩转设备、学习技巧和快速攻关的*分子。但是,随着更多的开发者加入这个行业,手机游戏已经进化成涉及到专业设计师、构造师、开发者、产品经销商、律师、和艺术家在内的主导产品。这是行业走向成熟的必然趋势。尽管在某人的业余时间内要开发和推销简单的游戏更加困难,但是更多的专业商店将为许多人提供更多的就业机会。
1.5 手机软件现状
在信息社会中,手机及其他无线设备越来越多的走进普通百姓的工作和生活,随着信息网络化的不断进展,手机及其他无线设备上网络势在必行。但是传统手机存在以下弊端:
1.  传统手机出厂时均由硬件厂商固化程序,程序不能增加、删除,有了错误也不能更新、修改,若要增加新功能必须另换一部手机。
2.  传统手机访问互联网是通过WAP(Wireless Application Protocal),所有网络资源必须接通网络才能在线访问,非常耗时、费用亦很高。
而Java技术在无线应用方面的优势非常明显:
1.  应用程序可按需下载,而不是购买由硬件商提供的套件,可升级空间大。
2.  Java技术提供了一个类库,它使的应用开发商可以创建更为直觉、丰富的用户界面(GUI);

3.  Java技术使网络带宽的应用更为有效,因为应用程序可以下载到器件上,并在本地运行,仅仅是在连接到服务器时才会占用网络带宽。
基于以上分析,Java手机将是未来手机的发展方向,是业界的热点。

1.6 本游戏的未来
看过周润发主演的《赌神》都知道里的豪斯是很好玩的,也很有发展前途。一,这个游戏对手机的要求不高;二,这个游戏简单;三,当这个游戏做成网络游戏时,可以是各个玩家斗智的平台;要实现这点技术要求并不难。在本设计的基础上再加上某些技术就可以了:a) 网络的连接 b) 能读能存网络数据库 c) 牌不再是用随机产生这么简,它应该根据玩家的积分来产生,这样会更好。
1.7 手机未来的发展方向
手机游戏的盈利主要是由于它们的涉及面很广。手机已经与现代生活方式紧紧地结合在一起。他们是最普遍携带的个人用品中仅次于钥匙和钱包的东西。传统的台式机游戏将目标锁定在低级趣味的人和青少年身上,而手机游戏则每个人都可以访问的到——随时,随地。尽管每个手机游戏都不贵,但是巨大的使用量(如:每人每星期一个新游戏)将使得这个市场商机无限。只要有好的游戏,就会有更广的使用量。
1.8   游戏业务及J2ME概述
虽然 Java 已经被用到许多企业级软体上,可是其实骨子里面还是非常适合用在嵌入式系统之中。Java平台演进到Java2后,Java平台分别针对不同领域的需求被分成四个版本,亦即J2EE、J2SE、J2ME以及JavaCard。其中J2ME定位在消费性电子产品的应用上。这个版本针对资源有限的电子消费产品的需求精简核心类库,并提供了模块化的架构让不同类型产品能够随时增加支持的能力。这个版本的应用层面相当广泛,会是未来Java平台发展的重点项目。J2ME 在设计其规格的时候,遵循着“对各种不同的装置而造出一个单一的开发系统是没有意义的事”这个基本原则。于是 J2ME 先将所有的嵌入式装置大体上区分为两种:一种是运算功能有限、电力供应也有限的嵌入式装置(比方说PDA 、手机);另外一种则是运算能力相对较佳、在电力供应上相对比较充足的嵌入式装置 (比方说冷气机、电冰箱、电视机上盒 (set-top box))。因为这两种型态的嵌入式装置,所以Java 引入了一个叫做Configuration 的概念,把上述运算功能有限、电力有限的嵌入式装置定义在Connected Limited Device Configuration(CLDC)规格之中;而另外一种装置则规范为 Connected Device Configuration(CDC)规格。也就是说, J2ME 先把所有的嵌入式装置利用Configuration 的概念区隔成两种抽象的型态。J2ME平台被认为是最杰出的手机游戏平台,它为开发者、设备制造商、网络通信公司和消费者广泛接受。它有一些非常重要的特征对所有组织都有益。因为J2ME应用在不同设备上都是便携式的,他们常常可在网络上下载和执行。如果没有正确的防范,它则为用户和无线通信公司冒着无数个安全的风险。幸运的是,Java被设计成一种安全的语言。所有字节码应用在执行之前都要校验;JVM在执行过程中监督应用的安全性和存储违反问题。MIDP v2 运行时间包括一个完全特征化的、基于域的安全管理员,它在应用的数字签名者鉴别的基础上赋予应用API级许可。
纵观IT产业的历史,就像军事的发展时常推动计算机的演化一样,计算机游戏已经成为技术创新背后的动力之一。计算机游戏者渴望更加强大的硬件计算能力;渴望不受不同的软件的限制——无论是将图形强制在人工智能(AI)上 还是网络安全性。游戏开发者和玩家常常是前沿计算机技术的最早的采用者。由于他们的创新天性,游戏不再是由大型采购公司控制的技术产品——游戏领域总是有充足的空间给那些在这方面有天分的单个创新者。
手机游戏的盈利主要是由于它们的涉及面很广。手机已经与现代生活方式紧紧地结合在一起。他们是最普遍携带的个人用品中仅次于钥匙和钱包的东西。传统的台式机游戏将目标锁定在低级趣味的人和青少年身上,而手机游戏则每个人都可以访问的到——随时,随地。尽管每个手机游戏都不贵,但是巨大的使用量(如:每人每星期一个新游戏)将使得这个市场商机无限。但是,对于开发者来说,将控制台游戏迁移到手机游戏工程很大。因为他们所面向的对象、生活方式和分布式模型都有着极大的区别。

二  开发环境及相关技术的介绍


2.1 Java语言的特点
1. 平台无关性
Java引进虚拟机原理,并运行于虚拟机,实现不同平台之间的Java接口。使
用Java编写的程序能在世界范围内共享。Java的数据类型与机器无关。
2. 安全性
Java的编程类似C++,但舍弃了C++的指针对存储器地址的直接操作,程序运
行时,内存由操作系统分配,这样可以避免病毒通过指针入侵系统。它提供
了安全管理器,防止程序的非法访问。
3. 面向对象
Java吸收了C++面向对象的概念,将数据封装于类中,实现了程序的简洁性和
便于维护性,使程序代码可以只需一次编译就可反复利用。
4. 分布式
Java建立在TCP/IP网络平台上,提供了用HTTP和FTP协议传送和接收信息
的库函数,使用其相关技术可以十分方便的构建分布式应用系统。
5. 健壮性
Java致力与检查程序在编译和运行时的错误,并自动回收内存,减少了内存出
错的可能性。Java取消了C语言的结构、指针、#define语句、多重继承、goto
语句、操作符、重载等不易被掌握的特性,提供垃圾收集器自动回收不用的内存空间。

2.3  J2ME 是什么?
据J2ME应用程序调用的API,J2ME应用程序分为标准应用程序和不易移植的应用程序两种。
据J2ME应用程序调用的API,J2ME应用程序分为标准应用程序和不易移植的应用程序两种。
术语:
CDC:连接设备配置
CLDC:连接限制设备配置
JRE:Java运行环境
MIDP:Mobile Information Device Profile规范
OTA:Over The Air
非OTA:数据线、蓝牙(Bluetooth)、红外线(IrDA)、多媒体信息服务(MMS)、Email附件
2.3  J2ME智能客户端 

J2ME平台被认为是最杰出的手机游戏平台,它为开发者、设备制造商、网络通信公司和消费者广泛接受。它有一些非常重要的特征对所有组织都有益


2.4 J2ME 总体架构
J2ME 使用配置和简表定制 Java 运行时环境 (JRE)。作为一个完整的 JRE,J2ME 由配置和简表组成,配置决定了使用的 JVM,而简表通过添加特定于域的类来定义应用程序。
配置将基本运行时环境定义为一组核心类和一个运行在特定类型设备上的特定 JVM。我们将在 J2ME 配置一章中详细讨论配置。
简表定义应用程序;特别地,它向 J2ME 配置中添加特定于域的类,定义设备的某种作用。我们将在 J2ME 简表一章中深入介绍简表。
下面的图表描述了不同的虚拟机、配置和简表之间的关系。它同时把 J2SE API 和它的 Java 虚拟机进行了比较。虽然 J2SE 虚拟机通常被称为一种

 
JVM,但是 J2ME 虚拟机、KVM 和 CVM 都是 JVM 的子集。KVM 和 CVM 均可被看作是一种 Java 虚拟机 -- 它们是J2SE JVM 的压缩版,并特定于 J2ME。
移动通信的时代即将来临,通信相关待业变得前景可期,而除了移动通信的主要通信工具——手机,其功能越来越强大之外,有更多的厂商相继投入移动通信设备的生产与开发,其平台和操作系统的复杂度比目前混乱的PDA有过之而不及,除了Symbian 针对手机推出操作系统 Symbian OS(过去称做EPOC,)操作系统之外,微软也针对手机推出操作系统 Smartphone 2002 (Stinger),但是日前市面上的手机仍以使用厂商自行开发的操作系统者居多。
平台的复杂纷乱对一般用户当然没什么影响,但是对于程序开发人员来说,这么多不同的程序发展平台,光看完头就昏了。如果每个平台都有自己的程序写法以及程序库,那么光是看上面这些平台至少就要学习五种以上程序的写法。当然,只专精一种平台当然是很好的事情。可是程序员不禁要说:“如果我们写出来的软件可以在不经过修改源代码的情况下就能够在这些平台上执行,那不是更完美吗?“对程序开发人员来说,这样的投资报酬率当然是最大的。
要在那么多平台上开发程序,对程序员来说的确是很大的挑战,如果要把所有的时间和精力放在软件的可用性上,那么相对而言很多时候我们根本没有那么多时间撰写各种平台的程序。要解决这个问题,一般来说程序员会选用一个可以跨平台的Framework来达成至少source code level的跨平台。利用Java的“Write once,run anywhere”特性,我们可以真正达到程序只要写一次,拿到任何平台上都可以执行。

利用Java撰写手机上的程序当然有其缺点,最广为人知的可能就是执行效率的问题,Java在执行速度这个议题上一直让人诟病。不过笔者认为,随着技术的发达,将会有更快更省电的嵌入系统专用CPU出现,Java One中也传出将针对J2ME开发专用芯片,让Java在 手机上的程序可以跑得更快,因此效率上的总是其实是可以忽略的。更何况,当Sun在设计J2ME的时候,也用了很多方式企图加快Java 在手机上的执行速度。
因此,目前的移动通信设备大厂几乎采用J2ME平台。搭建J2ME 环境:
2.5  简单环境的搭建:

1、  下载JDK和J2ME无线工具包(www.sun.com.cn
安装SUN JDK:j2sdk-1_4_0_03-windows=i386
安装SUN J2ME SDK:j2me_wireless_toolkit-1_0_3-win

2     关于Wireless Tool Kit
WTK(Wireless Tool Kit)是Sun公司针对J2ME推出的用于手机和Palm等
移动设备的开发包,是除手机厂商的专用开发包外唯一的手机模拟器开发包。
它通用性高,开发出的应用程序可保证能运行在大部分设备上,而不像专用
厂商具有一定的不兼容性。虽然它没有强大的功能和完善的调试手段,但它
提供运行模拟器的最基本组件,是其他IDE需集成采用的必备元素。

3, 用于手机游戏开发的环境不是只建立一个 java sdk 就可以了,它还需要了一个一个库类,叫 java toolkit。首先安装 java sdk 再在原来的目录下安装java toolkit这个新的环境就可以满足我们的需要了。然后我们编写一个最简单的程序用来验证这个环境是否可以用来编写我们的手机程序。

编写以下代码:
保存成 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 MIDlet", "Hello MIDP!", 256, 0);
        display.setCurrent(t);
    }
 public void pauseApp() {
    }
public void destroyApp(boolean unconditional) {
    }
}
启动Ktoolbar 新建一个工程,取名 Hello 。把以上文件拷到 Hello 文件的目录编译。能通过编译并能运行,说明这个开发环境就能满足我们的需要了。
2.6 MIDP应用程序:

MIDP应用程序是指遵循由应用程序主体(Jar文件)和应用程序描述文件(Jad文件)组成。按部署方式可分OTA和非OTA两种。
MIDlet:是Jave一个类,扩展了javax.microediton.midlet.MIDlet抽象类。MIDlet生命周期分3种状态:运行状态,暂停状态,销毁状态等。
2.6 Java Appication Manager
手机中负责调配程序运行资源的管理后台是Java Application Manager。它所使用的传输媒体可以是红外线、网络、以及其他可用来传输的媒体。Java Application Manager 会从网络上下载代表该Application Suite 的JAR 档,接着在手机上安装此MIDlet Suite,
然后在手机开始执行该应用程序。整个详细的运作流程如图2-1所示。
 
2.7 预先审核:
审核动作一般在PC上执行,速度可以接受。为了是对安全的考虑,任何来自本地或者远程机器,都必须经过Byte Code审核器的验证,以防止程序在传输途中遭到恶意更改。如图
 
2.8 J2ME、J2SE 与 J2EE 之间的比较:
下面的图表描述了支持 J2ME 应用程序的设备,同时说明了 J2ME 适合 Java 平台之处:
 
三   J2ME 手机游戏需求分析
3.1  本程序需解决的有关技术问题
1.  游戏程序是一项精度要求很高的程序系统,因为其代码利用率很高。一个实时运行的最终作品,每秒都会运行成千上万行程序,绘图事件、键盘事件都会以极高的频率在后台等待响应,若有丝毫的差别都将很容易导致程序在运行不久后可能出现严重错误,甚至死循环。因此,其逻辑设计应当相当严谨,需将所有可能发生的事件及意外情况考虑在设计中。

2.  游戏中为了美观,适用性强,可能需要采用外部文件引入的图片贴图,有关贴图,在MIDP2.0中提供了用于增强游戏功能的game包,使得解决静态或动态、画面背景、屏幕刷新的双缓冲等都有较好的解决方案。
3.  一开始就把牌全部分配好,这样就可以简单而有效地避免了牌的重复,也可以大大地简化了程序代码的编写。
4.  牌的转化可以用一个函数来实现,这样就可以使代码更模块化,利用也就可以更高。
5.牌慢慢移动的实现。牌的移动是用线程来实现的,用Thread.sleep()
这个函数来作时间延迟,让坐标变量缓慢地变化。然后通过刷新画图,就会使人觉得牌是在缓慢地移动。
6.是否是要牌还是放弃(认输),它通过向右键(要牌)和向左键(放弃)来操作的。当点击向右键时,发生点击事伯,变量发生相应的变化,然后触发画图事件。
7.当玩家都跟到5张牌时,就要判断牌的大小了,它通过多次循环,来确定其大小。
8.  Java是基于虚拟机的半解释型编译系统,其执行效率较C++等完全编译后的程序会低很多,程序如果不进行精简和优化,将可能导致运行的不流畅。除开发过程中对结构上的控制、变量的使用、算法的优化等优化外,还可以使用混淆器(Obfuscator)进行程序打包后的优化。
以上相关技术细节和整体流程将分别在以下小节阐述。
3.2  程序流程图
MIDlet suite是MIDP应用程序的最小单位,JAM负责将手机内的MIDlet suite以图形
v 化的方式呈现,让用户能够选取欲执行的MIDlet suite,一旦选取了某个MIDlet suite,操作系统就会激活KVM执行里面的MIDlet。MIDlet及相关的支持类组成了MIDP应用程序的实际内容。每个MIDlet都必须继承javax.microedition.midlet.MIDlet这个抽象类。在MIDP规格中定义了MIDlet的生命周期,以及可以存在的三种状态,包括Paused、Active以及Destroyed,每一个MIDlet在任何时刻只可能处于其中的一个状态这
v 三种状态的转换关系如图所示:
 
本程序采用面向对象的设计模式,对游戏中的所有物体赋予对象的概念和属性。运行程序后允许用户选择执行选项菜单,在开始游戏后将先从外部文件载入牌的图文件,对背景的所有物体进行绘图。在主程序运行的线程中,画面刷新将以一定的频率采用双缓冲技术对屏幕重绘,实时反映整个游戏的进行状态。用户控制的牌运行在主线程中,随屏幕刷新的频率而移动。牌将在游戏开始时逐渐新增线程,每增加一个牌对象就新增加一条线程,一旦线程数满到最大值(本程序暂设置最大值为几千多个),就不允许线程的再增加。这个线程问题就是本设计的一个 bug。
本程序主要的流程如图3—2所示:
 
流程简略解释:
程序一开始执行是先从主类 test.java 来开始。这个类从 MIDlet 类继承过
来,在程序的开始函数 startApp() 中我用
Navigator.current = Navigator.MAIN_SCREEN; 这个等式把程序的执行转到
MainScreen.java 文件(程序的画面)来。而这个类继承 List ,这个 List 类可以更方便地控制程序的选择。在这个文件中列出一个菜单供用户去选择,如下:

开始
设定
版权声明
离开

当点击开始时,程序转到 GameScreen 这个类去。GameScreen 这个类继承 Canvas,Canvas 专门是用来控制游戏的。里面包括屏幕的绘图,一些变量的初始化。
当点击设定时,程序转到SetScreen 这个类。这个类用于登录帮助网站的,当然这个是需要网络支持的。
当点击版权声明时,程序转到 CopyScreen 这个类,这个类继承 Alert,它用于描述版权的一些信息。
当点击离开时,程序退出 test 类。

3.3 绘图与MIDP2.0新增的GameCanvas包

3.3.1  提供低级绘制的Canvas类
为了能有程序开发人员控制接口的外观和行为,需要使用大量的初级用户接口类,尤其在游戏程序中,几乎完全依赖的就是Canvas抽象类进行绘图。从程序开发的观点看,Canvas类可与高级Screen类交互,程序可在需要时在Canvas中掺入高级类的组件。Canvas提供了键盘事件、指点杆事件(如果设备支持),并定义了允许将键盘按键映射为游戏控制键的函数。键盘事件由键代码指定,但这样控制游戏会导致缺乏通用性,并不是每个设备的键盘布局都适合游戏的操作。应当将键代码转换为游戏键的代码,以便硬件开发商能定义他们自己的游戏键布局。本程序中,操纵用户坦克运行的按键都定义为游戏控制键,这样便能适应所有的机器。

3.3.2 Graphics类
Graphics类提供了简单的2D绘图功能。它具有24位深度色彩的绘制能力,以三原色分别各占一个字节表示其颜色。程序只能在paint()函数中使用Graphics绘制,GameCanvas可调用getGraphics()函数直接绘制在缓冲区上,可以在任何时间请求传输到前台。其对象会被传给Canvas的paint()函数,
以便最终显示。
3.3.3 PNG格式
PNG(Portable Network Graphics)格式是MIDlet唯一支持的图象格式,PNG具体格式由PNG Specification,Version 1.0定义的。PNG格式提供透明背景的图象,这对绘制游戏画面和被操纵主角极有帮助。坦克之间或与障碍物碰撞时就不会因为背景有特定的颜色,显示出的效果像贴上的图片而缺乏真实感,物体之间轻微重叠时最上层图片也不会覆盖超过其有效象素外的部分。
PNG格式图片中包含许多定义其图片特性的冗余部分(Chunks)。这些代码包含在每一个单独的png格式图象中,然而如果将多个png图象合并在一张幅面稍大一些的整图中,多个chunks就可以得到精简,图片的大小可以得到控制。使用Image
类中的createImage函数可从整图中分割出所需要的元素。在Game包中的TiledLayer和Sprite类都整合了这样的
功能。本程序中的地图元素都集成在一张tile.png图片中,实现了方便的管理和程序体积的精简。

3.3.4 有关绘图的一些技术
在没有MIDP2.0前,进行游戏绘图一般需要手动编程使用双缓冲。需要在paint()方法内所想要画的图形画在一张预先准备好的背景,等所有绘图操作都完成后再将背景的数据拷贝到实际的屏幕上。Image类提供了一个建立背景的静态方法createImage(int width, int height),再利用getGraphics()方法取得属于这个背景的Graphics对象,所进行的绘图操作都会作用在背景上,等到全部的绘图操作完成后,再调用drawImage()方法将背景的数据复制到实际显示的屏幕上。
这样的技术在绘制动画时特别有用。绘制动画时经常需要不断地更新画面,而更新画面的操作就是先将屏幕以fillRect()的方式清除,再将下一张图片画在屏幕上,然而反复的清除及重绘会造成屏幕的闪烁现象(flicker),因此使用双重缓冲的好处就是在背景进行这个清除及重绘的操作,再将完成的绘图拷贝到屏幕上,由于用户看不到清除的操作,因此就不会出现闪烁的现象了。不过在某些MIDP的实现上已经加上了双重缓冲的支持,因此在处理前应先利用Canvas类的isDoubleBuffer()方法来判断。

3.7  内存使用的最佳化
通常在MIDP应用程序的手机执行环境中,所牵涉的内存有下列三种:
*应用程序存储内存
*RecordStore存储内存
*执行时期内存(Java Heap)
其中前两种是持久性的内存,关闭电源后还能保持数据的正确性,通常这两种内存所能存储的容量是合并计算的,这个上限对每种手机都不一样,大部分在一两百KB内。在这样的情况下需要在不影响原有功能的情况下适当的缩减JAR文件的大小,除了可以克服内存空间的限制外,也能大幅度缩短下载的时间(费用也降低了),势必会有更多的人愿意下载所开发的程序。其方法有:
第一,就是尽量缩短命名的长度。在应用程序内,对于所建立的类、接口、方法及变量名而言,都需要赋予一个识别的名称,所命名的名称每多一个字符就会在类文件内多产生一个字节,对于一个较复杂的应用程序而言就会增加为数不小的数据量。所有这些可以借助混淆器来帮助实现。
第二是减少复杂的程序结构,为一些共同的行为建立一个抽象类(Abstract Class) 来表示继承的子类的共通性。
第三是减少图形数据的大小。将PNG格式的小分辨率图象合并在一张大的高分辨率图象中,由于减少了chunks,将比合并前的总大小减少许多。
3.6  混淆器(Obfuscator)的使用
Java 语言并没有完全编译成二进制可执行文件,编译出的.class文件是一种介于源程序和二进制之间的一中基于半解释的字节码,需要虚拟机来执行。它包括了所有的信息。然而这样会导致.class很容易被反编译为源代码,从而不能保护作者的知识成果。目前流行的如decode,JAD等反编译工具可以以很快的速度生成源文件。如果不加以施行有效的措施,将造成严重的后果。

由此引入混淆器的概念。混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,如果缺乏相应的函数名指示和程序注释,即使被反编译,也将难以阅读。
    混淆器的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。由于以上介绍的减少变量、函数的命名长度的关系,编译后也会从.class文件中减少这些冗余的信息。混淆后,体积大约能减少25%,这对当前费用较贵的无线网络传输是有一定意义的。
为了能与各种IDE集成,就像Java2 SDK一样,混淆器采用命令行参数的形式,以便可被其调用。目前流行的Obfuscator有RetroGuard等。



3.7  模拟器的相关调试
IDE整合的Wireless Tool Kit提供了许多在运行时监视运行状态的工具。 包括内存状况的检测(手机上的内存空间十分有限,必须时刻关注机载内存是否大于程序所能使用到的最大可能的内存空间),网络状况的检测,运行函数的跟踪等。如图3-4,是内存跟踪测试随时间变化的调试器。其中,允许强制垃圾回收(Garbage Collection)。由于Java语言中,不像许多其他的如C++语言,不需要指定回收函数中特定不使用的资源,资源回收机制将自动清空无效变量占用的空间。在程序运行中也可以调用System类的gc()函数手动收回废弃的内存。

四   程序分析和编写手机代码

4.1  游戏进入前的选择
每个MIDlet程序都必须有一个主类,该类必须继承自MIDlet。它控制着整个程序的运行,并且可以通过相应函数从程序描述文件中获取相关的信息。该类中拥有可以管理程序的创建、开始、暂停(手机中很可能有正在运行程序却突然来电的情况,这时应进入暂停状态。)、结束的函数。
本设计程序进入时,首先载入画面的不是游戏运行状态,而是提供选项,当选择 开始 时正式运行。因此,在 MainScreen的构造函数中分配了List类,即选项画面的内存空间。在startApp()函数中,随即调用了Displable的setCurrent()函数将当前屏幕设置为Navigator.MAIN_SCREEN。在 版权声明 界面前,建造了一个Alert类。Alert对象用于显示提示、警告,还有版权信息等告之用户信息的临时闪现的屏幕,它可作为setCurrent的参数,提前显示在最终需要显示的屏幕前。当程序转到 GameScreen 时,画面将永久显示游戏本身,直到用户点击相应按钮手动结束。但是当显示的画面元素超过一屏大小时,将自动转换为永久状态。
GameScreen 继承了接口commandListener,这样,就可以使用高级界面的Command按钮。继承了commandListener的类必须拥有commandAction(),以决定对按键采取什么样的行为。即按钮事件触发后需执行的函数。在设置好commandlistener后,需要调用setCommandListener()以将按钮事件激活。键盘事件中,可用getCommandType()返回的Command类型来确定选择的是什么按钮。MainScreen继承了List类,用于显示列表选项,使用其append()函数可将选项加入到列表中。getSelectIndex()可检测到选择的项目的序号,序号从0开始递增。其中,当选择第一项时将载入正式游戏画面GameScreen类,第二项将显示转到设定画面,第三项用来显示用户信息,版权声明等。第四项是离开游戏选项。

4.2  主游戏逻辑及其涉及到的若干类
4.2.1 程序的菜单选项
这个写在 MainScreen.java 文件中,这个类继承 List 。同时也继承了接口commandListener,这样,就可以使用高级界面的Command按钮。继承了commandListener的类必须拥有commandAction(),以决定对按键采取什么样的行为。即按钮事件触发后需执行的函数。在设置好commandlistener后,需要调用setCommandListener()以将按钮事件激活。用append 方法来加入菜单:
append("开始",null);
append("设定",null);
append("版权声明",null);
append("离开",null);
setCommandListener(this);
再用 getString(int) 函数来获得菜单的选项。
MainScreen.java 文件:

import javax.microedition.lcdui.*;
public class MainScreen extends List implements CommandListener
{
private static Displayable instance;
synchronized public static Displayable getInstance()
{
if(instance == null)
instance = new MainScreen();
return instance;
}
private MainScreen()
{
super("进入Game",IMPLICIT);
/// 菜单选择
append("开始",null);
append("设定",null);
append("版权声明",null);
append("离开",null);
// 事件侦听
setCommandListener(this);
}
public void commandAction(Command c,Displayable s)
{
// 事件侦听
String cmd= getString(getSelectedIndex());
// 把得到的值传给程序控制器,用程序控制器来决定指引程序的执行。
Navigator.flow(cmd);
}
}

4.2.2 游戏的设定
这个写在 SetScreen.java 文件中,而这个类继承 Form 类,同时也继承了接口commandListener,这样,就可以使用高级界面的TextField按钮。继承了commandListener的类必须拥有commandAction(),以决定对 TextField 值获取采等行为。在设置好commandlistener后,需要调用setCommandListener()函数来把值传入程序中。
SetScreen.java 文件:
import javax.microedition.lcdui.*;
public class SetScreen extends Form

//继承 CommandListener 接口
implements CommandListener
{
private static Displayable instance;
synchronized public static Displayable getInstance()
{
if(instance == null)
instance = new SetScreen();
return instance;
}
// 定义 TextField (文本输入框)
TextField url;
Gauge volume;
private SetScreen()
{
super("设定");
url = new TextField("请输入服务器位置","socket://127.0.0.1:99",40,TextField.URL);
append(url);
volume = new Gauge("音量",true,10,3);
// 软键菜单
addCommand(new Command("辅助说明",Command.HELP,1));
addCommand(new Command("返回",Command.BACK,1));
// 事件侦听
setCommandListener(this);
}
public void commandAction(Command c,Displayable s)
{
Navigator.flow(c.getLabel());
}
}

4.2.3 游戏中的帮助和程序控制器
首先调用 HelpScreen 类,然后赋一个值:辅助说明给 Navigator 类,当Navigator接收到这个值时,就显示帮助字符串。
HelpScreen.java
import javax.microedition.lcdui.*;
public class HelpScreen extends Form implements CommandListener
{
public HelpScreen(String c)
{
super("辅助说明");
append©;
addCommand(new Command("返回",Command.BACK,1));
setCommandListener(this);
}
public void commandAction(Command c,Displayable s)
{
Navigator.flow(c.getLabel());
}
}
这个写在Navigator.java 里,它接收 SetScreen.,MainScreen等类传过来的值。然后根据它触发的按钮事件,来调用相对应的事件。
//Navigator.java
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import java.util.*;
public class Navigator
{
final public static int MAIN_SCREEN = 1;
final public static int GAME_SCREEN = 2;
final public static int SET_SCREEN  = 3;
final public static int GAME_HELP_SCREEN = 4;
final public static int SET_HELP_SCREEN = 5;
public static MIDlet midlet;
public static Display display;
public static int current;
// 自定义一个类
// 重载函数
public static void show(Object obj)
{
switch(current)
{
case MAIN_SCREEN:
display.setCurrent(MainScreen.getInstance());
break;
case GAME_SCREEN:
display.setCurrent(GameScreen.getInstance());

break;
case SET_SCREEN:
display.setCurrent(SetScreen.getInstance());
break;

case GAME_HELP_SCREEN:
display.setCurrent(new HelpScreen((String)obj));
break;
case SET_HELP_SCREEN:
display.setCurrent(new HelpScreen((String)obj));
break;
}
}
public static void flow(String cmd)
{
switch(current)
{
case MAIN_SCREEN :
if(cmd.equals("开始"))
{
current = GAME_SCREEN ;
show(null);
}
else if(cmd.equals("设定"))
{
current = SET_SCREEN;
show(null);
}
else if(cmd.equals("版权声明"))
{
display.setCurrent(CopyScreen.getInstance());
}
else if(cmd.equals("离开"))
{
midlet.notifyDestroyed();
}
break;
case GAME_SCREEN :
if(cmd.equals("辅助说明"))
{
current = GAME_HELP_SCREEN;
show("游戏的操作方式:向右键要牌,向左键放弃!顺子并同花最大,其次四张相同的牌,顺子,再次三张相同的牌和两个到顺子,再次到同花.");
}
else if(cmd.equals("返回"))
{
current = MAIN_SCREEN;
show(null);
}
break;

case SET_SCREEN :
if(cmd.equals("辅助说明"))
{
current = SET_HELP_SCREEN;
show("设定方式");
}
else if(cmd.equals("返回"))
{
current = MAIN_SCREEN;
show(null);
}
break;
case GAME_HELP_SCREEN :
if(cmd.equals("返回"))
{
current = MAIN_SCREEN;
show(null);
}
break;
case SET_HELP_SCREEN:
if(cmd.equals("返回"))
{
current = SET_SCREEN;
show(null);
}
break;
}
}
}
4.2.4 游戏版权声明
// CopyScreen.java
import javax.microedition.lcdui.*;
public class CopyScreen extends Alert
{
private static Displayable instance;
synchronized public static Displayable getInstance()
{
if(instance == null)
instance = new CopyScreen();
return instance;
}

private CopyScreen()
{
super("版权声明");
// 声明字段
setString("乘风破浪需要勇气!此程序版权属阿朗and海波所有");
setType(AlertType.INFO);
setTimeout(Alert.FOREVER);
}
}
4.2.5 游戏的主要程序
// GameScreen.java
下面根据程序的先后顺序对游戏的主程序作简单的解释。
牌是用一个随机函数来获得的,在牌的开始前就已经分配好了,当然,这是本设计鸡胁的地方。这缺少了变化,也使游戏缺了好玩性,少了人智化,这会在下个版本中改正过来的。在下个版本中,版的分配是在你要牌后才产生,而且它的产生是根本你的总分,你的等级而产生。当你的总分越高你的等级越高,得到你想要的牌的机率就会越高。
下面是本设计中分配牌的随机函数:
for(int i=0; i < 10; i++) {
//使用随机数来决定扑克牌(这种方法要改进)
Card[i] = rdm.nextInt() % 51;
//System.out.println(Card[i]);
if(Card[i] < 0) 
{
//当随机出现负数的时候
Card[i] = Card[i] * (-1);
}
//检查是否重复
for(int j=0; j < i; j++)
{
if(Card[i] == Card[j]) 
{
//若有相同的扑克牌存在时
//再次随机抽牌
i--;
break;
}
}
}

由于对牌的分配是 0到 51 来表示的,所以必需对牌进行转换:它是用这个数除4取余,得到这个牌的色(红心,方块等),除12取余得这具牌的点数:
////// 实现牌的转换
// Card[i] 开始分配的点数
// Num[] 表示牌的点数
// kind[] 表示牌的色
for(int i = 0;i < 10; i++)
{
if( Card[i]%13 == 0)
{
Num[i] = "2";
}
if( Card[i]%13 == 1)
{
Num[i] = "3";
}
if( Card[i]%13 == 2)
{
Num[i] = "4";
}
if( Card[i]%13 == 3)
{
Num[i] = "5";
}
if( Card[i]%13 == 4)
{
Num[i] = "6";
}
if( Card[i]%13 == 5)
{
Num[i] = "7";
}
if( Card[i]%13 == 6)
{
Num[i] = "8";
}
if( Card[i]%13 == 7)
{
Num[i] = "9";
}
if( Card[i]%13 == 8)
{
Num[i] = "10";
}
if( Card[i]%13 == 9)
{
Num[i] = "J";
}
if( Card[i]%13 == 10)
{
Num[i] = "Q";
}
if( Card[i]%13 == 11)
{
Num[i] = "K";
}
if( Card[i]%13 == 12)
{
Num[i] = "A";
}
}
////// 牌的种类
for(int i = 0; i < 10 ; i++)
{
if(Card[i]/13 == 0)
{
///// 黑桃
Kind[i] = 0;
}
if(Card[i]/13 == 3)
{
///// 黑桃
Kind[i] = 3;
}
if(Card[i]/13 == 2)
{
///// 红心
Kind[i] = 2;
}
if(Card[i]/13 == 1)
{
///// 梅花
Kind[i] = 1;
}
}

把得到的版绘画在手机屏幕上:
它是用 paint() 函数来实现的,慢慢移动的镜头用线程的完成。这会使这个线程产生多个线程,这是程序要改进的地方。
// bCard1 用于控制程序的运行,当牌已经移动到目标地,就使bCard1= false
if ( bCard1)
{
//对家

if(x <=12)
{
bCard1 = false;
bCard2 = true;
b2 = true;
bb1 = true;
}
doThreadStart();
g.setColor(255,255,255);
g.fillRect(x - 2,70,25,40);
g.setColor(0,0,0);
g.drawRect(x - 2,70,25,40);
// 自家
if( Kind[1] == 3)
{
g.setColor(255,255,255);
g.fillRect(x - 2,120,25,40);
g.setColor(0,0,0);
g.drawString(Num[1],x,120,g.TOP|g.LEFT);
g.drawImage(king1,x - 2,133,g.TOP|g.LEFT);
g.setColor(0,0,0);
g.drawRect(x - 2,120,25,40);
}
if(Kind[1] == 2)
{
g.setColor(255,255,255);
g.fillRect(x - 2,120,25,40);
g.setColor(255,0,0);
g.drawString(Num[1],x,120,g.TOP|g.LEFT);
g.drawImage(king2,x - 2,133,g.TOP|g.LEFT);
g.setColor(0,0,0);
g.drawRect(x - 2,120,25,40);
}

if(Kind[1] == 1)
{
g.setColor(255,255,255);
g.fillRect(x - 2,120,25,40);
g.setColor(0,0,0);
g.drawString(Num[1],x,120,g.TOP|g.LEFT);
g.drawImage(king3,x - 2,133,g.TOP|g.LEFT);
g.setColor(0,0,0);
g.drawRect(x - 2,120,25,40);
}
if(Kind[1] == 0)
{
g.setColor(255,255,255);
g.fillRect(x - 2,120,25,40);
g.setColor(255,0,0);
g.drawString(Num[1],x,120,g.TOP|g.LEFT);
g.drawImage(king4,x - 2,133,g.TOP|g.LEFT);
g.setColor(0,0,0);
g.drawRect(x - 2,120,25,40);
}
repaint();
}
// 画第一张牌:
// 当移动到目标后,用以下来绘出这张牌。
if ( bb1)
{
g.setColor(255,255,255);
g.fillRect(10,70,25,40);
// 自家
if( Kind[1] == 3)
{
g.setColor(255,255,255);
g.fillRect(10,120,25,40);
g.setColor(0,0,0);
g.drawString(Num[1],12,120,g.TOP|g.LEFT);
g.drawImage(king1,10,133,g.TOP|g.LEFT);
g.setColor(0,0,0);
g.drawRect(10,120,25,40);
}
if(Kind[1] == 2)
{
g.setColor(255,255,255);
g.fillRect(10,120,25,40);
g.setColor(255,0,0);
g.drawString(Num[1],12,120,g.TOP|g.LEFT);
g.drawImage(king2,10,133,g.TOP|g.LEFT);
g.setColor(0,0,0);
g.drawRect(10,120,25,40);
}
if(Kind[1] == 1)
{
g.setColor(255,255,255);
g.fillRect(10,120,25,40);
g.setColor(0,0,0);
g.drawString(Num[1],12,120,g.TOP|g.LEFT);
g.drawImage(king3,10,133,g.TOP|g.LEFT);
g.setColor(0,0,0);
g.drawRect(10,120,25,40);
}

if(Kind[1] == 0)
{
g.setColor(255,255,255);
g.fillRect(10,120,25,40);
g.setColor(255,0,0);
g.drawString(Num[1],12,120,g.TOP|g.LEFT);
g.drawImage(king4,10,133,g.TOP|g.LEFT);
g.setColor(0,0,0);
g.drawRect(10,120,25,40);
}
}
// 以上调用了 drawImage 这个函数,这个函数必须先把图片加入程序中:
try 
{
king1 = Image.createImage("/spade.png");
king2 = Image.createImage("/heart.png");
king3 = Image.createImage("/clover.png");
king4 = Image.createImage("/dia.png");
}
catch(Exception e) {}

// 重载线程函数,它通过不断的调用线程来使程序,改变x 坐标,产生移动的效果。
private void doThreadStart() 
{
new Thread(this).run();
}
public void run() 
{
try
{
if( x >= 12)
{
x = x - 3;
repaint();
Thread.sleep(1);
}
}
catch(Exception e) 
{
}
}

// 按键函数,它响应你的按键。产生按键事件。
protected void keyPressed(int keycode)
{ // 向右键
if(keycode == getKeyCode(Canvas.RIGHT))
{ /// iClick 响应的是第几张牌的事件。
iClick = iClick + 1;
if (iClick == 2)
{
bCard3 = true;
x = getWidth();
}

if (iClick == 3)
{
bCard4 = true;
x = getWidth();
}
if (iClick == 4)
{
bCard5 = true;
x = getWidth();
}
}

游戏的重新开始,把各值初始化。
/////////// 响应键盘事件
protected void keyPressed(int keycode)
{
if(keycode == getKeyCode(Canvas.RIGHT))
{
iClick = iClick + 1;
if (iClick == 2)
{
bCard3 = true;
x = getWidth();
}
if (iClick == 3)
{
bCard4 = true;
x = getWidth();
}
if (iClick == 4)
{
bCard5 = true;
x = getWidth();
}
}
if(keycode == getKeyCode(Canvas.DOWN))
{
///////////////////////////////////////////////////////
bCard1 = false;
bCard2 = false;
bCard3 = false;
bCard4 = false;
bCard5 = false;
b2 = false;
b3 = false;
b4 = false;
b5 = false;

// 用来慢镜后的显示:
bb1 = false;
bb2 = false;
bb3 = false;
bb4 = false;
bb5 = false;
iClick = 1;
bCard1 = true;
x = getWidth();
}
/// 当一盘牌给束时,比较大小:
它的思想是:当玩家要满5张牌时,就判断两个玩家牌的大小。
牌的大小如下定义的:
1 同花顺最大(如:A K Q J 10 并同一色)
2 然后到四个一样的牌(如 四个A最大)
3 再到三个一样的加一个对子。
4 顺子(不要求同色)
5 同色(不要求是顺子)
6 两个对子
7 一个对子
程序实现:
////比较大小:
int tong1 = 0;
int tong2 = 0;
int se1 = 0;
int se2 = 0;
for(int m = 0 ;m <=8; m = m + 2)
{
for(int n  = m ; n <=8; n = n +2)
{
if( Num[m] == Num[n] )
{
tong1 ++;
se1 = m;
}
}
}
////比较对子
for(int m = 1 ;m <=9; m = m + 2)
{
for(int n  = m ; n <=9; n = n +2)
{
if( Num[m] == Num[n] )
{
tong2 ++;
se2 = m;
}
}
}
if(tong1 == tong2)
{
if(se1 != 0)
{
if(Card[se1]%13 > Card[se2]%13)
{
g.setColor(0,0,255);
g.drawString("你输了,点向下键继续!",10,20,g.TOP|g.LEFT);
}
if(Card[se1]%13 < Card[se2]%13)
{
g.setColor(0,0,255);
g.drawString("你赢了,点向下键继续!",10,20,g.TOP|g.LEFT);
}
}
}
if(tong1 != tong2)
{
if(tong1 < tong2)
{
g.setColor(0,0,255);
g.drawString("你赢了,点向下键继续!",10,20,g.TOP|g.LEFT);
}
if(tong1 >tong2)
{
g.setColor(255,0,0);
g.drawString("你输了,点向下键继续!",10,20,g.TOP|g.LEFT);
} } }

4.2.6 游戏的主程序
/// 手机游戏的主程序都是继承 MIDlet类的,本游戏是一开头用代码:Navigator.current = Navigator.MAIN_SCREEN; 就把程序控制交给了类 Navigator。
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class test extends MIDlet
{
boolean init = true;
public test()
{
Navigator.display = Display.getDisplay(this);
 
  作者: 222.248.37.*  2005-6-21 13:48   回复此发言  
 
13 2005年毕业论文----J2ME手机游戏开发高计(原创)
 
Navigator.midlet = this;
}
public void startApp()
{
Navigator.current = Navigator.MAIN_SCREEN;
Navigator.show(null);
if(init)
{
//Alert splash = new Alert("版头画面");
//splash.setType(AlertType.CONFIRMATION);
//splash.setTimeout(5000);
//Navigator.display.setCurrent(splash);
init = false;
}
}
public void pauseApp()
{
}
public void destroyApp(boolean uncondition)
{
}
}
五   常见错误分析

5.1.1 画图错误
在画图出现异常,这是因为在程序开始阶段没有采用异常捕获产生的错误。在程序的初始化加异常捕获就行了,如:
try 
{
king1 = Image.createImage("/spade.png");
king2 = Image.createImage("/heart.png");
king3 = Image.createImage("/clover.png");
king4 = Image.createImage("/dia.png");
}
catch(Exception e) {}

5.2 变量没有定义或没有初始化
这是编程常见的错误,在 java 语言中,变量在使用的使用不像C语言中变量没有初始化时将变量赋为0,面java 变量使用前是需要的。还有变量的定义,变量的使用周期等定义也常使程序出错。要减少这种错误,除了有经验外,还必须要细心。
5.3 在 GameScreen 类中不能用线程
线程作为一个独立的类,它必须给重载过来才能用的。就好像在类 MIDlet中
不能用 Timer 这个 java 包中类的一样。所以必须在GameScreen 内重载它。
private void doThreadStart() 
{
new Thread(this).run();
}
public void run() 
{
try
{
if( x >= 12)
{
x = x - 3;
repaint();
Thread.sleep(1);
}
}
catch(Exception e) 
{
} } }
用以上方法虽然能用线程了,也能达到了牌慢慢移动的效果。但,从另一个方面它就转成另外一个弊端了,线程过多了。
 5.4 在玩游戏时出会发出两张同样的牌
     在玩游戏时出现两张同样的牌,这是一幅牌的游戏所不能允许的,错误原因:在分别黑红梅方的游戏算法中出现了错误,把取4的余改成了取13的模,不会再发出两张同样的牌:
////// 牌的种类
for(int i = 0; i < 10 ; i++)
{
if(Card[i]/13 == 0)
{
///// 黑桃
Kind[i] = 0;
}
if(Card[i]/13 == 3)
{
///// 黑桃
Kind[i] = 3;
}
if(Card[i]/13 == 2)
{
///// 红心
Kind[i] = 2;
}
if(Card[i]/13 == 1)
{
///// 梅花
Kind[i] = 1; }

5.5 错误总结
由以上可以知道,错误的出现不外有两种:一,是知识不够。要解决这种如绘图时的错误,线程的应用等。要彻底解决这种错误就必须多看书,多动手做程序,加深对J2ME 手机游戏开发的了解。二,书写代码时要细心,这样才会减少程序的开发周期。如,变量的没有定义等错误。
六   用户手册
这个游戏仿自周润发的《赌神》,它具有很高的可玩性。
它操作也很方便,玩法也算简单。
下面先说一下它的玩法:
它最多只能要五张牌,一开始,第一张牌是处于未开状态,
第二张打开。然后,根据玩家的牌大小(A最大,K次之)
由牌大的玩家下注(也可以放弃),牌小的玩家决定是否
跟(向右键),或是放弃(向左键)。就这样到了玩家都
有了五张牌。最后,是否开第一张牌也是由大小先下注。
牌大小的定义:
1 同花顺最大(如:A K Q J 10 并同一色)
2 然后到四个一样的牌(如 四个A最大)
3 再到三个一样的加一个对子。
4 顺子(不要求同色)
5 同色(不要求是顺子)
6 两个对子
7 一个对子
8 比较单张牌大小,从A K 这样的次序。
八   参考资料
1 王森 <java 手机和 pda 程序设计入门>
2 循序渐进Java2教程(第三版)Rogers Cadenhead著,淡文刚 于红梅 译,人民邮电出版社
3 java 程序设计教程 清华大学出版社 H.M.Deitel,P.J.Deitel 著
http://www.javasoft.com/j2me
http://www.nokia.com