浙江万里学院2012 /2013 学年第 一 学期
《J2ME开发技术》大作业
题目:角色扮演类游戏RPG(Role Play Game)游戏
《MM历险记》
组长:XXX
组员:XXX XXX|
小组分工:
package mmrpg; package mmrpg; package mmrpg; package mmrpg; package mmrpg; package mmrpg; package mmrpg; 代码调试及修改实验报告
-- 行动 --界面布局 --按钮控制 --用户界面 --NPC设置 --事件管理 --主应用程序
目 录
1 摘要…………………………………………………………………………….……. 3
2 设计内容及要求…………………………………………………………………… 3
3 设计过程…………………………………………………………............…………. 4 3.1 设计方案……………………………..................................…………………… 4 3.2 概要设计………………………............................................…………………...5 3.3 界面设计图 ………………........................................………………………… 6 3.4 代码实现………….............................................................................…………...11
4 设计总结….......................................…………………………………………………11
附录:程序源代码…….....................................…………………………………….11
1 、摘要
本文给出了一个基于MIDP的角色扮演类游戏RPG(Role Play Game)游戏设计方案,并给出全部实现源代码。利用J2ME语言编程的思想来完成系统的设计,然后编写出程序设计代码进行界面设计,实现友好的界面交互,具有清晰的程序流程图,最后编程实现了全过程。该游戏的最大特色是屏幕自适应,无论各种手机,PDA的屏幕大小如何,该游戏总是能获得最佳的显示效果。
2 、设计内容和要求
一个完整的RPG游戏至少要有故事情节、人物、NPC(游戏中不受游戏者操控的角色,通常为游戏中城镇、村落的商人等游戏人物通过与之对话进行物品交易或获取信息)、场景四个要素。
3 、设计过程
3.1 设计方案
J2ME语言继承了JAVA语言易学易用的特点,特别适合于初学者学系统编程。随着21世纪信息社会的到来,计算机在人们的工作和生活中的深入,要求我们越来越多地与计算机打交道,为了使用户在繁忙的日程工作中得到放松,于是出现了各种各样的休闲软件,如聊天工具,游戏等等。通过这学期来J2ME课程的学习,我初步掌握了J2ME语言的最基本的知识,于是老师的指导下动手用J2ME编写角色扮演类游戏RPG(Role Play Game)游戏,MM历险记。
RPG要素如下:1、故事情节:一个MM被困在洞穴里,她不懈努力,最终逃出了洞穴。2、人物:MM,是游戏的控制角色。可使用方向键移动MM。3、NPC:游戏中设置若干个NPC,MM通过与之对话,可知道洞穴的出口。4、场景:本游戏要求设计至少2个以上场景,分别是洞穴的第N层。MM可通过阶梯从洞穴的一层进入另一层。每层场景都有阶梯、泥地和石头等区域。其中,有石头的区域被设置成MM不可以通过的区域。
3.2 概要设计
本游戏的操作流程非常简单,用户启动MIDlet启动后,即进入游戏主画面,屏幕开始显示为欢迎画面。用户按下[开始]按钮后,就可以开始玩游戏了,当用户想暂停时,再次按一下[开始]按钮,游戏就暂停了,在暂停的情况下再按[开始]按钮,游戏继续运行。任何时候按[退出]按钮,游戏MIDlet都会终止.
游戏画面流程图如下:
MIDlet选择画面
[启动]按钮 欢迎画面 [退出]按钮 游戏运行画面 [开始]按钮 [退出]按钮
游戏暂停画面
游戏在结束状态
GAME OVER 本程序共有7个java源文件: package mmrpg; -- 行动 package mmrpg; --界面布局 package mmrpg; --主应用程序 package mmrpg; --按钮控制 package mmrpg; --用户界面
package mmrpg; --NPC设置 对话 package mmrpg; --事件管理
3.3界面设计图
paint方法的流程图如下所示:
游戏处于欢迎画面状态 游戏处于Game Over状态 游戏处于暂停状态 游戏处于运行状态 绘制欢迎画面 绘制Game Over 画面 NPC 对话 是 执行空操作 否 继续寻找 否 是否找到出口 是 继续寻找 得到提示 显示对话 显示Game Over 是 Game Over 设置游戏状态为 否 Game Over 继续寻找 Game Over 结 束
(1) 初始化欢迎界面(如图所示)
初始欢迎界面显示了程序初始的一个屏幕
(2)游戏主界面(如图所示)
进入游戏界面后,在游戏窗体中用户就可以使用键盘的方向键来控制MM行走路线
过程中,与NPC对话,得到出口提示,图为NPC1
图为MM与NPC2对话,得到最后提示,知道出口地方
图为,MM找到游戏出口,游戏结束
3.4代码实现
MIDP的游戏设计,本质上就是用一个线程或者定时器产生重绘事件,用线程和用户输入改变游戏状态。这个游戏也不例外,启动MIDlet后,就立即生成一个重绘线程,该线程每隔50ms绘制一次屏幕。当然,重绘时有一些优化措施,并不是屏幕上所有的像素都需要重绘,而是有所选择。
游戏区域
游戏区域为手机屏幕的一部分,该区域为正方形
游戏地图
游戏地图是用来存储游戏容器上的固定地图块。游戏容器为一个宽为6个小地图块单位,高为6个小地图块单位,所以用一6X的二维数组(程序里叫mapdata)来存储固定地图块。
4 、设计总结
由于课程设计时间较短,所以该游戏还有许多不尽如人意的地方,比如NPC 数量少,才两个地图层也只有两个,游戏难度很低,退出游戏不能存储进度等多方面问题。这些都有待进一步改善,如在游戏中还可以更换背景音乐,以适合不同的玩家,在每通过一关可以给玩家播放一段flash,吸引玩家去挑战极限,不断提高玩家的兴趣。
课程设计已经结束了,但在课程设计的这些体会要应用到今后的日常学习中去。在新的学期,我觉得自己要在以下几个方面加以注意:
首先,在学习专业课的时候要注意理论联系实际。注意将课本上的知识应用到日常的操作中,真正做到学以致用。只有这样,才能做到目的明确,才能有足够的学习动力。
其次,在学习过程中要经常与同学进行交流,讨论所遇到的问题,并一起解决。在讨论中解决问题,会节约很多时间,并且在交流的过程中,我们也可以学到更多的东西。
课程设计已经结束了,这次课程设计带给自己很多体会,在以后的学习中要不断总结,不断改进,使自己的成绩有新的提高。
附录:程序源代码
package mmrpg; -- 行动
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas; import javax.microedition.lcdui.game.Sprite;
public class Actor extends Sprite{
private int m_nLastMoveX; private int m_nLastMoveY;
//上次移动的X距离 //上次移动的Y距离
Actor(Image image, int frameWidth, int frameHeight){ super(image, frameWidth, frameHeight); }
//处理按键输入
public void Input( int keyStates ){ int nFrame = getFrame();
if( ( keyStates & GameCanvas.UP_PRESSED ) != 0 ) {//向上运动 Move( 0, -1 );
nFrame ++;
if( nFrame > 1 || nFrame < 0 ) nFrame = 0; setFrame( nFrame );
setTransform( TRANS_NONE ); }
else if( ( keyStates & GameCanvas.RIGHT_PRESSED ) != 0 ) {//向右运动 Move( 1, 0 );
nFrame ++;
if( nFrame > 3 || nFrame < 2 ) nFrame = 2; setFrame( nFrame );
setTransform( TRANS_NONE ); }
else if( ( keyStates & GameCanvas.DOWN_PRESSED ) != 0 ) {//向下运动 Move( 0, 1 ); nFrame ++;
if( nFrame > 5 || nFrame < 4 ) nFrame = 4; setFrame( nFrame );
setTransform( TRANS_NONE );
}
else if( ( keyStates & GameCanvas.LEFT_PRESSED ) != 0 ) {//向左运动 Move( -1, 0 );
nFrame ++;
if( nFrame > 3 || nFrame < 2 ) nFrame = 2;
setFrame( nFrame );
setTransform( TRANS_MIRROR ); }
}
//移动MM,参数nX、nY分别是移动的X、Y距离 private void Move( int nX, int nY ){ m_nLastMoveX = nX;
m_nLastMoveY = nY;
setRefPixelPosition( getRefPixelX() + nX, getRefPixelY() + nY ); }
//使MM向后退 public void MoveBack(){
Move( -m_nLastMoveX, -m_nLastMoveY ); m_nLastMoveX = 0; m_nLastMoveY = 0; } }
package mmrpg; --界面布局
import java.io.IOException; import java.util.Random;
import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas; import javax.microedition.lcdui.game.LayerManager;
public class MainCanvas extends GameCanvas implements Runnable{ public static final int GAME_UI = 0; //进入用户界面 public static final int GAME_GAMING = 1; //进行游戏 public static final int GAME_END public static final int GAME_STATE_NUM private int m_nState 态
= 2; //游戏结束 = 3; //状态总数
//存储当前的游戏状
= GAME_UI;
private boolean
m_bRunning;
//控制线程运行 //界面对象
//层管理器对象 //MM对象 //NPC对象
private MyUI m_UI;
private LayerManager m_Manager; private Actor m_Actor; private Npc m_Npc;
private Scene m_Scene;
private Random m_Random; public MainCanvas( ){ super(true);
//场景对象 //随机数对象
try{
//创建用户界面,并设置界面中的按钮 MyButton button = new MyButton(); m_UI = new MyUI(button); }
catch(Exception exception){exception.printStackTrace();} setState(GAME_UI); Start();
}
//重新开牌
private void Reset(){
//创建随机数对象
if( m_Random == null )
{ m_Random = new Random();}
if( m_Actor == null ){ try{
//读取MM及NPC的精灵图像
Image image = Image.createImage(\"/MM.png\"); m_Actor = new Actor( image, 16, 32 );
m_Actor.defineCollisionRectangle( 0, 16, 16, 16 ); m_Actor.defineReferencePixel( 8, 24 ); image = Image.createImage(\"/NPC.png\"); m_Npc = new Npc( image, 16, 32 );
m_Npc.defineCollisionRectangle( 0, 16, 16, 16 ); m_Npc.defineReferencePixel( 8, 24 ); //创建场景
m_Scene = new Scene(); //创建层管理器
m_Manager = new LayerManager(); }
catch (IOException e){ e.printStackTrace();} }
m_Manager.append( m_Actor );
m_Manager.append( m_Npc );
m_Scene.AppendToManager( m_Manager ); m_Scene.EnterScene( 0, m_Actor, m_Npc ); //设置可视区域
SetViewWindow(); }
//设置可视区域
private void SetViewWindow(){
int nX = m_Actor.getRefPixelX() - getWidth()/2; int nY = m_Actor.getRefPixelY() - getHeight()/2; if( nX < 0 )
nX = 0;
else if( nX > m_Scene.GetWidth() - getWidth() ) nX = m_Scene.GetWidth() - getWidth(); if( nY < 0 )
nY = 0;
else if( nY > m_Scene.GetHeight() - getHeight() ) nY = m_Scene.GetHeight() - getHeight();
m_Manager.setViewWindow( nX, nY, getWidth(), getHeight() ); }
private void setState( int state ){
//设置游戏状态
if( state < 0 || state >= GAME_STATE_NUM ) return;
m_nState = state; switch( m_nState ){ case GAME_UI: 界面
m_UI.setState(MyUI.UI_TITLE); 界面
break;
case GAME_GAMING: m_UI.setState(MyUI.UI_HIDE); break; case GAME_END: break; } }
public void Start(){
m_bRunning = true;
Thread thread = new Thread(this); //分配新线程 thread.start(); //线程启动
//进入用户//显示标题
//正在游戏
//游戏结束
m_UI.setState(MyUI.UI_HIDE);
}
public void run() {
//得到系统当前时间,并将时间换算成毫秒 long T1 = System.currentTimeMillis(); long T2 = T1; while(m_bRunning){
T2 = System.currentTimeMillis(); if( T2 - T1 > 100 ){ //间隔100毫秒 T1 = T2; Input(); Logic(); Paint();
} } }
public void Stop(){
m_bRunning = false; }
public void Input(){
int keyStates = getKeyStates(); try{
switch( m_nState ){ case GAME_UI: {
switch( m_UI.Input( keyStates ) ){ case MyButton.BUTTON_START: //按下开始键 Reset();
setState(GAME_GAMING);
break;
case MyButton.BUTTON_EXIT: 按下退出键
MMRpgMidlet.midlet.notifyDestroyed(); break; } }
break;
case GAME_GAMING:
//游戏中
//
if( !m_Npc.m_bDialog )
m_Actor.Input( keyStates );
if( m_Scene.m_nCurIndex == 0 &&
m_Actor.getRefPixelX() > 250 &&
m_Actor.getRefPixelY() > 250 ){ setState(GAME_END); return; }
CheckCollisions(); SetViewWindow(); break;
case GAME_END:
if( ( keyStates & GameCanvas.FIRE_PRESSED ) != 0 ){ Reset();
setState(GAME_GAMING); } break; }
}catch(Exception e){e.printStackTrace();}
}
public void Logic(){
switch( m_nState ){ case GAME_GAMING: m_Npc.Logic(); break; } }
public void Paint(){
Graphics g = getGraphics(); //清屏
g.setColor(0);
g.fillRect( 0, 0, getWidth(), getHeight() );
g.setColor(0xFFFF0000); switch( m_nState ){ case GAME_UI:
//显示界面
m_UI.Paint(g, getWidth(), getHeight()); break;
case GAME_GAMING:
//显示游戏画面
m_Manager.paint( g, 0, 0 ); if( m_Npc.m_bDialog )
m_Npc.DrawText( g, getWidth(), getHeight() ); break; case GAME_END:
m_Manager.paint( g, 0, 0 );
g.setColor( 0xffffff );
g.drawString( \"MM终于走出了洞穴!\ getHeight() / Graphics.TOP|Graphics.HCENTER ); break; }
flushGraphics(); }
//碰撞检测
public void CheckCollisions(){
if( m_Actor.collidesWith( m_Npc, false ) ){ m_Actor.MoveBack(); m_Npc.DialogStart(); return; }
m_Scene.CollidesWidth( m_Actor, m_Npc ); } }
package mmrpg; --主应用程序
import javax.microedition.lcdui.Display; import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
public class MMRpgMidlet extends MIDlet { public static MMRpgMidlet midlet;
private static MainCanvas m_MainCanvas; //定义MainCanvas的引用 public MMRpgMidlet() {
super(); //初始化,继承MIDlet类的构造 midlet = this;
}
protected void startApp() throws MIDletStateChangeException { //程序开始运行,为m_MainCanvas分配存储空间
try{
m_MainCanvas = new MainCanvas(); //分配存储空间
}
catch (Exception ex){ex.printStackTrace(); } 误处理
//设m_MainCanvas为屏幕的当前画布
Display.getDisplay(this).setCurrent(m_MainCanvas);
2,
//不做错
}
protected void pauseApp() {
//由被呼叫或其他原因使程序暂停 m_MainCanvas.Stop(); }
protected void destroyApp(boolean arg0)
throws MIDletStateChangeException { m_MainCanvas.Stop(); }
}
package mmrpg; --按钮控制
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas; import javax.microedition.lcdui.game.Sprite;
public class MyButton {
//定义一组表示按钮类型值
public static final int BUTTON_START = 0; //开始按钮 public static final int BUTTON_EXIT = 1; //退出按钮
public static final int BUTTON_TYPE_NUM = 2; //按钮种类总数
private int m_nType 型
private Sprite
//构造方法
public MyButton(){
try{//创建按钮
Image img = Image.createImage(\"/button.png\"); m_ButtonSp = new Sprite(img, 41, 21); }
catch (Exception ex){ex.printStackTrace();} }
//处理按键的输入,返回选择的按键类型 public int Input(int keyStates){
if( ( keyStates & GameCanvas.UP_PRESSED ) != 0 ) {//选择上一个按钮
if( m_nType > 0 ) m_nType --; }
= BUTTON_START;
//当前选择的按钮类
m_ButtonSp; //按钮精灵图像
if( ( keyStates & GameCanvas.DOWN_PRESSED ) != 0 ) {//选择下一个按钮
if( m_nType < BUTTON_TYPE_NUM - 1 ) m_nType ++; }
if( ( keyStates & GameCanvas.FIRE_PRESSED ) != 0 ) {//按下当前选择的按钮 return m_nType; }
return -1; 何按钮
}
//显示按钮
public void Paint(Graphics g, int scrWidth, int scrHeight){ int h = m_ButtonSp.getHeight() + 2; int x = 15;
int y = 15;
for( int n = 0; n < BUTTON_TYPE_NUM; n ++ ) {
if( m_nType == n ) {//显示高亮的按钮
m_ButtonSp.setFrame(n); } else
{//显示灰色按钮
m_ButtonSp.setFrame(n + BUTTON_TYPE_NUM); }
m_ButtonSp.setPosition(x, y); m_ButtonSp.paint(g); y = y + h; } }
}
package mmrpg; --用户界面
import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image;
public class MyUI {
//定义一组表示界面显示状态的数值
public static final int UI_TITLE = 0; //显示标题画面 public static final int UI_HIDE = 1; //隐藏界面 public static final int UI_STATE_NUM = 2; //界面状态总数
//返回-1表示未选择任
private int m_nState = UI_TITLE; //当前的界面状态
private Image m_TitleImg; //标题画面图像 private MyButton m_Button; //按钮对象
public MyUI( MyButton button ){
m_Button = button; try{ //读取标题图像
m_TitleImg = Image.createImage(\"/title.png\"); }
catch (Exception ex){ex.printStackTrace();} }
/*****
* 获取当前的界面状态 * @返回:当前界面状态 */
public int getState(){
return m_nState; }
/*****
* 设置当前的界面状态,并进行出错处理 * @参数 nType.........要设置的状态
*/
public void setState( int state ){
if( state < 0 || state >= UI_STATE_NUM ) return; m_nState = state; }
public MyButton getButton(){ return m_Button;
}
/*****
* 处理按键的输入
* @参数 keyStates.....当前的按键状态 * @返回所选按钮的类型 */
public int Input( int keyStates ){ int type = -1;
switch( m_nState ){ case UI_TITLE:
type = m_Button.Input(keyStates); break; case UI_HIDE: break;
}
return type; }
/*****
* 显示界面内容
* @参数 g.............对应显示屏幕 * @参数 scrWidth......屏幕的宽 * @参数 scrHeight.....屏幕的高 */
public void Paint( Graphics g, int scrWidth, int scrHeight ){ int x = scrWidth; int y = scrHeight; switch( m_nState ){ case UI_TITLE:
//显示标题画面
if( m_TitleImg != null ){
x = ( x - m_TitleImg.getWidth() ) / 2; y = ( y - m_TitleImg.getHeight() ) / 2; g.drawImage(m_TitleImg, x, y, 0 ); }
m_Button.Paint(g, scrWidth, scrHeight); break; case UI_HIDE: break; } }
}
package mmrpg; --NPC设置 对话
import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;
public class Npc extends Sprite{
public boolean m_bDialog = false; private String m_strText = null;
//隐藏界面
//是否正在对话 //对话的文字 //对话开始的时间
private long m_nDialogStartTime = 0;
Npc(Image image, int frameWidth, int frameHeight){ super(image, frameWidth, frameHeight); }
//设置对话的文字
public void SetText( String strText ){ m_strText = null;
m_strText = new String(strText);
}
//开启对话
public void DialogStart(){
m_nDialogStartTime = System.currentTimeMillis(); m_bDialog = true; }
//逻辑操作
public void Logic(){
if( !m_bDialog ) return;
long lTime = System.currentTimeMillis(); if( lTime - m_nDialogStartTime > 3000 ) m_bDialog = false; }
//显示对话文字
public void DrawText( Graphics g, int nWidth, int nHeight ) {
if( !m_bDialog ) return; g.setColor( 0 );
g.fillRect( 0, nHeight - 30, nWidth, 30 ); g.setColor( 0xffffff );
g.drawRect( 0, nHeight - 30, nWidth - 1, 29 ); g.drawString( m_strText, 10, nHeight - 25,
Graphics.TOP|Graphics.LEFT); } }
package mmrpg; 事件管理
import java.io.IOException; import java.io.InputStream;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.LayerManager; import javax.microedition.lcdui.game.TiledLayer;
public class Scene {
public int m_nCurIndex;
private TiledLayer m_LyPass; private TiledLayer m_LyNotPass; private TiledLayer m_LyJump; Scene( ){ try{
//可通过的区域 //不可通过的区域
//跳转场景的区域
//读取tile图像
Image image = Image.createImage(\"/map.png\"); m_LyPass = new TiledLayer( 10, 10, image, 32, 32 ); m_LyNotPass = new TiledLayer( 10, 10, image, 32, 32 ); m_LyJump = new TiledLayer( 10, 10, image, 32, 32 );
}catch (IOException e){e.printStackTrace();} }
//将各图层加入到层管理器
public void AppendToManager( LayerManager mManager ){ mManager.append( m_LyPass ); mManager.append( m_LyNotPass ); mManager.append( m_LyJump ); }
//进入指定场景
//nIndex是场景的编号,mActor、nNpc分别是MM和NPC对象 public void EnterScene( int nIndex, Actor mActor, Npc mNpc ){ m_nCurIndex = nIndex; InputStream is = null;
//根据场景,设置MM和NPC的位置及图像 switch( nIndex ) { case 0:
is = getClass().getResourceAsStream(\"/map0.txt\"); mActor.setRefPixelPosition( 150, 150 ); mNpc.setFrame( 0 );
mNpc.SetText( \"洞穴上层有个人知道出口!\" );
mNpc.setRefPixelPosition( 130, 120 );
break; default:
is = getClass().getResourceAsStream(\"/map1.txt\");
mActor.setRefPixelPosition( 200, 160 ); mNpc.setFrame( 1 );
mNpc.SetText( \"洞穴出口在下层的右下角。\" ); mNpc.setRefPixelPosition( 150, 50 );
break; }
//读取场景 LoadScene( is ); }
//获取场景的宽度 public int GetWidth(){
return m_LyPass.getWidth(); }
//获取场景的高度
public int GetHeight(){
return m_LyPass.getHeight(); }
//检测MM和场景及NPC的碰撞
public void CollidesWidth( Actor mActor, Npc mNpc ){ if( mActor.collidesWith( m_LyJump, false ) ) {
mActor.MoveBack(); if( m_nCurIndex == 0 )
EnterScene( 1, mActor, mNpc ); else
EnterScene( 0, mActor, mNpc ); }
else if( mActor.collidesWith( m_LyNotPass, false ) ) {
mActor.MoveBack(); } }
//读取场景文件
private void LoadScene( InputStream is ){ try{
int ch = -1;
for( int nRow = 0; nRow < 10; nRow ++ ) {
for( int nCol = 0; nCol < 10; nCol ++ ) {
ch = -1;
while( ( ch < 0 || ch > 7 ) ) {
ch = is.read(); if( ch == -1 ) return; ch = ch - '0'; }
if( ch == 4 || ch == 5 ) {//不可通过的区域
m_LyPass.setCell( nCol, nRow, 0 ); m_LyNotPass.setCell( nCol, nRow, ch );
m_LyJump.setCell( nCol, nRow, 0 ); }
else if( ch == 6 || ch == 7 ) {//跳转场景的区域
m_LyPass.setCell( nCol, nRow, 0 );
m_LyNotPass.setCell( nCol, nRow, 0 );
m_LyJump.setCell( nCol, nRow, ch ); } else
{//可通过的区域
m_LyPass.setCell( nCol, nRow, ch ); m_LyNotPass.setCell( nCol, nRow, 0 );
} }
m_LyJump.setCell( nCol, nRow, 0 ); } } } }
catch (IOException e){e.printStackTrace();}
因篇幅问题不能全部显示,请点此查看更多更全内容