上一次已经做完了第一个简单的J2ME程序,但是可能大家最关心的还是如何用J2ME制作手机游戏吧!不要急,一步一步来,现在就开始做我们的第一个J2ME小游戏吧!
很多人应该都玩过贪吃蛇的游戏吧,那么现在我们就要做一个贪吃蛇的手机游戏了!OK,准备好工具,建立工程,名字就叫做SnakeGame,再建立一个Midlet,名字也是SnakeGame,还要建一个游戏的Screen.起名叫做GameScreen。
首先我们考虑一下游戏中会出现的算法,首先,贪吃蛇应该是可以在屏幕上运动的(好像是废话?),而它的身体长度又是随着游戏的进行而变化的,因此,这里我们用一个数组来保存贪吃蛇每个身体单元(可以理解为身体的一小段)在屏幕中的位置,为什么采用数组而不采用Vector呢,因为Vector效率没有直接用数组高,J2ME程序由于资源的限制是要把效率放在首位的。数组定义好了,接下来就是如何控制贪吃蛇运动。首先贪吃蛇始终要有一个运动方向,或上或下或左或右,而四个方向对于屏幕的坐标变化是各不相同的,但我们可以找出些规律来利用。我们用direction表示贪吃蛇运动的方向,比如向上,direction=0,向下direction=1,左direction=2,右direction=3,然后定义两个对应坐标变化的数组,
dirX[] = { 0, 0, -1, 1};
dirY[] = { -1, 1, 0, 0};
这样,贪吃蛇的头部的下一个坐标就可以用(dirX[direction],dirY[direction])来表示。
每次运动,头部都会获得一个新的坐标,而后面的身体依次前进,第i+1块身体对应于运动之前的第i块身体。运动的过程可以如下表示:
int newX, newY;
newX = posX + dirX[direction];
newY = posY + dirY[direction];
for (int i = count - 1; i > 0; i--) {
snakeData[i].x = snakeData[i - 1].x;
snakeData[i].y = snakeData[i - 1].y;
}
snakeData[0].x = newX;
snakeData[0].y = newY;
posX = newX;
posY = newY;
这样,贪吃蛇的运动就可以很简单的实现了。
接下来就是遇到它喜欢吃的东西的时候的处理了,也很简单,就是相当于加了一个头部(其实是数组多了一个数据)。
为了提高游戏的难度,这里的规则是每次吃掉一个食物,就会随机出现一个新的食物和障碍物。因此在游戏进行当中,食物是只有一个(当然也可以修改程序让贪吃蛇有更多的食物),而障碍物是一直增加的。
还有就是按键的处理,这里并没有用Cavans的事件处理,而是用了一个专门为游戏设计的GameCavans类,在一个线程中来获取按键的状态和处理贪吃蛇的位置以及其他游戏的信息。
有了这些准备,做一个类似的小游戏还是比较简单的。下边是程序源代码:
package snakegame;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.GameCanvas;
import java.util.Random;
import java.io.*;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.*;
/**
*
* <p>Title: 贪吃蛇坐标的类</p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2005</p>
* <p>Company: </p>
* @author not attributable
* @version 1.0
*/
class SnakePos {
int x, y;
public SnakePos(int x, int y) {
this.x = x;
this.y = y;
}
}
/**
*
* <p>Title: 游戏屏幕</p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2005</p>
* <p>Company: </p>
* @author 大糊涂
* @version 1.0
*/
public class GameScreen
extends GameCanvas
implements Runnable, CommandListener {
private int DIR_UP = 0;//定义方向,上
private int DIR_DOWN = 1;//下
private int DIR_LEFT = 2;//左
private int DIR_RIGHT = 3;//右
private int dirX[] = {
0, 0, -1, 1};//X方向变化数据
private int dirY[] = {
-1, 1, 0, 0};//Y方向变化数据
private int direction = DIR_RIGHT;//贪吃蛇的行走方向
private int posX, posY;//贪吃蛇当前头的位置
private int gameWidth, gameHeight;//游戏画面的宽度和高度
private SnakePos[] snakeData;//贪吃蛇身体的坐标数组
private int count = 4;//初始贪吃蛇身体的长度
private int width = 4, height = 4;//每个身体段的长度和高度
private int time = 100;//延迟
private SnakePos block;//食物的坐标
private SnakePos cannot[];//障碍物的坐标数组
private int cannotCount = 0;//障碍物数量
private Thread thread;//游戏进程
private int level = 1;//游戏登记
private int score = 0;//游戏分数
private Command exit;//退出按钮
private Command ok;//开始按钮
private int status = 0;//游戏状态,0:未开始 1:游戏中 2:暂停中
private Image img,bgimg;//开始画面和背景图
private Graphics g;
/**
* 构造函数
* @param suppressKeyEvents boolean
*/
GameScreen(boolean suppressKeyEvents) {
super(suppressKeyEvents);
ok = new Command("开始", Command.OK, 2);
exit = new Command("退出", Command.EXIT, 1);
this.addCommand(ok);
this.addCommand(exit);
this.setCommandListener(this);
g = this.getGraphics();
try {
img = Image.createImage("/title.png");//开始画面
bgimg = Image.createImage("/bg.png");//背景
g.drawImage(img,0,0,Graphics.LEFT|Graphics.TOP);
}
catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* 开始游戏
*/
private void start() {
this.setFullScreenMode(false);//设置为非全屏模式
gameWidth = getWidth();//游戏屏幕宽度
gameHeight = getHeight() - 16;//高度,因为需要留出空间给信息,因此减去了16
int x = gameWidth / width;
int y = gameHeight / height;
posX = x / 2;
posY = y / 2;
snakeData = null;
cannot = null;
System.gc();//垃圾回收
snakeData = new SnakePos[x * y];
cannot = new SnakePos[x * y];
snakeData[0] = new SnakePos(posX, posY);
snakeData[1] = new SnakePos(posX - 1, posY);
snakeData[2] = new SnakePos(posX - 2, posY);
snakeData[3] = new SnakePos(posX - 3, posY);
count = 4;
cannotCount = 0;
direction = DIR_RIGHT;
putBlock();
score = 0;
level = 1;
}
/**
* 在游戏中显示信息
* @param str String 信息内容
* @param g Graphics
*/
private void drawMessage(String str, Graphics g) {
g.setColor(255,255,255);
g.fillRect(0,getHeight()-15,getWidth(),getHeight());
g.setColor(0,0,255);
g.drawString(str, 5, getHeight() - 16, Graphics.TOP | Graphics.LEFT);
}
/**
* 检查贪吃蛇在x,y处是否合法
* @param x int
* @param y int
* @return boolean
*/
private boolean check(int x, int y) {
if (!canPut(x, y)) {
if (block != null)
if (block.x == x && block.y == y)
return true;
return false;
}
return true;
}
/**
* 判断在xy处是否可以放置食物或者障碍物
* @param x int
* @param y int
* @return boolean
*/
private boolean canPut(int x, int y) {
System.out.println("x=" + x + " y=" + y);
for (int i = 0; i < count; i++) {
if (x == snakeData[i].x && y == snakeData[i].y)
return false;
}
for (int i = 0; i < cannotCount; i++) {
if (x == cannot[i].x && y == cannot[i].y)
return false;
}
if (block != null)
if (x == block.x && y == block.y)
return false;
if (x <= 0 || y <= 0)
return false;
if (x >= gameWidth / width - 1 || y >= gameHeight / height - 1)
return false;
return true;
}
/**
* 放置食物和障碍物
*/
private void putBlock() {
Random rnd = new Random();
block = null;
int x = Math.abs(rnd.nextInt()) % (gameWidth / width - 1);
int y = Math.abs(rnd.nextInt()) % (gameHeight / height - 1);
while (!canPut(x, y)) {
x = Math.abs(rnd.nextInt()) % (gameWidth / width - 1);
y = Math.abs(rnd.nextInt()) % (gameHeight / height - 1);
}
block = new SnakePos(x, y);
x = Math.abs(rnd.nextInt()) % (gameWidth / width - 1);
y = Math.abs(rnd.nextInt()) % (gameHeight / height - 1);
while (!canPut(x, y)) {
x = Math.abs(rnd.nextInt()) % (gameWidth / width - 1);
y = Math.abs(rnd.nextInt()) % (gameHeight / height - 1);
}
cannot[cannotCount] = new SnakePos(x, y);
cannotCount++;
}
/**
* 画游戏画面和贪吃蛇等
* @param g Graphics
*/
private void drawSnake(Graphics g) {
g.setColor(255,255,255);
g.drawRect(0,0,getWidth(),getHeight());
g.drawImage(bgimg,0,0,Graphics.LEFT|Graphics.TOP);
g.setColor(255, 0, 0);
g.drawRect(0, 0, gameWidth, gameHeight);
g.drawRect(width - 1, height - 1, gameWidth - width * 2 + 1,
gameHeight - height * 2 + 1);
g.setColor(200, 5, 100);
for (int i = 0; i < count; i++) {
g.fillRect(snakeData[i].x * width, snakeData[i].y * height, width, height);
}
if (block != null) {
Random rnd = new Random();
int rr = Math.abs(rnd.nextInt())%255;
int gg = Math.abs(rnd.nextInt())%255;
int bb = Math.abs(rnd.nextInt())%255;
g.setColor(rr, gg , bb);
g.fillRect(block.x * width, block.y * height, width, height);
}
for (int i = 0; i < cannotCount; i++) {
g.setColor(0, 0, 0);
g.fillRect(cannot[i].x * width, cannot[i].y * height, width, height);
}
drawMessage("Level:" + level + " Score:" + score, g);
}
/**
* run 游戏线程
*/
public void run() {
start();//初始化
status = 1;//设置状态
while (status != 0) {
if (status != 2) {
g = this.getGraphics();
int key = getKeyStates();//获取键盘状态
if (key != 0) {
if ( (key & UP_PRESSED) != 0) {
direction = DIR_UP;
}
else if ( (key & DOWN_PRESSED) != 0) {
direction = DIR_DOWN;
}
else if ( (key & LEFT_PRESSED) != 0) {
direction = DIR_LEFT;
}
else if ( (key & RIGHT_PRESSED) != 0) {
direction = DIR_RIGHT;
}
}
int newX, newY;
//获取新坐标
newX = posX + dirX[direction];
newY = posY + dirY[direction];
if (!check(newX, newY)) {
break;
}
//如果遇到了食物,吃掉它
if (newX == block.x && newY == block.y) {
count++;
snakeData[count - 1] = new SnakePos(0, 0);
snakeData[count - 1].x = snakeData[count - 2].x;
snakeData[count - 1].y = snakeData[count - 2].y;
putBlock();
score += 10;
if (score == level * 100) {//每级100分
level++;
time -= 10;//每升级一次速度加快
}
}
for (int i = count - 1; i > 0; i--) {
snakeData[i].x = snakeData[i - 1].x;
snakeData[i].y = snakeData[i - 1].y;
}
snakeData[0].x = newX;
snakeData[0].y = newY;
drawSnake(g);
this.flushGraphics();
posX = newX;
posY = newY;
}
try {
Thread.sleep(time);
}
catch (InterruptedException ex) {
}
}
removeCommand(ok);
ok = new Command("开始",Command.OK,2);
addCommand(ok);
drawMessage("您的得分:" + score,g);
status = 0;
this.flushGraphics();
}
/**
* 处理命令按钮
* @param command Command
* @param displayable Displayable
*/
public void commandAction(Command command, Displayable displayable) {
if (command == ok) {
System.out.println("OK");
if (status == 0) {
thread = new Thread(this);
thread.start();
this.removeCommand(ok);
ok = new Command("暂停", Command.OK, 2);
addCommand(ok);
}
else
if (status == 1) {
status = 2;
this.removeCommand(ok);
ok = new Command("继续", Command.OK, 2);
addCommand(ok);
}
else if (status == 2) {
status = 1;
this.removeCommand(ok);
ok = new Command("暂停", Command.OK, 2);
addCommand(ok);
}
}
if(command == exit){
SnakeGame.quitApp();
}
}
}
游戏运行画面:
在我自己的阿而卡特OT756上已经通过测试