www.pudn.com > ch8-spaceshooter.rar > GameCanvas.java


/* 
 * GameCanvas.java 
 * 
 * Copyright 2001 SkyArts. All Rights Reserved. 
 */ 
import javax.microedition.lcdui.*; 
import java.util.*; 
 
/** 
 * 游戏的描绘与操作所用的类 
 * 
 * @author  Hideki Yonekawa 
 * @version 1.0 
 */ 
class GameCanvas extends Canvas implements Runnable, CommandListener { 
	/** 储存横向长度的变量  */ 
	private int					screenWidth; 
	/** 储存纵向长度的变量 */ 
	private int					screenHeight; 
 
	/** 储存SpaceShooter对象的变量 */ 
	private SpaceShooter		spaceShooter; 
 
	/** 储存使用在标题之图像的变量 */ 
	private Image				titleImg; 
 
	/** 储存自机类别之对象的变量 */ 
	private MyShip			myShip			= new MyShip(); 
 
	/** 储存自机飞弹类别之对象的数组变量 */ 
	private MyMissile[]		myMissiles		= new MyMissile[] { 
		new MyMissile(), 
		new MyMissile(), 
		new MyMissile(), 
		new MyMissile(), 
		new MyMissile() 
	}; 
	/** 储存自机之飞弹发射数的变量 */ 
	private int			myMissileCount; 
 
	/** 储存了UFO类别之对象的数组变量 */ 
	private UFO[]			ufos           		= new UFO[] { 
		new UFO(myShip), 
		new UFO(myShip), 
		new UFO(myShip), 
		new UFO(myShip), 
		new UFO(myShip), 
		new UFO(myShip), 
		new UFO(myShip) 
	}; 
	/** 储存了UFO之使用数的变量 */ 
	private int			ufoCount; 
 
	/** 储存了UFO之飞弹类别对象的数组变量 */ 
	private UFOMissile[]		ufoMissiles		= new UFOMissile[] { 
		new UFOMissile(), 
		new UFOMissile(), 
		new UFOMissile(), 
		new UFOMissile(), 
		new UFOMissile(), 
		new UFOMissile(), 
		new UFOMissile(), 
		new UFOMissile(), 
		new UFOMissile(), 
		new UFOMissile() 
	}; 
	/** 储存了UFO之飞弹发射数的变量 */ 
	private int			ufoMissileCount; 
 
	/** 用来储存描绘的间隔(INTERVAL)的常量 */ 
	private static final long	DRAW_INTERVAL		= 100; 
 
	/** 显示暂停状态的标志 */ 
	private	boolean			isPaused; 
 
	/** 代表游戏循环停止的变量 */ 
	private boolean			isStopped; 
 
	/** 储存了操作UFO的动态时所使用之计数的变量 */ 
	private int			tickCount4UFO; 
 
	/** 用来改变UFO的出现之Random变量 */ 
	private Random			random				= new Random(); 
 
	/** Start指令变量 */ 
	private Command				startCmd		= new Command("Start", Command.SCREEN, 5); 
	/** Exit指令变量 */ 
	private Command				exitCmd			= new Command("Exit", Command.SCREEN, 1); 
	/** Pause指令变量 */ 
	private Command				pauseCmd		= new Command("Pause", Command.SCREEN, 5); 
	/** Resume指令变量 */ 
	private Command				resumeCmd		= new Command("Resume", Command.SCREEN, 5); 
	/** Quit指令变量 */ 
	private Command				quitCmd			= new Command("Quit", Command.SCREEN, 1); 
 
	/** 储存分数的变量 */ 
	private long				score; 
 
	/** 储存最高分数的变量 */ 
	private long				highScore		= 0; 
 
	/** 用来储存默认分数的常量 */ 
	private static final long	        DEFAULT_SCORE		= 100; 
 
	/** 储存生命点数的变量 */ 
	private int				lives; 
 
	/** 用来储存默认之生命点数的常量 */ 
	private static final int	DEFAULT_LIVES		= 3; 
 
	/** 储存默认字体的变量 */ 
	private Font				defFont			= Font.getDefaultFont(); 
 
	/** 储存分数显示部分之高度的常量(设定到默认字体高度) */ 
	private static final int	SCORE_AREA		 = Font.getDefaultFont().getHeight(); 
 
	/** 代表标题显示状态的常量 */ 
	private static final short	TITLE			= 0; 
	/** 代表游戏开始显示状态的常量 */ 
	private static final short	GAME_START		= 1; 
	/** 代表游戏中显示状态的常量 */ 
	private static final short	GAME_PLAYING		= 2; 
	/** 代表游戏结束显示状态的常量 */ 
	private static final short	GAME_END		= 5; 
	/** 储存现在显示状态的变量 */ 
	private short			gameState; 
 
	/** 构造函数 */ 
	GameCanvas(SpaceShooter spaceShooter) { 
		this.spaceShooter = spaceShooter; 
		//取得画面的可描绘范围 
		screenWidth = getWidth(); 
		screenHeight = getHeight(); 
 
		//取得标题图像 
		try { 
			titleImg = Image.createImage("/title.png"); 
		}catch(Exception e) {} 
 
		//登录指令监听 
		setCommandListener(this); 
 
		//将游戏的状态变成显示标题 
		gameState = TITLE; 
	} 
 
	/** 进行游戏之初始化的方法 */ 
	private void doGameInit() { 
		//清除分数 
		score = 0; 
		//将自机的位置设定于中央 
		myShip.setX((screenWidth - myShip.getWidth()) /2); 
		myShip.setY(screenHeight - myShip.getHeight()); 
		myShip.setHit(false);//清除Hit状态 
		//将生命点数设为默认值 
		lives = DEFAULT_LIVES; 
 
		//清除自机飞弹 
		for(int i=0; i < myMissiles.length; i++) { 
			myMissiles[i].setAlive(false); 
		} 
		//清除自机飞弹数 
		myMissileCount = 0; 
 
		//清除UFO 
		for(int i=0; i < ufos.length; i++) { 
			ufos[i].setAlive(false); 
		} 
		//清除UFO数 
		ufoCount = 0; 
 
		//清除UFO飞弹 
		for(int i=0; i < ufoMissiles.length; i++) { 
			ufoMissiles[i].setAlive(false); 
		} 
		//清除UFO飞弹数 
		ufoMissileCount = 0; 
 
		//清除要让UFO出现时所使用的Tick计数 
		tickCount4UFO = 0; 
 
		//清除游戏循环停止标志 
		isStopped = false; 
		//清除暂停标志 
		isPaused = false; 
	} 
 
	/** MIDlet的startApp()被调用出来时所调用的方法 */ 
	void doStartApp() { 
		//根据现在的状态来操作运作 
		switch(gameState) { 
			case TITLE:		//显示标题时 
				doTitle(); 
			break; 
 
			case GAME_START:	//游戏开始时 
			case GAME_PLAYING:	//游戏中 
				//进行暂停 
				isPaused = true; 
				//停止游戏循环 
				isStopped = true; 
 
				//以防万一删除关卡中的指令 
				removeCommand(pauseCmd); 
				removeCommand(resumeCmd); 
				//新增Resume指令 
				addCommand(resumeCmd); 
			break; 
 
			default:		//上述情形以外时 
				//解除暂停 
				isPaused = false; 
			break; 
		} 
	} 
 
	/** MIDlet的pauseApp()方法被调用出来时所调用的方法 */ 
	void doPauseApp() { 
		switch(gameState) { 
			case TITLE:				//显示标题时 
			break; 
 
			default:				//上述情形以外时 
				//暂停 
				isPaused = true; 
				//停止游戏循环 
				isStopped = true; 
			break; 
		} 
	} 
 
	/** 进行标题显示的方法 */ 
	private void doTitle() { 
		//将游戏的状态设为显示标题 
		gameState = TITLE; 
 
		//以防万一先将关卡中的指令删除 
		removeCommand(pauseCmd); 
		removeCommand(resumeCmd); 
		removeCommand(quitCmd); 
 
		//显示标题画面 
		repaint(); 
		//新增指令 
		addCommand(exitCmd); 
		addCommand(startCmd); 
	} 
 
	/** 启动描绘线程的方法 */ 
	private void doThreadStart() { 
		new Thread(this).start(); 
	} 
 
	/** 结束游戏的方法 */ 
	private void doGameStop() { 
		//停止游戏循环 
		isStopped = true; 
		//设定最高分数 
		setHighScore(score); 
	} 
 
	/** 进行暂停或是重新开始的方法 */ 
	private void doPauseOrResume() { 
		if(isPaused) { 
		//暂停时 
			//重新开始 
			isPaused = false; 
			isStopped = false; 
			doThreadStart(); 
		}else { 
		//不是暂停时 
			//暂停 
			isPaused = true; 
			doGameStop(); 
		}// end if 
		repaint(); 
	} 
 
	/** 
	 * 进行描绘的方法,通常不会从自类别来调用 
	 * @param	g	Graphics对象 
	 */ 
	protected void paint(Graphics g) { 
		//以黑色清除背景 
		g.setColor(0x00000000); 
		g.fillRect(0, 0, screenWidth, screenHeight); 
 
		g.setFont(defFont); 
 
		String drawSt; 
		int drawX, drawY, stWidth; 
		int stHeight = defFont.getHeight(); 
 
		//根据游戏状态来改变描绘内容 
		switch(gameState) { 
			case TITLE://显示标题时 
				//描绘标题图像 
				drawX = (screenWidth - titleImg.getWidth()) /2; 
				g.drawImage(titleImg, drawX, 0, Graphics.TOP|Graphics.LEFT); 
 
				//显示最高分数 
				g.setColor(0x00FFFFFF); 
				g.setFont(defFont); 
				drawSt = "High Score"; 
				drawX = (screenWidth - defFont.stringWidth(drawSt)) / 2; 
				drawY = titleImg.getHeight() -5; 
				g.drawString(drawSt, drawX, drawY, Graphics.TOP|Graphics.LEFT); 
				drawX = drawX + defFont.stringWidth(drawSt) - defFont.stringWidth(String.valueOf(highScore)); 
				drawY = drawY + stHeight; 
				g.drawString(String.valueOf(highScore), drawX, drawY, Graphics.TOP|Graphics.LEFT); 
 
				//显示着作权 
				drawSt = "Copyright 2001"; 
				drawX = (screenWidth - defFont.stringWidth(drawSt)) / 2; 
				drawY = getHeight() - stHeight *2; 
				g.drawString(drawSt, drawX, drawY, Graphics.TOP|Graphics.LEFT); 
 
				drawSt = "SkyArts.com"; 
				drawX = (screenWidth - defFont.stringWidth(drawSt)) / 2; 
				drawY = drawY + stHeight; 
				g.drawString(drawSt, drawX, drawY, Graphics.TOP|Graphics.LEFT); 
			return; 
 
			case GAME_START://游戏开始时 
				//描绘游戏开始画面 
				g.setColor(0x00FFFFFF); 
				drawSt = "Ready go !"; 
				stWidth = defFont.stringWidth(drawSt); 
				drawX = (screenWidth - stWidth) / 2; 
				drawY = (screenHeight - stHeight) / 2; 
				g.drawString(drawSt, drawX, drawY, Graphics.TOP|Graphics.LEFT); 
			return; 
 
			case GAME_PLAYING: 
			case GAME_END: 
			//游戏中或是游戏结束时 
				g.setColor(0x00FFFFFF); 
				//将分数描绘到左边 
				drawSt = "S:" + String.valueOf(score); 
				g.drawString(drawSt, 0, 0, Graphics.TOP|Graphics.LEFT); 
 
				//将生命点数描绘到右边 
				drawSt = "L:" + String.valueOf(lives); 
				g.drawString(drawSt, screenWidth- defFont.stringWidth(drawSt), 0, Graphics.TOP|Graphics.LEFT); 
 
				//描绘自机 
				myShip.doDraw(g); 
 
				//描绘UFO 
				for(int i=0; i < ufos.length; i++) { 
					ufos[i].doDraw(g); 
				} 
 
				//描绘自机飞弹 
				for(int i=0; i < myMissiles.length; i++) { 
					myMissiles[i].doDraw(g); 
				} 
 
				//描绘UFO飞弹 
				for(int i=0; i < ufoMissiles.length; i++) { 
					ufoMissiles[i].doDraw(g); 
				} 
 
				//在暂停时等画面中显示文本 
				switch(gameState) { 
					case GAME_PLAYING://游戏中 
						if(isPaused) { 
						//显示代表暂停的字符串 
							drawSt = "Pause"; 
						}else { 
						//不是暂停时就return 
							return; 
						} 
					break; 
 
					case GAME_END://游戏结束时 
						//显示游戏结束字符串 
						drawSt = "Game over"; 
					break; 
 
					default: 
					return; 
				} 
				stWidth = defFont.stringWidth(drawSt); 
				drawX = (screenWidth-stWidth) / 2; 
				drawY = (screenHeight - (stHeight)) / 2; 
 
				//描绘文本框 
				g.setColor(0x00BBBBBB); 
				g.fillRoundRect(drawX-2, drawY+2, stWidth+9, stHeight+2, 5, 5); 
				g.setColor(0x00000000); 
				g.fillRoundRect(drawX-6, drawY-2, stWidth+11, stHeight+4, 5, 5); 
				g.setColor(0x00FFFFFF); 
				g.fillRoundRect(drawX-4, drawY, stWidth+7, stHeight, 5, 5); 
 
				//输出消息 
				g.setColor(0x00000000); 
				g.drawString(drawSt, drawX, drawY, Graphics.TOP|Graphics.LEFT); 
			break; 
		} 
	} 
 
	/** 指定的事件发生时被调用出来的方法 */ 
	public void commandAction(Command c, Displayable s) { 
		if(c == startCmd) {//Start指令 
			//初始化游戏以开始游戏 
			doGameInit(); 
			gameState = GAME_START; 
			doThreadStart(); 
 
		}else if(c == exitCmd) {//EXIT指令 
			//调用出SpaceShooter类别的doExit方法以结束MIDlet本身 
			spaceShooter.doExit(); 
 
		}else if(c == pauseCmd) {//Pause指令 
			//交换Pause指令与Resume指令,调用出Pause、Resume处理方法 
			removeCommand(pauseCmd); 
			addCommand(resumeCmd); 
			doPauseOrResume(); 
 
		}else if(c == resumeCmd) {//Resume指令 
			//交换Resume指令与Pause指令,调用出Pause、Resume处理方法 
			removeCommand(resumeCmd); 
			addCommand(pauseCmd); 
			doPauseOrResume(); 
 
		}else if(c == quitCmd) {//Quit指令 
			//调用出游戏结束方法 
			doGameStop(); 
			//删除指令 
			removeCommand(pauseCmd); 
			removeCommand(resumeCmd); 
			removeCommand(quitCmd); 
			//显示标题 
			doTitle(); 
		} 
	} 
 
	/** 按键按下事件发生时被调用出来的方法 */ 
	protected synchronized void keyPressed(int keyCode) { 
		if(	gameState != GAME_PLAYING || 
			(gameState == GAME_PLAYING && isPaused)) { 
		//非游戏中,或在游戏中暂停时为return 
			return; 
		} 
 
		//为清除按键时不处理 
		if(keyCode == 0) return;	//for KDDI 
 
		int gameCode = getGameAction(keyCode); 
		switch(gameCode) { 
			case LEFT:	//按下向左按键时 
				doLeft(); 
			break; 
 
			case RIGHT:	//按下向右按键时 
				doRight(); 
			break; 
 
			case FIRE:	//按下Fire按键时 
			case UP:	//按下向上按键时 
				doFire(); 
			break; 
		} 
	} 
 
	/** 压下按键状态时被重复调用的方法 */ 
	protected void keyRepeated(int keyCode) { 
		keyPressed(keyCode); 
	} 
 
	/** 线程的运作部分 */ 
	public void run() { 
		try { 
			switch(gameState) { 
				case GAME_START:	//游戏开始时 
					//先将标题的指令删除 
					removeCommand(startCmd); 
					removeCommand(exitCmd); 
 
					//显示游戏开始画面 
					repaint(); 
					Thread.sleep(1500); 
 
					//新增指令 
					addCommand(quitCmd); 
					if(! isPaused) { 
						addCommand(pauseCmd); 
					} 
 
					//将状态设为游戏中,然后就此转移到游戏中的处理 
					gameState = GAME_PLAYING; 
 
				case GAME_PLAYING:	//游戏中 
					repaint(); 
					//游戏结束标志 
					boolean isGameOver = false; 
 
					//开始游戏循环 
					while(! isStopped) {//在Stop前持续循环 
						//再描绘 
						repaint(); 
 
						//移动自机(利用在爆炸显示时) 
						myShip.doMove(); 
 
						//UFO处理 
						for(int i=0; i < ufos.length; i++) { 
							if(ufos[i].isAlive()) { 
							//只处理在Alive状态的UFO 
								//移动UFO 
								ufos[i].doMove(); 
								if(! ufos[i].isAlive() || 
									ufos[i].getX()+ufos[i].getWidth() < 0 || 
									ufos[i].getX() > screenWidth) { 
								//移动UFO之后不是Alive状态时 
								//或是UFO跑到画面外时 
									//设定UFO不是Alive状态,减少UFO数目 
									ufos[i].setAlive(false); 
									ufoCount--; 
 
									//若UFO为命中状态的话,在分数中新增DEFAULT_SCORE 
									if(ufos[i].isHit()) score = score + DEFAULT_SCORE; 
 
								}else if(ufoMissileCount < ufoMissiles.length && 
										ufos[i].isDropBomb()) { 
								//UFO在Alive状态下发射飞弹时 
									//进行炸弹处理 
									//在飞弹数组中寻找不是Alive状态的飞弹 
									for(int j=0; j < ufoMissiles.length; j++) { 
										if(! ufoMissiles[j].isAlive()) { 
										//不是Alive状态的飞弹时 
											//符合飞弹的座标来攻击 
											ufoMissiles[j].setX(( 
												ufos[i].getWidth() - ufoMissiles[j].getWidth()) /2 + 
														ufos[i].getX()); 
											ufoMissiles[j].setY(ufos[i].getY() + ufos[i].getHeight()); 
											ufoMissiles[j].setAlive(true); 
											ufoMissileCount++; 
											break; 
										} 
									} 
								} 
							} 
						}// end for 
 
						//UFO飞弹处理 
						for(int i=0; i < ufoMissiles.length; i++) { 
							if(ufoMissiles[i].isAlive()) { 
							//只处理Alive状态的UFO飞弹 
								//移动UFO飞弹 
								ufoMissiles[i].doMove(); 
 
								//命中判定 
								if(myShip.isOverlaps(ufoMissiles[i])) { 
								//UFO飞弹与自机重叠时 
									myShip.setHit(true); 
									//删除飞弹 
									ufoMissiles[i].setAlive(false); 
									ufoMissileCount--; 
 
									//减少生命点数 
									lives--; 
									if(lives < 1) { 
									//当生命点数用完时 
										//删除指令 
										removeCommand(pauseCmd); 
										removeCommand(resumeCmd); 
										removeCommand(quitCmd); 
										//建立游戏结束标志 
										isGameOver = true; 
										//抽出for语法 
										break; 
									} 
 
								}else if(ufoMissiles[i].getY() > getHeight()) { 
								//UFO飞弹跑到画面外时 
									//将UFO飞弹设为无效 
									ufoMissiles[i].setAlive(false); 
									ufoMissileCount--; 
								} 
							} 
						} 
 
						//自机飞弹处理 
						for(int i=0; i < myMissiles.length; i++) { 
							if(myMissiles[i].isAlive()) { 
							//只处理Alive状态的自机飞弹 
								//移动自机飞弹 
								myMissiles[i].doMove(); 
 
								//命中判定 
								for(int j=0; j < ufos.length; j++) { 
									if(ufos[j].isAlive() && ! ufos[j].isHit()) { 
									//当UFO为在Alive状态,而不是被击中状态时 
										if(ufos[j].isOverlaps(myMissiles[i])) { 
										//自机飞弹与UFO重叠时 
											//将UFO设为击中状态 
											ufos[j].setHit(true); 
 
											//删除飞弹 
											myMissiles[i].setAlive(false); 
											myMissileCount--; 
											break; 
										} 
									} 
								} 
							} 
							if(	myMissiles[i].isAlive() && 
								myMissiles[i].getY() + myMissiles[i].getHeight() < SCORE_AREA) 
							{ 
							//从画面消失时 
								//删除自机飞弹 
								myMissiles[i].setAlive(false); 
								myMissileCount--; 
							} 
						}// end for 
 
						if(isGameOver) { 
						//当游戏结束标志建立时 
							//删除指令 
							removeCommand(pauseCmd); 
							removeCommand(resumeCmd); 
							removeCommand(quitCmd); 
 
							//结束这个循环 
							doGameStop(); 
 
							//显示游戏结束,再等待一段时间后显示标题 
							gameState = GAME_END; 
							repaint(); 
							Thread.sleep(5000); 
							doTitle(); 
							break; 
 
						}else { 
						//游戏未结束时 
							//让UFO出现 
							doEmerge4UFO(); 
							//在描绘间隔中待命 
							Thread.sleep(DRAW_INTERVAL); 
						} 
					}// end while 
				break; 
			} 
		}catch(Exception e) {} 
	} 
 
	/** 左按键被压下时所调用的方法 */ 
	private void doLeft() { 
		//向左移动自机大小的一半 
		int tmpX = myShip.getX() - (myShip.getWidth() /2); 
		if(tmpX < 0) { 
			tmpX = 0; 
		} 
		myShip.setX(tmpX); 
	} 
 
	/** 右按键被压下时所调用的方法 */ 
	private void doRight() { 
		//向右移动自机大小的一半 
		int tmpX = myShip.getX() + (myShip.getWidth() /2); 
		if(tmpX > screenWidth -myShip.getWidth()) { 
			tmpX = screenWidth -myShip.getWidth(); 
		} 
		myShip.setX(tmpX); 
	} 
 
	/** 按下决定按键、下按键时被调用的方法 */ 
	private void doFire() { 
		if(myMissileCount < myMissiles.length) { 
		//比飞弹数的上限还少时 
			for(int i=0; i < myMissiles.length; i++) { 
				if(! myMissiles[i].isAlive()) { 
				//自机飞弹不是Alive状态时 
					//发射飞弹 
					myMissiles[i].setX((myShip.getWidth() - myMissiles[i].getWidth()) /2 + 
										myShip.getX()); 
					myMissiles[i].setY(myShip.getY() - myMissiles[i].getHeight()); 
					myMissiles[i].setAlive(true); 
					myMissileCount++; 
					break; 
				} 
			} 
		} 
	} 
 
	/** 要让UFO出现时所调用的方法 */ 
	private void doEmerge4UFO() { 
		//在Tick计数中增加1 
		tickCount4UFO++; 
		if(ufoCount < 1 || tickCount4UFO > 4) { 
		//UFO为0时,或是是Tick计数比4还大时 
			tickCount4UFO = 0; 
 
			if(ufoCount < ufos.length) { 
			//UFO的数目比上限还少时 
 
				//取得乱数的int值 
				int tmpInt = random.nextInt() % 2; 
				if(tmpInt == 1 || tmpInt == -1) { 
				//让其出现时 
 
					tmpInt = random.nextInt() % 2; 
					//寻找不是Alive状态的UFO 
					UFO ufo; 
					for(int i=0; i < ufos.length; i++) { 
						if(! ufos[i].isAlive()) { 
							if(tmpInt == 0) { 
							//从右边出现时 
								tmpInt = random.nextInt() % 2;//改变高度 
								if(tmpInt < 0) tmpInt = tmpInt* (-1); 
								ufos[i].setDirection(UFO.DIRECTION_RIGHT); 
								ufos[i].setX(0 +2); 
								ufos[i].setY(SCORE_AREA + (((ufos[i].getHeight() +2) *2) * tmpInt)); 
 
							}else { 
							//从左边出现时 
								tmpInt = random.nextInt() % 2;//改变高度 
								if(tmpInt < 0) tmpInt = tmpInt* (-1); 
								ufos[i].setDirection(UFO.DIRECTION_LEFT); 
								ufos[i].setX((screenWidth - ufos[i].getWidth() / 2) -2); 
								ufos[i].setY(SCORE_AREA + 
											(((ufos[i].getHeight() +2) *2) * tmpInt) + 
											(ufos[i].getHeight() +2)); 
							} 
							ufos[i].setAlive(true); 
							ufoCount++; 
							break; 
						} 
					} 
				} 
			} 
		} 
	} 
 
	/** 设定最高分数的方法 */ 
	private void setHighScore(long score) { 
		if(score > highScore) { 
			highScore = score; 
		} 
	} 
}