今天折腾了一下把音乐的元素加到那个UFO游戏里面去了,目前来说这也算是个完整的游戏了。
它具备开始画面和开场音乐,有生命次数的游戏过程和背景音乐,有记分机制,有结束画面和结束音乐,最后还有一个MIDlet图标。
先把代码贴出来:
// UFOCanvas.java
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import java.util.*;
import java.io.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;
public class UFOCanvas extends GameCanvas implements Runnable {
private Display display;
private boolean sleeping, restart;
private long frameDelay;
private Random rand;
private Sprite ufoSprite;
private Sprite gameoverImg, coverImg;
private int ufoXSpeed, ufoYSpeed;
private int ox = 0, oy = 0;
private int gx = -getWidth(), gy = -getHeight();
private int cx, cy, sx, sy;
private int score;
private int ufoLife = 3;
private Sprite[] roidSprite = new Sprite[3];
private Sprite[] ufolifeSprite = new Sprite[3];
private Player startmusic;
private Player gameovermusic;
private Player backmusic;
public UFOCanvas(Display d) {
super(true);
display = d;
frameDelay = 33;
}
public void start() {
display.setCurrent(this);
rand = new Random();
ufoXSpeed = ufoYSpeed = 0;
restart = true;
score = 0;
try {
//初始化UFO、游戏结束画面、封面画面、玩法介绍画面、关于我们画面的精灵和位置
ufoSprite = new Sprite(Image.createImage("/Saucer.png"));
ufoSprite.setPosition((getWidth() - ufoSprite.getWidth()) / 2,
(getHeight() - ufoSprite.getHeight()) / 2);
gameoverImg = new Sprite(Image.createImage("/gameover.png"));
gameoverImg.setPosition(-getWidth(),-getHeight());
coverImg = new Sprite(Image.createImage("/cover.png"));
coverImg.setPosition(0,0);
//初始化小行星精灵和位置和动画侦范围
Image img = Image.createImage("/Roid.png");
roidSprite[0] = new Sprite(img, 42, 35);
roidSprite[1] = new Sprite(img, 42, 35);
roidSprite[2] = new Sprite(img, 42, 35);
//初始化UFO生命图示精灵和位置
Image img2 = Image.createImage("/UFO.png");
ufolifeSprite[0] = new Sprite(img2,12,12);
ufolifeSprite[0].setPosition(2,270);
ufolifeSprite[1] = new Sprite(img2,12,12);
ufolifeSprite[1].setPosition(14,270);
ufolifeSprite[2] = new Sprite(img2,12,12);
ufolifeSprite[2].setPosition(26,270);
//初始化开始、结束和背景音乐对象
InputStream is = getClass().getResourceAsStream("startmusic.mid");
startmusic = Manager.createPlayer(is, "audio/midi");
startmusic.prefetch();
is = getClass().getResourceAsStream("gameovermusic.mid");
gameovermusic = Manager.createPlayer(is, "audio/midi");
gameovermusic.prefetch();
is = getClass().getResourceAsStream("backmusic.mid");
backmusic = Manager.createPlayer(is, "audio/midi");
backmusic.prefetch();
//当start方法被调用时就播放开始音乐
startmusic.start();
}catch (IOException e) {}
catch (MediaException me) {}
//开始线程
sleeping = false;
Thread t = new Thread(this);
t.start();
}
public void stop() {
sx = getWidth() /2;
sy = getHeight() /2;
sleeping = true;
startmusic.close();
backmusic.close();
//gameovermusic.close();
}
public void run() {
Graphics g = getGraphics();
//游戏主循环
while (!sleeping) {
update();
draw(g);
try {
Thread.sleep(frameDelay);
}catch (InterruptedException ie) {}
} //while
} //run
private void update() {
//用restart来判断是否显示主画面还是进入游戏
if(restart) {
int keyState = getKeyStates();
if((keyState & FIRE_PRESSED)!= 0) {
//如果START被按下则停止开始音乐并播放背景音乐
try {
backmusic.setLoopCount(-1);
backmusic.setMediaTime(0);
startmusic.stop();
backmusic.start();
} catch (MediaException me) {}
coverImg.setPosition(-getWidth(),-getHeight());
restart = false;
score = 0;
}
else coverImg.setPosition(0,0);
return;
}
//通过按上下左右键来改变精灵速度
int keyState = getKeyStates();
if ((keyState & LEFT_PRESSED) != 0)
ufoXSpeed--;
else if ((keyState & RIGHT_PRESSED) != 0)
ufoXSpeed++;
if ((keyState & UP_PRESSED) != 0)
ufoYSpeed--;
else if ((keyState & DOWN_PRESSED) != 0)
ufoYSpeed++;
//UFO速度在8和-8之间随机取得
ufoXSpeed = Math.min(Math.max(ufoXSpeed, -8), 8);
ufoYSpeed = Math.min(Math.max(ufoYSpeed, -8), 8);
ufoSprite.move(ufoXSpeed, ufoYSpeed);
checkBounds(ufoSprite);
for (int i = 0; i < 3; i++) {
roidSprite[i].move(i + 1, 1 - i);//使三个小行星向不同的方向运动
checkBounds(roidSprite[i]);//折屏算法
//第二个小行星精灵roidSprite[1]是反向侦动画,否则是正向侦动画
if (i == 1) roidSprite[i].prevFrame();
else roidSprite[i].nextFrame();
// 检测碰撞
if (ufoSprite.collidesWith(roidSprite[i], true)) {
// 播放警告声音
AlertType.ERROR.playSound(display);
// 碰撞之后重置UFO精灵和移动速度;
ufoSprite.setPosition((getWidth() - ufoSprite.getWidth()) / 2,
(getHeight() - ufoSprite.getHeight()) / 2);
ufoXSpeed = ufoYSpeed = 0;
//重置小行星位置
for (int j = 0; j < 3; j++)
roidSprite[j].setPosition(0, 0);
//检测UFO生命减少是否到可以停止游戏
if ( ufoLife > 0 ) {
ufoLife--;
ufolifeSprite[ufoLife].setPosition(-12,-12);
}
else {
gameoverImg.setPosition(0,0);
for (int j = 0; j < 3; j++)
roidSprite[j].setPosition(-43, -35); //将小行星置于屏幕外
//播放游戏结束声音
try {
gameovermusic.start();
} catch(MediaException me){}
stop();
} //else
// 此时不用更新小行星精灵
break;
}
//检测得分
for(int l = 0; l < 3; l++) {
if (Math.abs(ufoSprite.getX() - roidSprite[l].getX()) < 22 &&
Math.abs(ufoSprite.getY() - roidSprite[l].getY()) < 22) {
AlertType.WARNING.playSound(display);
gx = getWidth()/2;
gy = getHeight()/2;
score += 5;
}
}
}
}
private void draw(Graphics g) {
g.setColor(0x000000);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(255, 255, 255); // white
g.drawString("You 've touched :"+score,sx, sy, Graphics.TOP | Graphics.LEFT);
g.drawString("Good!", gx, gy, Graphics.TOP | Graphics.HCENTER);
ufoSprite.paint(g);
gameoverImg.paint(g);
for (int i = 0; i < 3; i++)
roidSprite[i].paint(g);
for (int k = 0; k < 3; k++)
ufolifeSprite[k].paint(g);
coverImg.paint(g);
flushGraphics();
}
//折屏算法
private void checkBounds(Sprite sprite) {
if (sprite.getX() < -sprite.getWidth())
sprite.setPosition(getWidth(), sprite.getY());
else if (sprite.getX() > getWidth())
sprite.setPosition(-sprite.getWidth(), sprite.getY());
if (sprite.getY() < -sprite.getHeight())
sprite.setPosition(sprite.getX(), getHeight());
else if (sprite.getY() > getHeight())
sprite.setPosition(sprite.getX(), -sprite.getHeight());
}
}
================================================
UFOMIDlet.java文件和之前贴的没有区别,就不贴了。
目前游戏还有一个BUG,就是当UFO精灵和小行星精灵产生“绝妙”的时候会停在绝对距离小于22的那个位置重复运行。这个事情就属于代码活性不强,严格点儿说代码几乎可以说是错的。——虽然由于FrameDelay=33可以投机的避免碰撞之前的瞬间先处理得分机制,不过有时这种碰撞的滞后感还是非常令人恼火的!