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;
}
}
}