联系方式: 微信:biyebang QQ: 629001810
摘要
随着手机业务的迅速发展,手机游戏逐渐成为移动增值服务的兴奋点。本毕业设计就着眼于J2ME技术的应用,设计与开发一款探险类手机游戏(堡垒)。
该堡垒游戏是基于J2ME开发的手机RPG游戏,采用midp2.0技术实现了菜单、地图、主角动作及怪物动作和AI等,主要通过精灵间的碰撞检测来触动事件发生。游戏的主流程是玩家利用手机键盘操作主角在堡垒中拾取道具破解迷宫,并且随着关数的增加,游戏的难度也逐渐增加。另外,游戏中还包括了排行榜,声音设置,帮助等各种附加功能。其中充满了趣味性和刺激性,是适合各年龄段的益智冒险类游戏。
本论文介绍了J2ME的相关技术及该堡垒游戏程序的结构分析和具体功能的实现。
系统设计 手机游戏(堡垒)的开发 相信大家一定都在8位机机上玩过《冒险岛》这款游戏,非常有趣味性。 游戏中玩家通过不断的闯关,来解救公主。在每个关都很很多的怪物阻挡着你,所以需要运用各种机关或者秘籍来杀死它们。杀死他们的同时还可以获得各种奖励,加生命,加血等,增加了游戏的趣味性。 如图2所示: 这款《冒险岛》游戏的实现相对于其他RPG或者网络版手机游戏稍简单一些,适合初学者作为练习,所以我决定编写一款类似的手机游戏。 由于之前对手机游戏的编程知识以及游戏的设计只有初步的了解,因此,我们在游戏的构架和思路上经历了几个阶段。 刚开始我们只对J2ME有初步的了解。这时我们只是模仿之前在PC上看到的游戏,用语言把游戏的实现感性的描述为几大部分: 游戏界面系统:包括游戏开始界面;游戏开局界面;游戏运行界面;游戏结束界面。 游戏元素:菜单类;画布类;人物类;排行榜类。 在进一步熟悉了J2ME知识后,对框架做出了一些修改,逐步把游戏的基本功能确定。游戏依次进入加载界面;主菜单;游戏运行界面;游戏结束界面。 具体实现的功能为: 1.主菜单,有如下选项: (1)开始游戏——进入游戏界面。 (2)声音——设置声音的有无选项。 (3)帮助——介绍游戏的玩法。 (4)排行榜——玩家所得分数的排行榜。 (5)关于——用来显示说明信息以及背景图片。 2.游戏运行界面,包括: 游戏界面;目前游戏得分;游戏关数;生命次数; 3.游戏结束界面:游戏结束后,显示一行说明信息,然后退回到菜单。 游戏的主要模块为: 1.游戏主MIDlet(GameMIDlet)——对游戏生命周期的判断;对画布类的调用;管理游戏程序中各个屏幕之间的转换。 2.游戏画布(MyGame)——对游戏所用变量,常量的设定;游戏的初始化;游戏中精灵运动轨迹的控制;精灵与砖块的碰撞检测以及砖块状态的控制;游戏中各关卡的基本设定;游戏中对按键状态的处理。 3.菜单类——游戏中菜单事件的处理。 4.GameOgre类——游戏中怪物的类。 5.GamePlayer类——玩家控制的精灵类。 6.GameRMS类——用于实现分数排行榜。 7.PlayMusic类——用于实现音乐的播放。 8.MySet类——声音大小的设置。 程序一共有8个主要类,其中菜单类负责各个屏幕的切换。程序的类结构如图3所示: 进入游戏菜单。初始情况下,游戏菜单有5个选项,它们分别是开始游戏、游戏说明和排行榜、设置、关于。选择开始新游戏则进入游戏,在游戏中如果按下非游戏键则中断游戏返回菜单,此时菜单中增加了一个继续游戏的选项,可以返回游戏也可以重新开始新的游戏。在菜单中选择游戏说明或者高分记录,则进入相应的屏幕,他们都能用“后退”软键返回菜单。菜单中的退出选项用于退出程序。游戏的流程如图4所示: 系统实现 游戏一共实现了几个类,包括用于游戏外部的、菜单类、排行榜屏幕类、声音设置屏幕类、结束屏幕类,以及用于游戏本身的游戏画布类、声音效果类。 MIDlet是最核心的类。MIDlet程序有三种状态: 1.暂停状态 2.运行状态 3.销毁状态 J2ME程序都是从MIDlet类开始执行,系统在执行MIDlet程序时,首先构造一个MIDlet类型的对象,然后使程序进入到暂停状态,按照生命周期的规定,系统会自动调用MIDlet对象的startApp方法使程序进入到运行状态,开始程序的执行。 下图是运行时显示的画布对象: 首先,先要创建MIDlet类型的对象,下面我们来看对象的构造方法: //主程序构造方法 public GameMIDlet() { rs= null; RecordName= “GameRMS”; GameMenu.display= Display.getDisplay(this) ; GameMenu.midlet= this; } 在这个构造器中,创建了一个Display对象用于显示,然后,把当前程序的引用传给菜单类中的静态变量。 Display类有两个最主要的作用: 1、 获得屏幕的属性。例如屏幕是否是彩色的,以及支持的颜色数量等信息。 2、 控制屏幕的显示。例如使屏幕显示某个指定界面或者获得当前的显示界面等。 其中,特别是第二个作用使用的更加频繁。 当程序启动的时候,则调用了程序的startApp方法,用来显示画面: public void startApp() { //程序开始,打开数据库,如果数据库没有数据就添加10笔空白数据,最后关闭数据库 try { rs= RecordStore.openRecordStore(RecordName,true); }catch (Exception e) { System.out.println(“创建数据库失败”); } GameRMSrd = new GameRMS(“player”,0); bytetem[] = rd.encode(); try { if(rs.getNumRecords()==0) { for(inti=1;i<=11;i++) { try { rs.addRecord(tem,0,tem.length); }catch(Exception e) { System.out.println(“添加数据失败”); } } } } catch (Exception e1) { System.out.println(“得到数据库大小失败”); } try { rs.closeRecordStore(); }catch(Exceptione) { System.out.println(“关闭数据库失败”); } //将画面设为主画面MAIN SCREEN newThread(this).start(); } 程序开始,打开数据库,如果数据库没有数据就添加10笔空白数据,最后关闭数据库,然后开始一个新的线程,启动菜单。 MIDP中的存储系统是实际为一个类似于数据库的系统,而并不是简单的文件系统,这个系统称为记录管理系统(Record Management System,RMS)。RMS实现数据的持久性管理,提供了数据存储的功能,可以在程序下次启动是再次使用。 记录存储是面向记录的数据库,可把一个记录存储看作一个数据库文件,由许多记录组合而成,这些记录将持久保存并支持跨多个MIDlet的请求。在系统平台的整个常规应用期间,包括重启,更换电池等,MIDlet的记录存储都由系统平台负责维护,系统会尽可能维持记录的完整性。 当调用destroyApp 这个方法的时候,则退出程序。 public void destroyApp(booleanunconditional) { exit(); } public void exit() { System.gc(); notifyDestroyed(); } 退出程序的时候,要实行垃圾回收,释放掉不再使用的内存。 2.屏幕切换,如图6: 菜单的图形切换采用的是流程控制器,非常方便,简洁的实现了屏幕画面的切换,下面是关键实现代码: //导航器 //设置4个常量分别代表主画面,游戏画面,帮助画面和排行榜画面 final public static int MY_MENU = 1; final public static int MY_GAME = 2; final public static int MY_HELP = 3; final public static int MY_RMS = 4; final public static int MY_ABOUT = 5; final public static int MY_SET = 6; 采用静态变量来对画面进行标识。 //流程执行,获得需要画面的对象,并显示在屏幕上 public static void show() { switch(current) { case MY_MENU: display.setCurrent(MyMenu.getInstance()); break; case MY_GAME: display.setCurrent(MyGame.getInstance()); break; case MY_SET: display.setCurrent(MySet.getInstance()); break; case MY_HELP: display.setCurrent(MyHelp.getInstance()); break; case MY_RMS: display.setCurrent(MyRms.getInstance()); break; case MY_ABOUT: display.setCurrent(MyAbout.getInstance()); break; } } 这样,采用上面这个函数,非常方便的实现了流程的转换。实现屏幕的切换。 在J2ME游戏编程中,Canvas类是最常用的类之一,该类提供了获得手机屏幕属性、绘制界面以及事件处理等很多实用的功能。 Canvas类是Displayable的子类,主要用来需要处理低级事件,例如键盘按键事件等,以及需要绘制屏幕的程序。在实际的使用过程,一般都通过继承Canvas来利用该类提供的功能。Canvas类是一个抽象类,继承该类的时候必须覆盖paint方法。 GameCanvas 类提供了基本的游戏用户接口。除了从Canvas继承下来的特性(命令,输入事件等)以外,它还提供了专门针对游戏的功能,比如后备屏幕缓冲和键盘状态查询的能力。这也是GameCanvas与Canvas相比所具有的两个优点.来看游戏画面: 当流程控制器转到游戏运行界面时,则用到了游戏画布MyGame类。 下面是游戏截图: public class MyGame extendsGameCanvas implements Runnable { } 我们所用的画布程序,正是继承了GameCanvas这个类,同时派生了Runnable接口,来实现生成一个新的线程的功能。 程序的run方法: public void run() { longst = 0; long et = 0; Graphics g = getGraphics(); for(stage = 2;stage <= 4;stage++) { flag= true; g.setColor(0,0,0); g.fillRect(0,0,getWidth(),getHeight()); g.setColor(255,255,0); g.setFont(Font.getFont(Font.FACE_PROPORTIONAL,Font.STYLE_BOLD,Font.SIZE_LARGE)); g.drawString(“第 ” + (stage -1)+ “ 关”,getWidth()/2-30,getHeight()/2-10,Graphics.TOP|Graphics.LEFT); flushGraphics(); try{ Thread.sleep(1000);} catch (InterruptedException e1) {} CREAT_STAGE(); //调用创建关卡的方法 try{Thread.sleep(500);} catch(InterruptedException e1) {} mpaint(g); isKey= true; while(flag) //游戏正式开始 { while(ispause) //判断是否按下暂停 { try{Thread.sleep(100); } catch(Exceptione){} } st = System.currentTimeMillis(); StartGameTime =System.currentTimeMillis(); INPUT_KEY(); //调用按键方法 PENG_ZHUANG(); //调用各种判断和行动的方法 movMing(); //调用刷新画面的方法 mpaint(g); et = System.currentTimeMillis(); if((et-st)<rest) { try { Thread.sleep(rest-(et-st)); } catch(Exception e){} }} if(isEndGame) //判断是否输掉游戏 { if(islose) { EedGameTime= System.currentTimeMillis(); g.setColor(0,0,0); g.fillRect(0,0,getWidth(),getHeight()); g.setColor(255,255,0); g.setFont(Font.getFont(Font.FACE_PROPORTIONAL,Font.STYLE_PLAIN,Font.SIZE_LARGE)); g.drawString(“你失败了!”,getWidth()/2-40,getHeight()/2-20,Graphics.TOP|Graphics.LEFT); flushGraphics(); try{ Thread.sleep(3000); } catch(Exception e){} // init_Game(); break; }}} if(isEndGame) //判断是否赢得游戏 { if(!islose) { EedGameTime= System.currentTimeMillis(); g.setColor(0,0,0); g.fillRect(0,0,getWidth(),getHeight()); g.setColor(255,255,0); g.setFont(Font.getFont(Font.FACE_PROPORTIONAL,Font.STYLE_PLAIN,Font.SIZE_LARGE)); g.drawString(“你胜利了!”,getWidth()/2-40,getHeight()/2-20,Graphics.TOP|Graphics.LEFT); flushGraphics(); try{Thread.sleep(3000); } catch(Exception e){} // init_Game(); } // GetScore(); if (score > 0) { GameName in = new GameName(); GameMenu.display.setCurrent(in); in.start(); } else { GameMenu.current = GameMenu.MY_MENU; GameMenu.show(); }} } } 在线程中,通过 1、调用创建关卡的方法CREAT_STAGE(); 2、判断是否按下暂停while(ispause) 3、调用按键方法INPUT_KEY(); 4、调用各种判断和行动的方法PENG_ZHUANG(); 5、调用刷新画面的方法movMing(); 6、判断是否输掉游戏if(isEndGame) 7、判断是否赢得游戏if(isEndGame) 来对游戏进行不断监控。 Sprite类是继承自Layer的用于存储多帧的基本可视元素。不同的frame可交相显示,构成动态的效果。图片可翻转、颠倒、由一个主角图片就可以方便的得到所有方向的显示状态,相比原先只能使用Canvas绘图,需要将所有方向的主角图像都绘制在png图像中简化了许多。Sprite也可以从整合的图像中读图,读图时将把大图分解为若干等宽等高的小图。每个小图按照其排列顺序有相应的序号,在程序中调用其序号,就可以绘制出相应的图片。本程序中的玩家、怪物都由Sprite继承得到。在有些情况下,控制主角的翻转,尤其是多幅图片配合显示的过程,如果将多图的共享定位点设置在通常的左上角,将很不容易控制,因为许多翻转都是以其他点为参考电的(比如,中心点)。由此,引入参考点的概念。参考点由defineReferencePixel函数确定未翻转图片状态时的坐标。默认是(0,0)点,如果需要,可将参考点设置在画面边界之外。 在本程序中,由于J2ME中提供的Sprite类功能有限,所以我自己写了一个GamePlayer类来继承Sprite类,扩充了更多的功能。 public class GamePlayer extends Sprite { } 在游戏中和背景的碰撞检测始终是个难点,在本程序中是这样实现的: public void isPeng(int x,int y,int Array[][],int N) { for(int i = 0; i < 20; i++) { for(int j = 0;j< 18; j++) { if(Array[i][j]== N) { if(!MyGame.isU) { if(Length * j <= x + this.getWidth() /2 && Length *(j + 1) >= x +this.getWidth() /2 && 10 +Length * i <= y && 10 +Length *(i + 1) >= y) { this.setPosition(this.getX(), 20 + Length *(i + 1)); System.out.println(“UP:”+getY()); } //左 if(Length * j <= x + 1 && Length *(j + 1) >= x + 1 && 10 +Length * i <= y + this.getHeight()/2 && 10 + Length *(i + 1) >= y+ this.getHeight()/2) { this.setPosition(Length * (j + 1),this.getY()); } if(Length * j <=x + this.getWidth() - 1 && Length *(j + 1) >= x +this.getWidth() –1 && 10 + Length * i <= y + this.getHeight()/2&& 10 + Length *(i + 1) >= y + this.getHeight()/2) { this.setPosition(Length* j - this.getWidth() ,this.getY()); } } //左下 if(Length * j <= x + 3&& Length *(j + 1) >= x + 3 && 10 +Length * i <= y + this.getHeight() && 10 + Length *(i + 1) >= y +this.getHeight()) { this.setPosition(this.getX(), Length * (i -1)); } //右下 if(Length * j <= x +this.getWidth() -3 && Length *(j + 1) >= x +this.getWidth() -3 && 10 +Length * i <= y + this.getHeight() && 10 + Length *(i + 1) >= y +this.getHeight()) { this.setPosition(this.getX(), Length * (i - 1)); Length *i:+Length * (i-1)); } } } 通过查看背景数组和玩家坐标,来实现和背景的碰撞检测。 声音效果是一个游戏中必不可少的部分,没有了生动的音乐效果,游戏的体验就会大打折扣。在我们的游戏中当然也不能没有声音。 在我们的游戏程序中的PlayMusic类就是游戏中的音效类。该类的主要功能为使用MIDP2.0 Media API播放游戏中的声音效果:背景音乐和游戏结束的提示音。 我们用private Player createPlayer(Stringfilename, String f, int i) 创建播放器。该方法中的filename是要播放声音文件的相对路径,f是播放音乐文件的格式,i用来控制该音乐的播放次数。在播放某个声音之前我们都需要调用stop方法来中止声音(如果声音未播放则该方法无效)。游戏中的声音我们用的startPlayer方法来播放。在需要播放游戏中的声音时我们就调用该类中相应的方法。 源文件
4.1游戏的的思路、构想
4.1.1游戏想法的产生
4.1.2对游戏设计的初步认识
4.1.3模块成型阶段
4.2 程序的类结构
4.3 游戏的流程图
4.4.1主类GameMIDlet的实现
4.4.2游戏画布MyGame类的实现
4.4.3玩家精灵GamePlayer类的实现
4.4.5SoundEffects类的实现
版权所有© 帮我毕业网 并保留所有权利