基于J2ME MIDP2.0的手机游戏的开发与实现.doc
基于J2ME MIDP2.0的手机游戏的开发与实现第一章 引言11 手机软件及游戏概述111 手机软件概述1当今世界,各种智能消费类电子产品,如移动电话、PDA、电视机顶盒等设备呈现爆炸性增长,其数量大大超过了桌面PC。其中手机可以说是最多人拥有的终端设备。这在带给人们更多方便的同时,使用面的广泛也为手机软件带来了巨大的市场。随着手机能力的增强,传统的娱乐、工作、信息交互和共享等事务越来越拓展到以手机为代表的移动设备上完成。传统的手机再也不能满足人们日益提高的要求,因为它们有以下弊端:* 出厂时所有应用程序并操作系统都已固化在硬件中,不能增、删、改,要想获得新的服务,必须再购新机。如果不小心将固化程序删改,还可能导致机子报废;l 访问互联网是通过WAP(Wireless Application Protocal),所有网络资源必须接通网络才能在线访问,非常耗时,费用也高。而Java技术在这方面的优势非常明显:l 应用程序可按需下载,可升级空间大;l Java技术提供了一个类库,它使的应用开发商可以创建更为直觉、丰富的用户界面(GUI); l Java技术使网络带宽的应用更为有效,因为应用程序可以下载到器件上,并在本地运行,仅仅是在连接到服务器时才会占用网络带宽。综上所述,手机软件已经有了很大的市场,而且前景仍然广阔。而Java手机是未来手机的发展方向,是业界的热点。112 手机游戏概述无论在PC还是手机平台上,游戏软件总是受到热烈的欢迎。从最初的贪食蛇到如今的仙剑奇侠传手机版,手机游戏也从尝试走向了成熟。手机硬件的发展为高品质游戏的出现奠定了基础。一代代的高端手机不断突破原来的功能平台,硬件性能显然已超过了早期的微机(386时代前的计算机),智能操作系统的出现更是将手机这一载体推向了另一个高度。于是,一系列游戏精品应运而生。游戏精品为手机游戏带来了广阔的市场。手机游戏以其便携、简单、休闲等特点赢得了各个年龄段的无数爱好者。一款好的手机游戏能在几个大网站的相应版面引发热烈的讨论,其火爆程度不亚于一款PC游戏。手机游戏发展至今,其影响力已经甚为巨大。113手机游戏的特点7首先,手机游戏是容易盈利的。当今影视DVD、PC软件等数字商品的正常盈利因盗版遇到重重障碍。而手机游戏虽也属数字产品,但相比于上面几种产品,已有一条成功的盈利途径,且可操作性强。那就是将游戏发布在手机可上的网络上供用户下载,酌情收费。主要有两种策略:1、一遇下载则从该用户手机费中扣除相应费用即可;2、将程序分为几段(如两个关卡为一段,或得多少分为一段),每到一段结束,即运行嵌在游戏中的续费程序,提醒用户用手机续费,否则不能继续游戏。当然,还是有很大一部分用户可以通过互联网上免费下载来获得游戏,但由于手机游戏涉及面太广,需求量巨大,单个产品又都不贵,使得这个市场商机无限。一个成功的手机游戏本身应具有如下几个特点:1、易于学习: 由于手机有便携的特点,手机游戏一般是用户在工作间隙、等待期间、晚上睡前等零碎时间里方便的消遣工具,而不会花太多时间去研究。保持游戏的简单是最基本的要求。 2、可中断性: 多任务处理是手机生活方式的基本特征。一个好的手机游戏应该提供短时间的娱乐功能,并且允许用户在游戏和工作模式之间顺利切换。 3、基于订阅:手机游戏的盈利成功取决于他们巨大的使用量。基于订阅的游戏是不断产生收入的最好方法。 4、无违法内容:既然所有年龄/性别的人群都玩手机游戏并且常常在公共/工作场合,就应该避免明显的暴力或者色情内容。12 J2ME概述121 J2ME的形成11998年1月,在Sun的实验室里启动了Spotless项目,以研究Java编程语言应用于资源受限的设备。Spotless项目的产品化版本就是现在众所周知的K虚拟机(K Virtual Machine ,KVM)。后来摩托罗拉、诺基亚、西门子、NTT DoCoMo和RIM等其他移动设备制造商和运营商加入到KVM开发阵营。JCP(Java Community Process)在1999年9月10月先后颁布了移动信息设备简单MIDP和互联受限设备配置CLDC两个规范,后来又陆续发布了其他一些规范,这些标准成果形成了Java平台微型版本(Java 2 Platform ,Micro Edition,J2ME)。如今J2ME平台已成为最成功的手机游戏平台。因为J2ME应用在不同设备上都是便携式的,而且 Java被设计成一种安全的语言,所有字节码应用在执行之前都要校验。它的这些非常重要的特征对所有组织都有益。122 J2ME体系结构概述1J2ME所面对的是大量不同的设备,它们在外观和功能上均各不相同。J2ME在对这些设备进行分类时,将一些共性提取出来形成适合于某个范畴中设备可用的规范称为配置(Configuration),包含虚拟机和核心的类库;J2ME将某一行业或领域内设备的特性提取出来,形成简表(Profile),指的是某个行业或某个领域内特定的特性总结。J2ME分为两个配置:一个是CDC(Connected Device Configuration),另一个是CLDC(Connected Limited Device Configuration)。它们所针对的设备的CPU和内存是不一样的,所以它们所使用的虚拟机和核心类库也不相同。简表面向配置之上的纵向设备,定义了配置之上受支持设备的类型。在CDC上目前定义了Foundation Profile、Personal Basis Profile等简表;在CLDC上定义了Mobile Information Device Profile(MIDP)等简表。本文要介绍的程序就是在MIDP2.0的基础上编写的。13 本章小结本章回顾了手机软件的发展,展望了其广阔的前景;分析了手机游戏业务的诸多特点;介绍了J2ME的体系结构。由此描述了本论文的相关背景。第二章 开发环境搭建21 开发工具概览操作系统: Microsoft Windows XP SP2程序语言: Java 2开发工具: Java(TM) 2 Platform Standard Edition (J2sdk 1.5.0) JBuilder 2006Photoshop csMotorola SDK上述工具的安装与环境的配置很简单,且不是本文重点,故不再赘述。22 关于JDKJDK 是整个Java的核心,提供了虚拟机来运行程序。它包括了Java运行环境(Java Runtime Envirnment), Java基础的类库(rt.jar),以及编译器、打包工具、文档生成器和查错工具等基本组件。不论什么Java应用服务器实质都是内置了某个版本的JDK。最主流的JDK是Sun公司发布的JDK。23 关于WTK2WTK(Wireless Tool Kit)是Sun公司针对J2ME推出的用于手机和Palm等移动设备的开发包,是除手机厂商的专用开发包外唯一的手机模拟器开发包。它通用性高,开发出的应用程序可保证能运行在大部分设备上,而不像专用厂商具有一定的不兼容性。虽然它没有强大的功能和完善的调试手段,但它提供运行模拟器的最基本组件,是其他IDE需集成采用的必备元素24 关于JBuider 20063JBuilder是Borland公司开发的针对Java的开发工具,拥有众多的开发者。使用JBuilder将可以快速,有效的开发各类java应用,它使用的JDK与SUN公司标准的JDK不同,它经过了较多的修改,以便开发人员能够像开发Delphi应用那样开发java应用。JB7和JB8通过加入mobile set包并且和Nokia开发平台相集成的方法开发MIIDP程序,JB9已将JDK与WTK集成在软件中。JBuilder2006是JBuilder系列的最新版本。2. 4 关于Photoshop cs 和Motorola SDKPhotoshop在此只是一个辅助软件,不再过多介绍。它负责将游戏中用到的各种图片加工并保存为PNG格式,这是手机唯一能准确识别图片的格式。Motorola SDK是摩托罗拉公司专有的SDK。在本设计中仅仅用到它的模拟器。由于手机游戏存在移植性差的特点,所以只有选择一款确定的手机,才能将游戏的画面等各项元素做到最好。本设计选用了Motorola V360i模拟器2. 5 本章小结本章针对要开发的程序介绍了相关的软硬件环境及开发工具。第三章 作品概况、结构及相关技术 31游戏特点详述1、本游戏名叫荣耀飞车,基于MIDP2.0设计开发。其中用到了Game包等MIDP2.0新增的特性。游戏实现了全新游戏、游戏帮助、游戏存储和读取、退出游戏等基本功能,还加入了金钱、经验、不同的车辆等RPG元素,更有充满趣味的称号、道具等系统。这些比较复杂的系统的加入,不但使程序用到了更多的基本技术,也使得开发者更要注重各个类之间的逻辑关系,才能准确地做出相应的效果,而不只是实现一些通用的基本功能模块就能完成任务。 2、本游戏是俯视角赛车游戏,游戏中会遇见山石、河流等障碍阻止选手顺利到达终点,但用户可以依靠熟练的操作、获得的称号,以及从赛道上得到的道具来克服困难。3、菜单的实现方法放弃了相对简单的高级用户界面,采用复杂却灵活多变的低级用户界面技术。游戏中共有五个菜单,虽基本原理大同小异,但由于使用低级用户界面,使它们的外在表现丰富多彩,毫无重复之感。4、作为设计核心的赛道地图采用新增GAME包中的“背景图层(TiledLayer)”和“精灵(Sprite)”技术,赛车始终在屏幕的下方,而地图自动以当前赛车速度向下滚动。在左上角可以看到车辆当前获得的道具数、生命值和速度;右上角显示剩余时间。时间耗尽或车辆生命值小于零,游戏失败。顺利冲过终点则获胜,并根据剩余的时间和生命值获得相应的金钱经验。若达到了某种条件,还能获得称号,对以后游戏有帮助。5、主菜单上的读取游戏选项可将上次存储的游戏参数读出,并继续上次的游戏。6、由于手机的资源非常有限,所以程序还要进行一些内存优化。上述各模块的算法和具体代码将在第四章详细介绍。7、J2ME参考文献很多来自各大网站。32 程序详细流程程序采用面向对象的设计模式。各类是用与类名称相同的文件名存储的,且尽量用其实现的功能命名,以不致混乱。如主菜单类就用MainList命名。进入游戏后,先出现LOGO画面,显示欢迎字样和作者信息,持续三秒,然后进入主菜单。选择“全新游戏”后,填入ID,选择车辆,就进入“大厅”这个主菜单后又一重要的导航界面。可以选择“进入比赛”,选好跑道,就正式开始赛车游戏了。程序中的一个开关语句会根据用户选择的不同跑道调用不同的绘图函数,对背景的所有物体进行绘图。在主程序运行的线程中,画面刷新将以一定的频率采用双缓冲技术对屏幕重绘,实时反映整个游戏的进行状态。玩家控制的赛车就运行在主线程中,随屏幕刷新的频率而活动。在屏幕重绘的主程序中,将在每次的循环中判断若干事件。如:玩家控制的赛车生命值点数,玩家是否与其它图层碰撞,玩家的道具是否已使用等,由此执行相应的代码产生正确的效果。比赛一开始出现三秒倒计时,然后用户就能控制赛车移动了。胜负判定后,出现“公告板”显示本场游戏所获的金钱、经验,及得到或失去称号的提醒信息。“公告板”界面的两个软键的功能分别是保存游戏和回到“大厅”继续下一盘游戏。游戏详细流程图如下所示:(图3.1 游戏详细流程图)33 游戏相关技术331 MIDlet 2在MIDP中,应用程序称为MIDlet,因为每个MIDP应用程序都需要继承自MIDlet类。MIDlet的生命周期定义了MIDlet的各种状态以及各种状态之间如何转换。它可以利用如图3.2的状态机清楚地对此进行说明。(图3.2 MIDlet状态机)MIDlet运行时,先由应用管理系统创建一个MIDlet的实例,再调用MIDlet.startApp()方法,MIDlet进入活动状态;通过调用MIDlet.pauseApp()使资源暂时可用于其他功能;应用管理系统认为不再需要MIDlet了,则通过调用MIDlet.destroyApp()销毁程序。332 Display类1图形控件都是由Display对象管理的,每一个应用程序都会访问这一对象的惟一实例。该实例可以通过静态的Display.getDisplay()方法获得,该方法通常会把指向该实例的引用保存在一个成员变量里。setCurrent()方法用来为特定屏幕元素设置焦点,getCurrent()方法则用来获取元素焦点。MIDP2.0中增加了用于屏幕设置操作方法,如设置背景灯的方法flashBacklight()、设置振动的方法vibrate()等。本游戏中最常用到的是setCurrent()方法,用来将相应的类的绘图效果调出显示在屏幕上,是 切换游戏界面的重要方法。333 J2ME高级用户界面和低级用户界面用户界面(User Interface,UI)是用户与应用程序进行沟通的渠道,应用程序通过UI将自己所具有的功能,通过一定的方式呈现给使用者。UI又分为高级UI和低级UI。高级UI从类库的角度讲又叫高级API,使用极其简单,只要调用类库中现成的方法,即可方便地做出列表、文本框等界面。但它有非常明显的缺陷,即表现形式不灵活,程序开发者不能精确地按自己的需要和创意来控制屏幕元素,十分死板。而游戏正是要求精确和创意,所以本程序的绝大部分界面都用低级UI来做。低级UI又叫底层API,它对图形元素定位和控制的灵活精确,以及可以获得低级输入事件的功能,得益于从Displayable类继承下来的Canvas类。Canvas提供了键盘事件,并定义了允许将键盘按键映射为游戏控制键的函数。同时Canvas类可与高级API类交互,程序可在需要时在Canvas中掺入高级类的组件。本程序就在ID输入和帮助文档的显示两方面用到了高级类组件。类Graphics提供了相应的在Canvas上2D绘图的能力。它具有24位深度色彩的绘制能力,以三原色分别各占一个字节表示其颜色。图像绘制的最终位置由Graphics对象的来源决定。有一个比方,Canvas类相当于画布,Graphics类相当于画笔。Graphics对象的绘制目标包括用户设备显示屏和屏幕外图形缓冲区,在Canvas类中定义的paint()方法,是Graphics对象能够获取绘制画布屏幕目标的唯一方式,并且应用程序只能有paint()方法的运行期间使用这个Graphics对象并绘制图形;Image对象也可以用作Graphics对象的绘制目标,通过调用这个用作缓冲图象的getGraphics方法来获得绘制的Graphics对象。Graphics类中有很多绘制图形或文本的方法,具体用法在API中有规范,文献1、2、4、5、6和9中有侧重点不同的介绍。334 MIDP 2.0 新增Game包1 5在MIDP 1.0中,游戏开发者只能使用Canvas进行游戏编程。尽管Canvas为游戏开发者提供了各种灵活的界面控制API,开发者的游戏程序设计仍是一个很复杂的工作,对开发者的时间和精力都是极大的浪费。MIDP2.0新增的Game API包括5个类:GameCanvas类,Sprite类,Layer类,TiledLayer类和LayerManager类。用它们可以省去了开发者们许多不必要的麻烦。上述几个新类本程序中均有用到。GameCanvas类提供游戏基础用户界面,它与Canvas类相比有两点不同:拥有屏幕缓冲和可以直接得到设备键盘的物理状态。Sprite类是Layer类的一个子类,主要用于设计能够独立移动的物体。Sprite类实现的功能类似于电影的胶片功能,它们都是由一幅幅小的图片组合而成。一幅图片称为一帧(frame)。用到时只需指定相应的帧或帧序列,即可将图像或动画在屏幕上绘成。TiledLayer类是Layer类的另一个子类。使用时将源图像分割成大小相等的子图片,将游戏画面分割成大小相同的网格。设计好地图后,在每个网格中填上相应的子图片,即可绘成各种各样的不同地图。LayerManager类提供控制整体画面层的控制。它包括了一系列自动获取了代号和位置的层,简化了各层加入游戏画面的过程,提供了自动排序和绘制的能力。LayerManager存储了一个层的列表,新的层可以用append函数附加、删除和插入。LayerManager中可调用setViewWindow()方法来改变View Window的位置,制造出滚动屏幕的效果。这里只介绍了新增Game包中各类的基本理论,它们在本游戏中的应用详见第四章第四节。335 永久存储系统2在MIDP中,定义了一种面向记录的数据库系统RMS(Record Management System),存储机制由javax.microedition.rms中的类RecordStore实现。MIDlet运行结束后,需要保存的信息存储在“记录存储”(Record Store)中。MIDlet可以通过记录存储的名字记录存储。记录存储中读取或写入的字节数组都只能代表一个字段的信息,或者说是一种类型的数据,这在实际使用中很少见,大多数情况下都要保存很多类型的数据。由于在记录存储中是以字节为基本单位来存放的,只能存放一个字节数组的数据,即数据在存储到记录存储之前,需要先将数据转换成一个字节数组,我们可以用流的技术来达到这个目的。利用ByteArrayOutputStream和DataOutputStream可以写原始数据类型的特点向输出流中写入各种类型的数据,将输出流转换成字节数组,直接写入到记录存储中。从记录存储中读取记录的过程,需要用到ByteArrayInputStream和DataInputStream类。写入和读出的顺序必须一致,还有不同类型数据有不同的写入和读出的方法函数。本程序对用户的ID、车辆等资料的保存就用到这种技术。34 本章小结本章对本赛车游戏进行了详细的介绍,写出了游戏功能和游戏流程;接着从理论上对游戏所用到的相关技术如用户界面、绘图、新增Game包、永久存储、多媒体等进行了详细解释,为第四章中详细解释本游戏中和各种算法作准备。第四章 程序各功能的算法设计及具体代码实现41 MIDlet类MIDlet类负责游戏的初始化、暂停和销毁。拥有MIDlet必须有的三个抽象方法:startApp()、pauseApp()和destroyApp(Boolean unconditional)。每个程序都必须有一个继承自MIDlet的主类。在本游戏中,主类为UI.java,来对游戏中各个界面进行串联,并起到类间参数传递的作用。其结构为:public class UI extends MIDlet implements CommandListenerprivate Display display;private Command cmd;public UI() display = Display.getDisplay(this); public void startApp() /初始化程序;public void pauseApp() public void destroyApp(boolean unconditional) public void commandAction(Command c, Displayable d) 程序先定义了Display类和Command类。在UI()函数体中用Display.getDisplay(this)语句获得实例。startApp()用来初始化程序,调出LOGO画面和主菜单。pauseApp()和destroyApp()虽然为空,但必须存在。Command命令类用来定义手机左右两个软键盘在不同游戏界面中的功能,用cmd=new Command(String,int ,int)初始化。其中第一个参数为该命令在手机屏幕上显示的字符串,如“确定”、“退出”、“start”等;第二个是命令类型,确定命令对应软键的位置,如Command.Back、Command.OK等;第三个是优先值,值越小优先度越高,命令在屏幕上越先显示出来。为了使用户界面上的按钮可以被响应,在定义了UI类后需加上implements CommandListener设定命令监听者,提供按键监听功能。当系统发送某个命令时,便由commandAction( )这个系统方法进行统筹处理。其具体代码为:commandAction(Command c,Displayable d) if(c= =cmd,d= =displayable) 代码段的作用即:在displayable界面上按下cmd按钮后执行if语句中的指令。每个不同作用的按键响应即需要一个这样的if语句。本游戏所有的软键盘按键响应程序都在UI,java的commandAction()方法中包括。UI类还负责游戏界面的切换。用setCurrent()方法实现。为了使按键能够被响应,还需要加上setCommandListener()方法。本游戏中界面切换的完整程序段为:commandAction(Command c,Displayable d)if(c= =cmd1,d= =display1) /按下display1界面上的cmd1按钮;setCurrent(display2); /显示display2界面类;display2.addCommand(cmd2);/在display2界面添加按钮cmd2; /各种按钮添加完毕;display2.setCommandListener(this);/添加命令监听;42 LOGO画面游戏中出现的第一个界面LOGO画面由类Cover绘制,起到吸引用户和显示软件相关信息的作用。如上节所述,LOGO由UI类中的抽象方法startApp()调出。程序先定义了Cover类变量cv,语句 cv=new cover(display);将cv初始化,然后用display.setCurrent(cv)方法将LOGO画面显示在屏幕上。LOGO画面是一个动画,其实是两幅图片先后被显示出来 ,如下图所示。 (图4.1) (图4.2)本游戏中用到了Java的多线程。从LOGO画面的显示到主菜单被调用就是多线程的典型应用。UI类中初始化了cv并将其显示出来后,调用线程类的Thread.sleep(3000)语句使主类所在的线程停止3秒钟。在这3秒钟内,Cover类先在paint(Graphics g)函数中调用g.drawImage()方法,画成图4.1。后用Thread.sleep(1000)语句使Cover类所在线程停止1秒钟,再画图4.2。形成一个动画。3秒钟后,主类线程继续运行,用display.setCurrent(ml)调出主菜单。ml即UI类定义的MainList类变量。4. 3 菜单界面菜单在游戏中的地位非常重要。本游戏的10个类中就有5个实现各种菜单功能的类。它们分别是主菜单MainList、选车界面CarChoose、大厅界面Holl、赛道选择菜单RaceWayList和二级菜单SecondMenu。431 菜单功能的实现由于采用了低级UI绘图,各个菜单都极具个性,并不显得重复单调。但它们的功能都一样:根据用户选择的选项来决定程序的下一步动作。这种功能的实现代码也没有质的差异。作为程序中第一个比较复杂的算法,下面以MainList为例,结合程序中的注释,将代码进行详细解释(主菜单的四个选项“全新游戏”、“读取游戏”、“游戏说明”和“退出游戏”分别对应selected的值0、1、2、3):MainList.java中相关代码段:/根据按键改变selected的值public int selected = 0; public void keyPressed(int keyCode) int action = this.getGameAction(keyCode);switch (action) case Canvas.DOWN: /按下键selected = (selected + 1) % 4; break; / selected增大。/取4的余数,所以若selected为3,再按下键则变回0; case Canvas.UP: /按上键 if (-selected < 0) selected += 4; /selected减小。/当减小到小于0时,selected加4,所以若selected已为0,再按上键则变回3; break; default: break; UI.java中的相关代码段:/根据菜单类中selected值决定响应动作if (c = ok & d = ml) /用户按下主菜单界面上的确定按钮switch (ml.selected) /根据MainList中的selected值决定程序的响应 case 0: /ml.selected为0,对应“全新游戏”,创建新用户界面break; case 1:/ml.selected为1,对应“读取游戏”,读取游戏存档break;其它四个菜单虽然选项排列方式、选项内容、响应动作各不相同,但原理都如上面一样:在菜单类中用keyPress()方法获得键盘状态,(用方向键)控制参数selected的变化,在UI类中根据菜单类中selected的值来决定响应动作。432 菜单界面的绘制上一节介绍了菜单的代码原理,但菜单如果只有后台代码却不可视的话,使用是极不方便的。所以需要将菜单用可视化组件显示在屏幕上。本游戏采用了低级绘图技术来绘制个性十足,直观又美观的游戏菜单。绘制菜单的关键是根据用户所按下的键来显示相应的画面,使用户明白地知道所选中的功能。采用的方法和菜单功能的实现相似,根据菜单类中selected的值来决定要绘制的图像。根据按键来改变selected值的代码与上一节相同,而图像绘制代码则是在菜单类的paint()函数中用开关语句控制。在循环语句中调用drawString()方法在屏幕上从上到下均匀地用黑色画出字符串数组中的四个字符串,再根据selected与四个字符串的对应关系确定哪个是被选中的字符串,将其绘为红色,盖住原来的黑色,并确定被选中的字符串两旁的小箭头的准确位置。 (图4.3 主菜单界面)绘制效果如图4.3所示,可以看出图中此时selected为0。上方“荣耀飞车”四字是一幅图片,用g.drawImage()方法绘成。字符串两边的红箭头用绘制三角形的g.fillTriangle()方法和绘制矩形的fillRect()方法组合绘成。 44游戏场景的设计游戏场景即赛车在赛道上进行游戏的界面。这一界面十分复杂,不但绘制了跑道、路障、赛车等游戏元素,还得在适当的地方显示车辆生命值、获得道具数、剩余时间数等游戏信息。是MIDP2.0中新增游戏API的用武之地。本功能在游戏中由类CarCanvas实现。441 GameCanvas类的应用GameCanvas为游戏场景提供了两个非常重要的功能,即获取键盘输入状态和屏幕缓冲。获取键盘输入状态放弃了如我们菜单中用到的getGameAction()方法,改用getKeyStates()方法。它的优点是判断键盘状态更灵敏,而且可以实现组合键的效果。首先用方法getKeyStates()得到键盘状态,再通过位操作判断方向键的状态后作出相应的响应。按键动作在GameCanvas中通过定义位操作字段表示,除UP_PRESSED,DOWN_PRESSED,LEFT_PRESSED,RIGHT_PRESSED四个方向键外还有FIRE_PRESSED; GAME_A_PRESSED; GAME_B_PRESSED; GAME_C_PRESSED; GAME_D_PRESSED。屏幕缓冲技术用来保证游戏画面连贯。用多帧图像形成动画时,画面在显示的同时,程序又在改变它,可能形成屏幕闪烁。在GameCanvas中使用了双缓冲技术简单地解决了这一问题,即在绘图前用Graphics g=getGraphics;语句获得双缓冲,图像变化后用flushGraphics();语句释放缓存,改变画面。442 Layer类和LayerManager类的应用1、TiledLayer类继承自Layer类,用来建立背景图像,可以用一个小的源图像贴的集合来高效制作大的图像。其方法简介如下:先用初始化方法把源图像分成若干块小的图像帖片,TiledLayer类分配给每个图像帖片编号,左上角图片规定为1,以此类推。在本游戏中将源图像way.png分为16 X 16像素的小帖片。 同时,游戏场景也被图层初始化方法分为23 X 200的网格,每个网格都有自己的坐标位置,大小与小帖片相同。绘制地图的相关代码如下:/先将参数img用下面一句代码初始化为way.png:Image img = Image.createImage("/game/pic/way.png"); /创建图层;TiledLayer raceway= new TiledLayer(23, 200, img, 16, 16);/在以(3,0)为左上角锚点,宽4个,长200个网格的区域中填编号为6表示草地的帖片; raceway .fillCells(3, 0, 4, 200, 6); /在以(10,100)为坐标的网格中填编号为15的表示路障的贴片;raceway.setCell(10,100,15);上面以本游戏中的一小段有代表性的程序为例,说明了地图绘制的相关方法和方法中各参数的作用。将图层初始化后,只要运用fillCells()和setCell()两个方法,设好各参数,就能随心所欲地绘制出各种各样的地图来。way.png如下图所示。数字为帖片号,是本人为了清楚加的,源图像中没有:(图4.4 TiledLayer源图像) 2、Sprite类也继承自Layer类,使用一系列源图像帧来产生动画。创建一个Sprite类所需要的只是源图像和每个帧的尺寸。将源图像按帧尺寸从左上角起截成若干个子图片(帧),按从左到右,从上到下的顺序自动给帧编号。在本游戏中,源图像car_0.png, car_1.png, car_2.png, car_3.png用来创建帧大小为32 X 32像素的赛车Sprite对象:public void CarMade() /创建赛车,img已被初始化为源图像,后两个参数表示帧大小为32 X 32像素;Sprite carsprite = new Sprite(img, 32, 32);/显示第4帧作为初始图像;carsprite.setFrame (4); /设置参考像素点的位置为(16,16);carsprite.setRefPixelPosition(16,16);/设置Sprite类的参考像素点在TiledLayer上的位置。两个参数即X,Y轴坐标。carsprite.setPosition(9*16-25,200*16-32); 赛车的四个源图像中均有赛车正常、左转、右转、毁坏的子图片,根据游戏当前情况用setFrame()方法将相应的赛车状态显示出来。帧号为4的子图片画的是赛车正常状态,所以上例中在一开始用setFrame(4);语句。游戏截图如下图所示:(图4.5 游戏截图)3、LayerManager类是Layer类的管理器,提供各种Layer类的绘制、设定观察窗口的功能。Layer类创建之后,可以调用LayerManager的append()方法将Layer加入到LayerManager中,并调用paint()方法画出来。注意,后加入的Layer会被先加入的盖住。setViewWindow()也是一个非常重要的方法。由于大背景TiledLayer往往大于手机的屏幕,所以需要这个方法来设定当前屏幕上显示背景的哪一块。本游戏中通过计算确定了参数,设定赛车总保持在屏幕下方,造成滚屏效果。 4、Sprite类和TiledLayer类都继承自Layer图层类。它们有一些共有的方法。Move()方法能使图层移动。在本游戏中,赛车的移动就是用的它:int dx,dy;carSprite.move(dx,dy);/赛车水平方向上的速度为dx,垂直方向上的速度为dy;根据不同的情况调整dx和dy的值,赛车就会以不同的速度,不同的方向来移动。collidesWith()方法提供了碰撞检测功能,是非常重要的。游戏一定会遇上各元素互相碰撞的情况。另外,由于本游戏中赛车与地图上不同的东西碰撞产生的效果不同,所以地图上需要多个图层,如表示草地的raceway、表示河流的water、表示路障的stacle、表示终点的endline等,通过LayerManager类绘制在屏幕上。如本游戏中赛车与路障相撞,赛车速度减半,生命值减5的代码如下:/与图层stacle碰撞,布尔参数true表示像素级碰撞;if (carsprite.collidesWith(stacle, true) car.hp=car.hp-5;/赛车的hp减5; dy=dy/2; /赛车的垂直速度减半;45 公告板界面公告板界面在每次比赛结束后向用户显示本次比赛的胜负和金钱、经验、称号的得失情况。在游戏中由WinorLose类绘制。绘制本身没有什么特别,但由于游戏结果差异特别大,所以需要在此类中设一个整形变量来记录游戏结束时的状态。WinorLose类中将其设为states。其它类中也有类似的参数。每当比赛结束,CarCanvas类中表胜负的Result等都用switch语句相互影响着变化。最终由UI类传至WinorLose类,给states赋值,再用switch语句分情况绘出不同的字符串。这一过程牵涉到许多的类,是程序中逻辑性比较强的部分。下面举游戏中的一个例子,为简洁鲜明,只写出胜利时公告板上绘出“您得到了金钱XX”字样的程序段,其它的如胜负情况、称号得失情况,并所有类间参数传递的原理都相似。CarCanvas.java中的相关片断:if (carsprite.collidesWith(endline, true) /与终点碰撞,胜利 Result=1;/表示胜利; UI.java中的相关片断: /