`
kalogen
  • 浏览: 863461 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

J2ME游戏设计框架

    博客分类:
  • J2me
 
阅读更多

  因为移动设备运行速度问题,J2ME开发比较注重程序的复杂度。为了寻求时间复杂度与空间复杂度的一个平衡,通常将程序分为多个页面。多个页面就需要一个管理工具。本文展示了一种经典的J2ME中管理多个页面的调度器。

基础结构

为每一个页面编写一个类,在每一个类中实现以下方法

//无参的构造方法
//执行的run方法
public void run() {}
//绘图的paint方法,参数为屏幕的画笔
public void paint(Graphics g) {}
//按键时的响应方法,参数为按键消息码
public void keyPressed(int key) {}

调度器应该继承Canvas类,实现Runnable接口,完成以下功能

1、changeState方法

负责一个页面到另一个页面的切换。为了节约资源,应该根据旧的状态量释放当前页面,再根据请求的新状态创建另一个页面。

2、run方法

根据状态量判断当前哪个页面,并调用它(当前页面)的run方法。

3、paint方法

调用当前页面的paint(绘图)方法

4、keyPressed方法

调用当前页面的keyPressed(键盘相应)方法

这些是基本的功能,还可以根据需要添加更多的Canvas类中的方法,实现更多功能。

实现示例

这里演示了ModelLogo,ModelMenu和ModelGame三个页面的切换

public class ZYMIDlet extends MIDlet
{
	static ZYCanvas canvas = null;
	static ZYMIDlet midlet = null;
	/**初始动作 不要在构造函数中进行 可能出现问题
	 * 这主要是因为移动设备的资源性能都有限,
	 * 因此此种程序开发的一个宗旨是“在需要用资源的时候申请,用完立刻释放”
	 * */
	public ZYMIDlet()
	{
		midlet = this;
	}
	
	/**大多数初始化工作是放在startApp中完成
	 * midlet创建的时候 执行完构造函数,会调用此函数,midlet进入活跃状态
	 * midlet从暂停状态 恢复的时候 也会自动调用此函数*/
	public void startApp()
	{
		if(canvas == null)
		{
			canvas = new ZYCanvas();
		}
		Display display = Display.getDisplay(this);
		display.setCurrent(canvas);
	}
	public void pauseApp()
	{}	
	public void destroyApp(boolean unconditional)
	{}
}
class ZYCanvas extends Canvas implements Runnable
{
	public static final byte STATE_LOGO = 0;
	public static final byte STATE_MENU = 1;
	public static final byte STATE_GAME = 2;

	public static int state;

	public ModelLogo modelLogo;
	public ModelMenu modelMenu;
	public ModelGame modelGame;

	public ZYCanvas()
	{
		state = STATE_LOGO;

		//开启线程
		changeState(STATE_LOGO);
	}

	/**改变状态:这里可以做一些 上个状态的模块清除资源工作,和 新状态模块的初始化工作*/
	public static void changeState(byte new_state)
	{
		//可做的事情:清除上一个模块的资源
		state = new_state;
		//可作的事情:创建新状态模块,初始化
	}

	//每80毫秒执行一次
	public void run()
	{
		long time = System.currentTime();
		
		action();
		repaint();	//通知调用paint()函数
		serverPaint();	//强制paint执行

		long dur = System.currentTime() - time;
		long test = 80 - dur
		if(test > 0){
			try{Thread.sleep(test);}catch(Exception e){}
		}
	}
	//可以把要做的事情全部放到一个函数中
	public void action()
	{
		switch(state)
		{
			case STATE_LOGO: modelLogo.run();break;
			case STATE_MENU: modelMenu.run();break;
			case STATE_GAME: modelGame.run();break;		
		}	
	}


	//可以把要做的事情全部放到一个函数中
	public void paint(Graphics g)
	{
		switch(state)
		{
			case STATE_LOGO: modelLogo.paint(g);break;
			case STATE_MENU: modelMenu.paint(g);break;
			case STATE_GAME: modelGame.paint(g);break;		
		}	
	}
	
	public void keyPressed(int key)
	{
		switch(state)
		{
			case STATE_LOGO: modelLogo.keyPressed(key);break;
			case STATE_MENU: modelMenu.keyPressed(key);break;
			case STATE_GAME: modelGame.keyPressed(key);break;		
		}
	}
}

/**封装模块*/
class ModelLogo
{
	public ModelLogo(){}
	public void run()
	{
		//运行自己的逻辑
		//如果这个状态结束可以调用ZYCanvas.changeState()来改变状态
	}
	public void paint(Graphics g){}
	public void keyPressed(int key){}
}
class ModelMenu
{
	public ModelGame(){}
	public void run()
	{
		//运行自己的逻辑
		//如果这个状态结束可以调用ZYCanvas.changeState()来改变状态
	}
	public void paint(Graphics g){}
	public void keyPressed(int key){}
}
class ModelGame
{
	public ModelGame(){}
	public void run()
	{
		//运行自己的逻辑
		//如果这个状态结束可以调用ZYCanvas.changeState()来改变状态
	}
	public void paint(Graphics g){}
	public void keyPressed(int key){}
}

一些问题

通过调度器只需要调用changeState方法就可以在页面直接进行切换。但是实际应用时调度器有一些不足

我们想添加一个页面,需要声明一个状态量,再页面相应的类,然后在调度器每个方法中添加对于新的页面要执行的操作。

很明显的,不符合封装的设计思想。

怎样把调度器封装起来呢?

改进思路

很明显,我们要解决的最主要的问题是当调度器编写时,并不知道未来有一个什么样的页面类将要供调度器调度。

该如何使用未知的类?

我们可以应用java语言反射机制动态加载类来解决这类问题。通常,我们想要创建一个对象,我们采用以下方法

Class c = new Class();

java提供了动态加载类的机制,在运行时通过一个表示类名的字符串查找一个类并加载,还可以产生它的一个实例。使用方法如下

getClass().forName("package.className").newInstance();

只要创建一个调度器对象,再将页面类的类名添加进调度器,调度器使用动态加载类创建页面对象就可以了

改进

通过动态加载类创建的对象是Object类型的,显然这是没有我们想要的run,paint,keyPressed方法的,要通过强制类型转换把Object类型对象装换成含有这些方法的对象。

把它转换成什么类型对象合适,又如何保证用户编写的页面类肯定有这些方法呢?

通过java的接口来保证!

下面编写一个接口,规定用户编写的所有页面必须实现这个接口

package com.shiying.frame;

import javax.microedition.lcdui.Graphics;

//页面接口
public interface Model {
	
	public void run();
	
	public void paint(Graphics g);
	
	public void keyPressed(int key);
}

要使用动态加载类,应该保存每个页面类的类名字符串,同时还要保存每个页面所对应的状态量。最简单的使用集合来保存

private static Vector stateVector = new Vector();
private static Vector nameVector = new Vector();

为了调用当前类的方法,还要保存当前类的状态和对当前类对象的引用,这个页面应该是Model类型

private static Model model = null;
private static byte state;

下面编写一个方法,用来向调度器注册页面

public void addModel(byte stateId, String modelClassName) {
	stateVector.addElement(new Byte(stateId));
	nameVector.addElement(modelClassName);
}

当用户发出切换页面的请求后,应该释放当前页面,并启动新的页面

     /*
	 * 通过状态查找在表中的位置
	 */
	private static int searchState(byte state) {
		for (int n=0; nif (((Byte)Framework.stateVector.elementAt(n)).byteValue() == state) {
				return n;
			}
		}
		return -1;
	}

	/**改变状态:这里可以做一些 上个状态的模块清除资源工作,和 新状态模块的初始化工作*/
	public static void changeState(byte new_state)
	{
		//清除上一个模块的资源
		int index = searchState(state);
		if (index != -1)
			model = null;
		
		//状态转换
		state = new_state;
		
		//创建新状态模块,初始化
		index = searchState(new_state);
		if (index != -1)
		{
			String name = (String)Framework.nameVector.elementAt(index);
			Class c = null;
			try {
				c = Class.forName(name);
				model = (Model) c.newInstance();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}

因为每个时刻至多有一个页面对象,所以不需要判断状态,对页面函数调用就非常简单

     //每80毫秒执行一次
	public void run()
	{
		while (true) {
			long time = System.currentTimeMillis();

			action();
			repaint();	//通知调用paint()函数
			serviceRepaints();	//强制paint执行

			long dur = System.currentTimeMillis() - time;
			long test = 80 - dur;
			if(test > 0){
				try {
					Thread.sleep(test);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	//可以把要做的事情全部放到一个函数中
	public void action()
	{
		if (model != null) {
			model.run();
		}
	}


	//可以把要做的事情全部放到一个函数中
	public void paint(Graphics g)
	{
		if (model != null) {
			model.paint(g);
		}
	}
	
	public void keyPressed(int key)
	{
		if (model != null) {
			model.keyPressed(key);
		}
	}

编写一个启动调度器的方法,需要指明开启的第一个页面

     public void go(byte start) {
		//开启线程 
		state = start;
		changeState(start);
		
		Thread th = new Thread(this);
		th.start();
	}
OK,改装结束,整理一下代码
 

完整代码

//    Main.java    //////////////////////////////////////
package com.shiying.frame;


import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;

import com.shiying.Application.Frame;

public class Main extends MIDlet{

	static Frame canvas = null;
	static Main midlet = null;

	public Main()
	{
		midlet = this;
	}
	
	public void startApp()
	{
		if(canvas == null)
		{
			canvas = new Frame();
		}
		Display display = Display.getDisplay(this);
		display.setCurrent(canvas);
	}
	public void pauseApp()
	{}
	public void destroyApp(boolean unconditional)
	{}
	
	public static void shutDown() {
		midlet.notifyDestroyed();
	}

}


//    Model.java    //////////////////////////////////////
package com.shiying.frame;

import javax.microedition.lcdui.Graphics;

//页面接口
public interface Model {
	
	public void run();
	
	public void paint(Graphics g);
	
	public void keyPressed(int key);
}


//    Framework.java    //////////////////////////////////////
package com.shiying.frame;

import java.util.Vector;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;

public class Framework extends Canvas implements Runnable {

	public static Framework canvas = null;
	
	private static Vector stateVector = new Vector();
	private static Vector nameVector = new Vector();
	
	private static Model model = null;
	private static byte state;
	
	public Framework()
	{
		Framework.canvas = this;
	}
	
	public void go(byte start) {
		//开启线程 
		state = start;
		changeState(start);
		
		Thread th = new Thread(this);
		th.start();
	}
	
	public void addModel(byte stateId, String modelClassName) {
		stateVector.addElement(new Byte(stateId));
		nameVector.addElement(modelClassName);
	}
	
	/*
	 * 通过状态查找在表中的位置
	 */
	private static int searchState(byte state) {
		for (int n = 0; n < Framework.stateVector.size(); n++) {
  			 	if (((Byte) Framework.stateVector.elementAt(n)).byteValue() == state) {
  			  	return n;
  			 }
  		  }
		return -1;
	}

	/**改变状态:这里可以做一些 上个状态的模块清除资源工作,和 新状态模块的初始化工作*/
	public static void changeState(byte new_state)
	{
		//清除上一个模块的资源
		int index = searchState(state);
		if (index != -1)
			model = null;
		
		//状态转换
		state = new_state;
		
		//创建新状态模块,初始化
		index = searchState(new_state);
		if (index != -1)
		{
			String name = (String)Framework.nameVector.elementAt(index);
			Class c = null;
			try {
				c = Class.forName(name);
				model = (Model) c.newInstance();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}

	//每80毫秒执行一次
	public void run()
	{
		while (true) {
			long time = System.currentTimeMillis();

			action();
			repaint();	//通知调用paint()函数
			serviceRepaints();	//强制paint执行

			long dur = System.currentTimeMillis() - time;
			long test = 80 - dur;
			if(test > 0){
				try {
					Thread.sleep(test);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	//可以把要做的事情全部放到一个函数中
	public void action()
	{
		if (model != null) {
			model.run();
		}
	}


	//可以把要做的事情全部放到一个函数中
	public void paint(Graphics g)
	{
		if (model != null) {
			model.paint(g);
		}
	}
	
	public void keyPressed(int key)
	{
		if (model != null) {
			model.keyPressed(key);
		}
	}
}
 

如何使用

首先编写的所有页面需要实现Model接口,并且具有公有的无参构造方法

package com.shiying.Application;

import javax.microedition.lcdui.Graphics;

import com.shiying.frame.Model;

/*
 * 这是一个页面的示例,每一个页面必须实现com.shiying.frame.Model接口
 */
public class ModelDemo implements Model {
	
	/*
	 * 页面必须具有一个共有的无参构造方法
	 */
	public ModelDemo() {
		
	}

	/*
	 * 这个方法中添加页面对按键消息的响应过程
	 */
	public void keyPressed(int key) {
		// TODO Auto-generated method stub
		
	}

	/*
	 * 这个方法实现页面绘图过程
	 */
	public void paint(Graphics g) {
		// TODO Auto-generated method stub
		System.out.println("Demo paint");
		g.drawString("Frame Demo", Frame.canvas.getWidth() / 2, 100,
				Graphics.HCENTER|Graphics.TOP);
	}

	/*
	 * 这个方法实现页面要执行的操作
	 */
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("Demo run");
	}

}
下面编写一个调度器类,这个类是Framework类的子类
这个类需要完成的工作有
1、声明每个页面的状态量
2、注册页面类
3、启动第一个页面
package com.shiying.Application;

import com.shiying.frame.Framework;



public class Frame extends Framework {
	
	/*
	 * 这里声明代表每个页面的状态量(页面ID) 
	 */
	public static final byte STATE_DEMO = 0;
	

	public Frame() {
		super();
		
		/*
		 * 这里添加页面,需要传入两个参数,页面ID和相应页面类所在位置
		 */
		this.addModel(Frame.STATE_DEMO, "com.shiying.Application.ModelDemo");
		
		
		/*
		 * 这里设置第一个活跃的页面
		 */
		this.go(Frame.STATE_DEMO);
	}
	
}
 当这个类被创建时,第一个页面会被自动调用。在第一个页面中可以根据需要调用changeState可以切换到下一个页面。
原文地址:http://www.cnblogs.com/S-E-P/archive/2010/10/11/2045052.html
分享到:
评论

相关推荐

    基于JAVA基于J2ME的手机游戏开发的毕业设计,游戏采用了J2ME的游戏框架MIDP2.0,使用了J2ME的游戏引擎LWJGL

    游戏设计了5个游戏场景,分别是森林场景、海洋场景、城堡场景、火车场景和宇宙场景。每个场景都设计了不同的游戏角色、游戏物品和游戏敌人。游戏角色可以在不同场景中切换,实现了游戏的连贯性。游戏物品和游戏敌人的...

    基于J2ME的手机游戏框架设计与优化.pdf

    基于J2ME的手机游戏框架设计与优化.pdf

    嵌入式系统/ARM技术中的基于MVC模式的J2ME应用程序框架设计

    1 J2ME应用程序框架的现状 Sun公司在1999年6月推出了J2ME(Java 2 MicroEdition,Java 2袖珍版)。J2ME是专门为那些使用有限电源、有限网络连接以及有限图形用户界面能力的设备开发的,满足了消费电子和嵌入式设备...

    java源码包33个实例源码阳光酒店管理系统手机游戏J2ME毕业设计书籍管理系统网络电视源代码TV

    手机游戏J2ME毕业设计.rar 数据持久层框架 Hibernate.zip 数据结构提取器.rar 文件压缩解压缩包 Commons Compress.rar 日历同步统计 GCALDaemon.zip 物业管理系统毕业设计+源码.rar 用iText类库制作PDF文档.rar 用...

    JAVA基于J2ME的手机游戏开发的实现.rar

    游戏设计与规划: 设计游戏的玩法、关卡、角色、场景等要素,制定游戏开发的整体计划和流程。 考虑到手机设备的屏幕尺寸、输入方式等特点,设计简洁明了、易于操作的游戏界面。 选择游戏引擎: 根据游戏的需求和...

    机器猫--j2me游戏源码

    这个可是我花了一个星期,用的公司的框架设计的,游戏简单,代码一点都不简单的,不信你瞧瞧

    论文研究-基于J2ME技术的手机网络游戏研究与开发.pdf

    手机游戏开发基于J2ME技术基础,以牌类游戏为实例,采用了MVC框架模式进行设计,提高了系统的可维护性,有利于系统的二次开发;基于Java的特性使该系统具有较高的可移植性,可在主流手机平台中正常运行,减少移植...

    JAVA基于J2ME的手机游戏开发(论文+源代码).rar

    1、手机游戏开发以及J2ME的基本理论和J2ME类库的使用; 2、J2ME体系结构的研究; 3、MIDP移动信息设备简表的研究; (1)、MIDP的目标硬件环境; (2)、MIDP应用程序; (3)、CLDC和MIDP库中的类。 4、J2ME API...

    计算机毕业设计论文题目大全.doc

    "JAVA 打飞机游戏设计 "Eclipse "SQL Server " "JAVA 电子通讯录(带系统托盘) "Eclipse "SQL Server " "JAVA 多线程与线程安全实践-基于Http协 "Eclipse "SQL Server " "议的断点续传 " " " "JAVA 个人理财 ...

    java源码包实例源码JAVA开发源码55个合集.zip

    手机游戏J2ME毕业设计.rar 打地鼠游戏.rar 数据持久层框架 Hibernate.zip 数据结构提取器.rar 文件压缩解压缩包 Commons Compress.rar 日历同步统计 GCALDaemon.zip 物业管理系统毕业设计+源码.rar 电子书店管理系统...

    java源码包实例源码JAVA开发源码50个合集.zip

    基于J2ME的Java游戏梦幻炸弹人源程序.rar 基于JAVA的ICQ系统.rar 基于Java的mp3播放器源代码.rar 基于Java的小型人事管理系统,带数据库.rar 基于JAVA的日程提醒簿.rar 基于Java的邮件服务器源程序.rar 基于MVC的...

    java源码包---java 源码 大量 实例

    内容索引:JAVA源码,综合应用,J2me游戏,PNG,图形处理  这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失。使用时候只需在控制台窗口执行...

    java源码包2

    内容索引:JAVA源码,综合应用,J2me游戏,PNG,图形处理  这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失。使用时候只需在控制台窗口执行...

    java源码包3

    内容索引:JAVA源码,综合应用,J2me游戏,PNG,图形处理  这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失。使用时候只需在控制台窗口执行...

    java源码包4

    内容索引:JAVA源码,综合应用,J2me游戏,PNG,图形处理  这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失。使用时候只需在控制台窗口执行...

    java源码包JSP实例源码JAVA开发源码65个合集.zip

    手机游戏J2ME毕业设计.rar 打地鼠游戏.rar 数据持久层框架 Hibernate.zip 数据结构提取器.rar 文件压缩解压缩包 Commons Compress.rar 日历同步统计 GCALDaemon.zip 物业管理系统毕业设计+源码.rar 用iText类库制作...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    J2ME优化压缩PNG文件 4个目标文件 内容索引:JAVA源码,综合应用,J2me游戏,PNG,图形处理 这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失...

Global site tag (gtag.js) - Google Analytics