J2ME飞行射击类游戏一

实例说明:

1.实例规则

玩家控制一架飞机(该项目用一矩形代替大笑);按手机方向键可以控制飞机上下左右移动,但飞机不能出屏幕,按中间键可向前方发射子弹,按新号键,飞机发射炮弹的火力会逐渐增强,当炮弹触屏后会自动消失。

2.实例效果:

该实例在模拟器上的运行效果如下:

飞机的初始火力

按*号键后的火力

3.类设计:

(1)com.main包下的MainMidet主类和MyCanvas画布类;

①.MainMidlet主类:

package com.main;

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

public class MainMidlet extends MIDlet {

	public MainMidlet() {
		//显示游戏画布类
		Display.getDisplay(this).setCurrent(new MyCanvas());
	}

	public void exit() {
		try {
			destroyApp(true);
		} catch (MIDletStateChangeException e) {
			e.printStackTrace();
		}
		this.notifyDestroyed();// 通知结束应用程序
	}

	protected void destroyApp(boolean arg0) throws MIDletStateChangeException {

	}

	protected void pauseApp() {
		// 由被呼叫或其他原因使程序暂停
	}

	protected void startApp() throws MIDletStateChangeException {

	}

}

②.MyCanvas游戏画布类
package com.main;

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

import com.data.FontLib;
import com.data.ScreenData;
import com.stage.GameStage;
import com.stage.SuperStage;

public class MyCanvas extends Canvas {
	private Image backscreen;//后台屏幕
	private Graphics backgraphics;//后台画笔
	public static final int FPS = 20;//游戏每秒刷新率
	private SuperStage stage;//当前舞台对象的引用
	private int key;//用户按键的键值
	private int frame;	
	
	private long frametimestar = 0;
	private int totalframe = 20;
	
	public MyCanvas() {
		this.setFullScreenMode(true);
		key = -100;
		backscreen = Image.createImage(ScreenData.SCREENWIDTH,ScreenData.SCREENHEIGHT);
		backgraphics = backscreen.getGraphics();
		
		stage = new GameStage(this);//默认为GameStage
		
		//启动游戏线程
		new GameThread().start();
	}

	protected void paint(Graphics g) {
		g.drawImage(backscreen, 0, 0, 20);//将后台屏幕渲染到前台设备
	}

	//将游戏元素绘制在后台屏幕上
	public void render(){
		frame++;
		backgraphics.setColor(-1);//设置颜色为白色
		backgraphics.fillRect(0, 0, ScreenData.SCREENWIDTH, ScreenData.SCREENHEIGHT);		
		
		stage.draw(backgraphics);//渲染舞台		
		
		if(frametimestar == 0){
			frametimestar = System.currentTimeMillis();
		}
		if(System.currentTimeMillis() - frametimestar >= 1000){
			totalframe = frame;
			frame = 0;
			frametimestar = 0;
		}
		
		drawFPS();
	}
	
	private void drawFPS(){//将每秒刷新的频率(每秒刷新的帧数)绘到屏幕上以便观察和衡量游戏的刷新速度;
		backgraphics.setColor(0xffff0000);//设置画笔的颜色
		Font defaultfont = backgraphics.getFont();//得到设画笔的字体
		backgraphics.setFont(FontLib.MEDIUM);//设置画笔字体
		backgraphics.drawString("FPS:"+totalframe, 0, 0, 20);
		backgraphics.setFont(defaultfont);
	}
	
	public void input(){//接收用户输入
		stage.input(key);//调用游戏舞台接收用户输入的input方法
	}
	
	public void releaseKey(){
		key = -100;//在做单键按下时,用户释放按键,键值无条件归为-100;
	}
	
	public void logic(){//处理游戏逻辑
		stage.logic();//调用游戏舞台的逻辑
	}
	
	protected void keyPressed(int keyCode) {//用户的单键按下事件
		key = keyCode;
	}

	protected void keyReleased(int keyCode) {//用户单键释放事件
		key = -100;
	}

	private class GameThread extends Thread{//游戏线程
		public void run(){
			while(true){
				long start = System.currentTimeMillis();
				input();//用户输入
				logic();//游戏逻辑
				render();//渲染
				repaint();//请求重绘
				long end = System.currentTimeMillis();
				if(end - start <= 1000/FPS){//每秒刷新的次数
					try {
						Thread.sleep(1000/FPS - (end - start));
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}else{
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}


(2)com.data包下的字体接口FontLib和屏幕数据接口ScreenData;

①FontLib常用字体接口:(封装了常用字体);
package com.data;

import javax.microedition.lcdui.Font;
//封装常用字体
public interface FontLib {
	Font SMALL = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL);
	Font MEDIUM = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
	Font LARGE = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_LARGE);
}

②ScreenData屏幕数据接口(封装了手机屏幕的宽高和键盘的按键对应的值);
package com.data;

import javax.microedition.lcdui.Canvas;

public interface ScreenData {
	int SCREENWIDTH = 240;//屏幕宽
	int SCREENHEIGHT = 320;//屏幕高
	//左右上下及*号键
	int LEFT = -3;
	int RIGHT = -4;
	int UP = -1;
	int DOWN = -2;
	int STAR = Canvas.KEY_STAR;
}

(3)com.statge游戏舞台类的设计:

设计思想:每一个游戏都应该有游戏菜单,游戏帮助,游戏设置,游戏大厅界面;每个界面好比是一个舞台,用户在游戏菜单,玩游戏,游戏帮助,游戏设置中的切换,可以看作是舞台之间的

相互切换;所有的游戏舞台可以抽象出一个父类(SuperStage);父类中有一个游戏画布Mycanvas类对象的引用,并且包含了以下抽象方法:初始化舞台(init());处理逻辑(logic);处理舞台按键事件(input());绘制舞台(draw());释放舞台资源(gc());

①SuperStage舞台抽象类的设计

package com.stage;

import javax.microedition.lcdui.Graphics;

import com.main.MyCanvas;

public abstract class SuperStage {
	protected MyCanvas canvas;

	public SuperStage(MyCanvas canvas) {// 每一个舞台都A
		this.canvas = canvas;
	}

	public abstract void init();// 初始化舞台

	public abstract void gc();// 释放舞台资源

	public abstract void logic();// 处理逻辑

	public abstract void draw(Graphics g);// 绘制舞台

	public abstract void input(int key);// 处理舞台按键事件
}


②子类游戏舞台类(GameStage)的设计;

这里只写了GameStage类继承SuperStage;以后可以根据游戏的具体情况设置相应的舞台类;如(菜单舞台(MenuStage),设置舞台(SetStage)),GameStage类的代码具体如下:

package com.stage;

import javax.microedition.lcdui.Graphics;

import com.data.ScreenData;
import com.game.Bullet;
import com.game.Plane;
import com.main.MyCanvas;

public class GameStage extends SuperStage {
	private Plane myplane;//游戏飞机类型的成员变量
	public GameStage(MyCanvas canvas) {
		super(canvas);
		init();
	}

	public void init() {//初始化游戏飞机的位置
		int w = 50;
		int h = 30;
		myplane = new Plane((ScreenData.SCREENWIDTH>>1) - (w >> 1), (ScreenData.SCREENHEIGHT-h-10), w, h);
	}

	public void gc() {//释放资源
		
	}
	
	public void input(int key) {//用户输入并根据相应的情况移动飞机的位置
		if(key == ScreenData.LEFT){
			myplane.move(-5, 0);
			if(myplane.rect.x<=0){
				myplane.rect.x=0;
				return;
			}
		}else if(key == ScreenData.RIGHT){
			myplane.move(5, 0);
			if(myplane.rect.x>=ScreenData.SCREENWIDTH-myplane.rect.w){
				myplane.rect.x=ScreenData.SCREENWIDTH-myplane.rect.w;
				return;
			}
		}else if(key == ScreenData.UP){
			myplane.move(0, -5);
			if(myplane.rect.y<=0){
				myplane.rect.y=0;
				return;
			}
		}else if(key == ScreenData.DOWN){
			myplane.move(0, 5);
			if(myplane.rect.y>=ScreenData.SCREENHEIGHT-myplane.rect.h){
				myplane.rect.y=ScreenData.SCREENHEIGHT-myplane.rect.h;
				return;
			}
		}else if(key == ScreenData.STAR){//增加火力
			myplane.power += 2;
			canvas.releaseKey();
		}
	}
	
	long start = 0;
	
	public void logic() {//更新逻辑
		if(start == 0){
			start = System.currentTimeMillis();
		}
		//更新飞机子弹容器
		if(System.currentTimeMillis() - start >= 100){//每隔100毫秒飞机自动开火
			myplane.fire();
			start = 0;
		}
		//更新子弹的逻辑
		//从容器中取出子弹并移动
		for (int i = 0; i < myplane.bullet_v.size(); i++) {
			Bullet temp = (Bullet) myplane.bullet_v.elementAt(i);
			temp.move(0, -6);
			if(temp.outScreen()){
				temp.visible = false;
				myplane.bullet_v.removeElementAt(i);
				i--;
			}
		}
	}

	public void draw(Graphics g) {
		myplane.draw(g);
		for (int i = 0; i < myplane.bullet_v.size(); i++) {
			Bullet temp = (Bullet) myplane.bullet_v.elementAt(i);
			temp.draw(g);
		}
	}
}


(4)com.gameitem包下的Ractangle类设计:

为什么要设计这个类呢;我们可以这样想,子弹和飞机都有自己的x,y坐标和宽高属性,我们可以将他们的x,y坐标和宽高属性封装成一个矩形;生成一个子弹对象货飞机对象的时候都生成一个矩形对象 存储对应子弹或者飞机的坐标和宽高属性。

Rectangle类的代码如下

package com.gameitem;

import com.data.ScreenData;

public class Rectangle {
	public int x,y;
	public int w,h;
	public Rectangle(int x,int y,int w,int h) {
		this.x = x;
		this.y = y;
		this.w = w;
		this.h = h;
	}
	
	public void move(int dx,int dy){
		this.x += dx;
		this.y += dy;
	}
	public boolean outScreen(){//判断是否出屏
		if(this.x + this.w <= 0 || this.x >= ScreenData.SCREENWIDTH || this.y + this.h <= 0 || 
				this.y >= ScreenData.SCREENHEIGHT)
			return true;
		return false;
	}

}


(5)com.game包下的飞机类(Plane)和子弹类(bullet)的设计;

①Plane类的设计:

Plane除了有包含自身坐标和宽高属性的矩形类成员变量外,还应该有存储子弹的容器类Vector对象;用于存储子弹,同时飞机应该有开火(fire()),移动(move())和绘图(draw())方法;

Plane类的代码具体如下:

package com.game;

import java.util.Vector;

import javax.microedition.lcdui.Graphics;

import com.gameitem.Rectangle;

public class Plane {
	public Rectangle rect;
	public int power ;
	public Vector bullet_v;
	public Plane(int x,int y,int w,int h) {
		this.rect = new Rectangle(x, y, w, h);
		this.power = 1;
		bullet_v = new Vector();
	}
	
	public void move(int dx,int dy){
		this.rect.move(dx, dy);
	}
	
	public void draw(Graphics g){
		g.setColor(0xff0000ff);
		g.fillRect(this.rect.x, this.rect.y, this.rect.w, this.rect.h);
	}

	public void fire() {//飞机开火
		int w = 6;
		int h = 12;
		//开火的时候向飞机的子弹容器中添加子弹
		for(int i = -power/2; i <= power/2 ; i++){
			Bullet temp = new Bullet(this.rect.x+this.rect.w/2 - w/2 + i*10, this.rect.y-h+6, w, h);
			this.bullet_v.addElement(temp);
		}
	}
	
}

②子弹类(Bullet)的设计

Bullet除了有包含自身坐标和宽高属性的矩形类成员变量外,同时应该有移动(move())和绘图(draw())方法;

Bullet类的代码具体如下:

package com.game;

import javax.microedition.lcdui.Graphics;

import com.gameitem.Rectangle;

public class Bullet {
	public Rectangle rect;
	public boolean visible;
	public Bullet(int x,int y,int w,int h) {
		rect = new Rectangle(x, y, w, h);
		this.visible = true;
	}
	
	public void move(int dx,int dy){
		this.rect.move(dx, dy);
	}
	
	public boolean outScreen(){
		return this.rect.outScreen();
	}
	
	public void draw(Graphics g){
		if(!visible)
			return;
		g.setColor(0xffff0000);
		g.fillRect(this.rect.x, this.rect.y, this.rect.w, this.rect.h);
	}
	
}


注意:如果子弹出屏幕后,还对其进行重绘,也不从游戏飞机的子弹容器中移除;随着飞机的开火(fire())功能被调用,容器中的子弹越来越多,屏幕上要绘制的子弹对象越来越多,这样对内存的消耗逐渐变大,最终耗尽内存,如果上面的程序不进行出屏检测和在出片后移除子弹,FPS会逐渐减小,整个游戏的绘图将会出现问题;让我们取出出屏检测和移除子弹,随着游戏的运行,将会出现如下图情况:

其次,我们也可以在飞机类和子弹类中加入input ();logic();gc()等方法,减少gamestage中这些方法的代码量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值