一.游戏的实现
包含类
SheepDogMIDlet
主游戏类,负责控制和管理各个屏幕和组件还有它们之间的联系。
SplashScresn
闪屏类,在屏幕中央显示一幅图像,同时程序在后台初始化。
MenuList
出现在闪屏之后,游戏结束或是游戏被暂停。
InstructionsScreen
向用户显示游戏说明
HightScoreScreen
显示最好记录
SheepdogCanvas
主游戏类
Field
用于画草地和水
SheepDog
画牧羊犬
Sheep
画羊,并且负责动物的人工智能
GameoverScreen
游戏结束画面
SoundEffects
利用MIDP 2.0 Media API 播放四种音效
Uml图
二.主游戏类SheepDogMIDlet class
这个类会和SplashScreen有联系,通过splashScreenPainted方法和splashScreenDone方法——在显示闪屏的时候在后台初始化游戏。
readRecordStore和writeRecordStore方法用于将游戏中的最高分保存在“BESTTIME”中。
1. 新建一个SheepDogMIDlet类。
2. 引入需要的类
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.rms.*;
import java.util.*;
import java.io.*;
3. 继承MIDlet类和Runnable接口,并给出构造函数。
public class SheepdogMIDlet
extends MIDlet
implements Runnable
{
private static final String RS_NAME = "BESTTIME";
private MenuList menuList;
private SheepdogCanvas sheepdogCanvas;
private boolean initDone = false;
private static final Random random = new Random();
private boolean hasBestTime = false;
private long bestTime;
public SheepdogMIDlet()
{
}
4. startApp, pauseApp 和 destroyApp方法,因为继承了Runable接口,所以要实现run方法。
public void startApp()
{
Displayable current = Display.getDisplay(this).getCurrent();
if (current == null)
{
// first time we've been called
Display.getDisplay(this).setCurrent(new SplashScreen(this));
}
else
{
if (current == sheepdogCanvas)
{
sheepdogCanvas.start(); // start its animation thread
}
Display.getDisplay(this).setCurrent(current);
}
}
public void pauseApp()
{
Displayable current = Display.getDisplay(this).getCurrent();
if (current == sheepdogCanvas)
{
sheepdogCanvas.stop(); // kill its animation thread
}
}
public void destroyApp(boolean unconditional)
{
if (sheepdogCanvas != null)
{
sheepdogCanvas.stop(); // kill its animation thread
}
}
5. 创建一些控制程序的方法。
private void quit()
{
destroyApp(false);
notifyDestroyed();
}
public void run()
{
init();
}
private synchronized void init()
{
if (!initDone)
{
readRecordStore();
SoundEffects.getInstance();
menuList = new MenuList(this);
sheepdogCanvas = new SheepdogCanvas(this);
initDone = true;
}
}
void splashScreenPainted()
{
new Thread(this).start(); // start background initialization
}
void splashScreenDone()
{
init(); // if not already done
Display.getDisplay(this).setCurrent(menuList);
}
void menuListContinue()
{
Display.getDisplay(this).setCurrent(sheepdogCanvas);
sheepdogCanvas.start();
}
void menuListNewGame()
{
sheepdogCanvas.init();
Display.getDisplay(this).setCurrent(sheepdogCanvas);
sheepdogCanvas.start();
}
void menuListInstructions()
{
// create and discard a new Instructions screen each time, to
// avoid keeping heap memory for it when it's not in use
Display.getDisplay(this).setCurrent(new InstructionsScreen(this));
}
void menuListHighScore()
{
// create and discard a new High Score screen each time, to
// avoid keeping heap memory for it when it's not in use
Display.getDisplay(this).setCurrent(new HighScoreScreen(this));
}
void menuListQuit()
{
quit();
}
void sheepdogCanvasMenu()
{
sheepdogCanvas.stop();
menuList.setGameActive(true);
Display.getDisplay(this).setCurrent(menuList);
}
void sheepdogCanvasGameOver(long time)
{
sheepdogCanvas.stop();
menuList.setGameActive(false);
Display.getDisplay(this).setCurrent(new GameOverScreen(this, time));
}
void gameOverDone()
{
Display.getDisplay(this).setCurrent(menuList);
}
void instructionsBack()
{
Display.getDisplay(this).setCurrent(menuList);
}
void highScoreBack()
{
Display.getDisplay(this).setCurrent(menuList);
}
// method needed by lots of classes, shared by putting it here
static Image createImage(String filename)
{
Image image = null;
try
{
image = Image.createImage(filename);
}
catch (java.io.IOException ex)
{
// just let return value be null
}
return image;
}
// method needed by lots of classes, shared by putting it here
static int random(int size)
{
return (random.nextInt() & 0x7FFFFFFF) % size;
}
// only the MIDlet has access to the display, so put this method here
void flashBacklight(int millis)
{
Display.getDisplay(this).flashBacklight(millis);
}
// only the MIDlet has access to the display, so put this method here
void vibrate(int millis)
{
Display.getDisplay(this).vibrate(millis);
}
6. 创建一个记录最高分的方法
long getBestTime()
{
return hasBestTime ? bestTime : -1;
}
boolean checkBestTime(long time)
{
if (!hasBestTime || (time < bestTime))
{
hasBestTime = true;
bestTime = time;
writeRecordStore();
return true;
}
else
{
return false;
}
private void readRecordStore()
{
hasBestTime = false;
RecordStore rs = null;
ByteArrayInputStream bais = null;
DataInputStream dis = null;
try
{
rs = RecordStore.openRecordStore(RS_NAME, false);
byte[] data = rs.getRecord(1);
bais = new ByteArrayInputStream(data);
dis = new DataInputStream(bais);
bestTime = dis.readLong();
hasBestTime = true;
}
catch (IOException ex)
{
// hasBestTime will still be false
}
catch (RecordStoreException ex)
{
// hasBestTime will still be false
}
finally
{
if (dis != null)
{
try
{
dis.close();
}
catch (IOException ex)
{
// no error handling necessary here
}
}
if (bais != null)
{
try
{
bais.close();
}
catch (IOException ex)
{
// no error handling necessary here
}
}
if (rs != null)
{
try
{
rs.closeRecordStore();
}
catch (RecordStoreException ex)
{
// no error handling necessary here
}
}
}
}Create the method for writing a new high score.
// this will only be called when we have a best time
private void writeRecordStore()
{
RecordStore rs = null;
ByteArrayOutputStream baos = null;
DataOutputStream dos = null;
try
{
rs = RecordStore.openRecordStore(RS_NAME, true);
baos = new ByteArrayOutputStream();
dos = new DataOutputStream(baos);
dos.writeLong(bestTime);
byte[] data = baos.toByteArray();
if (rs.getNumRecords() == 0)
{
// new record store
rs.addRecord(data, 0, data.length);
}
else
{
// existing record store: will have one record, id 1
rs.setRecord(1, data, 0, data.length);
}
}
catch (IOException ex)
{
// just leave the best time unrecorded
}
catch (RecordStoreException ex)
{
// just leave the best time unrecorded
}
finally
{
if (dos != null)
{
try
{
dos.close();
}
catch (IOException ex)
{
// no error handling necessary here
}
}
if (baos != null)
{
try
{
baos.close();
}
catch (IOException ex)
{
// no error handling necessary here
}
}
if (rs != null)
{
try
{
rs.closeRecordStore();
}
catch (RecordStoreException ex)
{
// no error handling necessary here
}
}
}
}
}
二.
SplashScreen
类
说明:在屏幕中显示图像,并画一个相框,当图像显示完之后,返回主程序类,经过3s或者按下按键,再次叫回主程序,主程序在闪屏出现的时候进行初始化。
1.
创建SplashScreen类,继承Canvas类和Runable接口;
2.
导入类
import javax.microedition.lcdui.*;
3.继承Canvas类和Runable接口
class SplashScreen
extends Canvas
implements Runnable
{
private final SheepdogMIDlet midlet;
private Image splashImage;
private volatile boolean dismissed = false;
3. 构造函数
SplashScreen(SheepdogMIDlet midlet)
{
this.midlet = midlet;
setFullScreenMode(true);
splashImage = SheepdogMIDlet.createImage("/splash.png");
new Thread(this).start();
}
4. 创建run方法
public void run()
{
synchronized(this)
{
try
{
wait(3000L); // 3 seconds
}
catch (InterruptedException e)
{
// can't happen in MIDP: no Thread.interrupt method
}
dismiss();
}
}
5. 创建paint方法绘制屏幕
public void paint(Graphics g)
{
int width = getWidth();
int height = getHeight();
g.setColor(0x00FFFFFF); // white
g.fillRect(0, 0, width, height);
g.setColor(0x00FF0000); // red
g.drawRect(1, 1, width-3, height-3); // red border one pixel from edge
if (splashImage != null)
{
g.drawImage(splashImage,
width/2,
height/2,
Graphics.VCENTER | Graphics.HCENTER);
splashImage = null;
midlet.splashScreenPainted();
}
}
6. 创建监视按键的方法和使闪屏消失的方法
public synchronized void keyPressed(int keyCode)
{
dismiss();
}
private void dismiss()
{
if (!dismissed)
{
dismissed = true;
midlet.splashScreenDone();
}
}
}
三.MenuList类
说明:在闪屏过后,或是游戏结束后显示,如果是暂停,列表最上方还会加入Continue项
1. 创建类
2. 导入类
import javax.microedition.lcdui.*;
3. 继承List类和CommandListener接口
class MenuList
extends List
implements CommandListener
{
private SheepdogMIDlet midlet;
private Command exitCommand;
private boolean gameActive = false;Create the menu listing for the game.
MenuList(SheepdogMIDlet midlet)
{
super("Sheepdog", List.IMPLICIT);
this.midlet = midlet;
append("New game", null);
append("High score", null);
append("Instructions", null);
exitCommand = new Command("Exit", Command.EXIT, 1);
addCommand(exitCommand);
setCommandListener(this);
}
4. 创建激活游戏方法
void setGameActive(boolean active)
{
if (active && !gameActive)
{
gameActive = true;
insert(0, "Continue", null);
}
else if (!active && gameActive)
{
gameActive = false;
delete(0);
}
}
5. 创建监视菜单命令的方法
public void commandAction(Command c, Displayable d)
{
if (c == List.SELECT_COMMAND)
{
int index = getSelectedIndex();
if (index != -1) // should never be -1
{
if (!gameActive)
{
index++;
}
switch (index)
{
case 0: // Continue
midlet.menuListContinue();
break;
case 1: // New game
midlet.menuListNewGame();
break;
case 2: // High score
midlet.menuListHighScore();
break;
case 3:
midlet.menuListInstructions();
break;
default:
// can't happen
break;
}
}
}
else if (c == exitCommand)
{
midlet.menuListQuit();
}
}
}
下面是菜单中的几个选项
说明类
import javax.microedition.lcdui.*;
class InstructionsScreen
extends Form
implements CommandListener
{
private final SheepdogMIDlet midlet;
private final Command backCommand;
private static final String instructions =
"Herd the sheep into the fold as quickly as you can.\n" +
"Sheep won't leave the fold once they've entered it.\n" +
"If they're not behaving, bark using the Fire key!";
InstructionsScreen(SheepdogMIDlet midlet)
{
super("Instructions");
this.midlet = midlet;
append(new StringItem(null, instructions));
backCommand = new Command("Back", Command.BACK, 1);
addCommand(backCommand);
setCommandListener(this);
}
public void commandAction(Command c, Displayable d)
{
midlet.instructionsBack();
}
}
最高分类
import javax.microedition.lcdui.*;
class HighScoreScreen
extends Form
implements CommandListener
{
private final SheepdogMIDlet midlet;
private final Command backCommand;Write the code that will create the high score screen.
HighScoreScreen(SheepdogMIDlet midlet)
{
super("High score");
this.midlet = midlet;
long bestTime = midlet.getBestTime();
String text = (bestTime == -1) ? "none yet"
: (Long.toString(bestTime) + "s");
append(new StringItem("Best time", text));
backCommand = new Command("Back", Command.BACK, 1);
addCommand(backCommand);
setCommandListener(this);
}
public void commandAction(Command c, Displayable d)
{
midlet.highScoreBack();
}
}
游戏结束类
注意文字描边效果的实现
import javax.microedition.lcdui.*;
class GameOverScreen
extends Canvas
{
private final SheepdogMIDlet midlet;
private boolean wasBestTime;
private long time;
private long bestTime;
GameOverScreen(SheepdogMIDlet midlet, long time)
{
super();
this.midlet = midlet;
this.time = time;
setFullScreenMode(true);
if (midlet.checkBestTime(time))
{
wasBestTime = true;
bestTime = time;
SoundEffects.getInstance().startHighScoreSound();
}
else
{
wasBestTime = false;
bestTime = midlet.getBestTime();
SoundEffects.getInstance().startGameOverSound();
}
midlet.flashBacklight(1000); // 1 second
}
Create the method for painting the screen.
public void paint(Graphics g)
{
int width = getWidth();
int height = getHeight();
// clear screen to green
g.setColor(0x0000FF00);
g.fillRect(0, 0, width, height);
// Write message. We use a trick to make outlined text: we draw it
// offset one pixel to the top, bottom, left & right in white, then
// centred in black.
g.setFont(Font.getFont(Font.FACE_PROPORTIONAL,
Font.STYLE_BOLD,
Font.SIZE_LARGE));
int centerX = width / 2;
int centerY = height / 2;
g.setColor(0x00FFFFFF); // white
drawText(g, centerX, centerY - 1);
drawText(g, centerX, centerY + 1);
drawText(g, centerX - 1, centerY);
drawText(g, centerX + 1, centerY);
g.setColor(0x00000000); // black
drawText(g, centerX, centerY);
}Create a method for drawing text on the screen.
private void drawText(Graphics g, int centerX, int centerY)
{
int fontHeight = g.getFont().getHeight();
int textHeight = 3 * fontHeight;
int topY = centerY - textHeight / 2;
g.drawString("GAME OVER",
centerX,
topY,
Graphics.HCENTER | Graphics.TOP);
g.drawString("Time: " + time + "s",
centerX,
topY + fontHeight,
Graphics.HCENTER | Graphics.TOP);
g.drawString(wasBestTime ? "New best time!" :
("Best time: " + bestTime + "s"),
centerX,
topY + 2 * fontHeight,
Graphics.HCENTER | Graphics.TOP);
}
public void keyPressed(int keyCode)
{
midlet.gameOverDone();
}
}