最近刚学J2ME,今天写了个贪吃蛇的游戏,下面提供游戏机制及源代码。
【游戏机制】:玩家控制由多个矩形组成的蛇,通过按键控制蛇的移动。指定按键功能:
2 up
4 left
6 right
8 down
随机显示食物的位置,当蛇头碰到食物时,将身体节点加1,食物消失。用户不做按键,蛇自动按照当前方向向前移动。
【游戏算法】:
1、将窗口按照16*16划分成多个网格
2、实现一个蛇头的按键控制
3、通过线程让蛇头自动移动
4、在窗口中随机出现可吃的食物小块 16的倍数
5、实现方块和可以吃的食物小块的碰撞检测
6、将蛇的身体各块定义为Vector,每个块是一个对象
Node{
int x,
int y,
int nextDir;//下一步要移动的方向
}
7、判定死亡
【游戏源代码】:
import java.util.Random;
import java.util.Vector;
import javax.microedition.lcdui.*;
public class SnakeCanvas extends Canvas {
// 方向标识
private static final int SNAKE_UP = 1;
private static final int SNAKE_DOWN = 2;
private static final int SNAKE_LEFT = 3;
private static final int SNAKE_RIGHT = 4;
private static final int SNAKE_STOP = -1;
private static final int SNAKE_OVER = -2;
private class Node {
int x;
int y;
int nextDir;
}
private Node head;
private Node eat;
private Random r1;// 随机对象
private int screenW;
private int screenH;
private Vector allBody;// 蛇身体
public SnakeCanvas() {
r1 = new Random();
screenW = this.getWidth();
screenH = this.getHeight();
head = new Node();
head.x = r1.nextInt(screenW / 2);// 产生一个0-screenW/2的随机数
head.y = r1.nextInt(screenH / 2);
head.x = head.x / 16 * 16;// 确保16的倍数
head.y = head.y / 16 * 16;
head.nextDir = SNAKE_STOP;// 停止
allBody = new Vector();
eat = new Node();
eat.x = r1.nextInt(screenW / 2);
eat.y = r1.nextInt(screenH / 2);
eat.x = eat.x / 16 * 16;
eat.y = eat.y / 16 * 16;
startGrame();// 开始游戏
}
/** 移动头部* */
public void moveHead() {
switch (head.nextDir) {
case SnakeCanvas.SNAKE_UP:
head.y -= 16;
break;
case SnakeCanvas.SNAKE_LEFT:
head.x -= 16;
break;
case SnakeCanvas.SNAKE_RIGHT:
head.x += 16;
break;
case SnakeCanvas.SNAKE_DOWN:
head.y += 16;
break;
}
}
/** 检测碰撞* */
public void checkEat() {
if (head.x == eat.x && head.y == eat.y) {// 吃上
// 1. 添加到蛇身体的向量中
Node no = new Node();
Node addNode = null;// 准备插入位置
if (allBody.size() == 0) {// 插到头部
addNode = head;
} else {
addNode = (Node) allBody.elementAt(allBody.size() - 1);// 插到最后一个节点
}
no.nextDir = addNode.nextDir;
switch (addNode.nextDir) {
case SnakeCanvas.SNAKE_UP:
no.x = addNode.x;
no.y = addNode.y + 16;
break;
case SnakeCanvas.SNAKE_LEFT:
no.x = addNode.x + 16;
no.y = addNode.y;
break;
case SnakeCanvas.SNAKE_RIGHT:
no.x = addNode.x - 16;
no.y = addNode.y;
break;
case SnakeCanvas.SNAKE_DOWN:
no.x = addNode.x;
no.y = addNode.y - 16;
break;
}
this.allBody.addElement(no);
// 2. 可以吃的小块随机产生
eat.x = r1.nextInt(screenW / 2);
eat.y = r1.nextInt(screenH / 2);
eat.x = eat.x / 16 * 16;
eat.y = eat.y / 16 * 16;
} else if (head.x < 0 || head.x > this.screenW || head.y < 0
|| head.y > this.screenH) {// 死亡检测:碰到墙
head.nextDir = SnakeCanvas.SNAKE_OVER;
} else if (allBody.size() > 0) {// 死亡检测:是否有尾巴
for (int i = 0; i < allBody.size(); i++) {
Node point = (Node) allBody.elementAt(i);
if (head.x == point.x && head.y == point.y) {// 碰到尾巴
head.nextDir = SnakeCanvas.SNAKE_OVER;
}
}
}
}
/** 移动所有蛇的身体* */
public void moveAllNode() {
int dir = head.nextDir;// 记录上一节点的方向,初始为:当前蛇头方向
int tmp = 0;
for (int i = 0; i < allBody.size(); i++) {
Node now = (Node) allBody.elementAt(i);
switch (now.nextDir) {
case SnakeCanvas.SNAKE_UP:
now.y -= 16;
break;
case SnakeCanvas.SNAKE_LEFT:
now.x -= 16;
break;
case SnakeCanvas.SNAKE_RIGHT:
now.x += 16;
break;
case SnakeCanvas.SNAKE_DOWN:
now.y += 16;
break;
}
tmp = now.nextDir;
now.nextDir = dir;
dir = tmp;
}
}
/** 实现游戏中的蛇头的自动移动* */
public void startGrame() {
// 规划线程要执行的任务
Thread t = new Thread() {
public void run() {
while (true) {
// 1. 移动蛇头
moveHead();
// 2. 移动所有蛇的身体
moveAllNode();
// 3. 碰撞检测
checkEat();
// 4. 重新绘制屏幕
repaint();
// 5. 休息
try {
Thread.sleep(800);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
};
if (SnakeCanvas.SNAKE_OVER == head.nextDir) {// 死亡
t.interrupt();
} else {
t.start();
}
}
protected void paint(Graphics g) {
// 画背景
g.setColor(255, 255, 255);
g.fillRect(0, 0, screenW, screenH);
// 画蛇头
g.setColor(0, 0, 0);
g.fillRect(head.x, head.y, 16, 16);
// 画身体
g.setColor(0, 255, 0);
for (int i = 0; i < allBody.size(); i++) {
Node now = (Node) allBody.elementAt(i);// 获取第i个节点
g.fillRect(now.x, now.y, 16, 16);
}
// 画可以吃的小块
g.setColor(255, 0, 0);
g.fillRect(eat.x, eat.y, 16, 16);
if (SnakeCanvas.SNAKE_OVER == head.nextDir) {// 死亡
Font overFont = g.getFont();
int fontH = overFont.getHeight();
int fontW = overFont.stringWidth("Game Over!");
g.drawString("Game Over!", (screenW - fontW) / 2,
(screenH - fontH) / 2, g.TOP | g.LEFT);
}
}
// 相应按键功能( 移动交给线程完成,所以按键只需要控制 下一步移动的方向,就可以了)
public void keyPressed(int n) {
switch (n) {
case Canvas.KEY_NUM2:
head.nextDir = SnakeCanvas.SNAKE_UP;
break;
case Canvas.KEY_NUM4:
head.nextDir = SnakeCanvas.SNAKE_LEFT;
break;
case Canvas.KEY_NUM1:
head.nextDir = SnakeCanvas.SNAKE_LEFT;
break;
case Canvas.KEY_NUM6:
head.nextDir = SnakeCanvas.SNAKE_RIGHT;
break;
case Canvas.KEY_NUM8:
head.nextDir = SnakeCanvas.SNAKE_DOWN;
break;
}
this.repaint();
}
public void keyReleased(int n) {
}
public void keyRepeated(int n) {
}
}
//创建游戏入口类
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class AppMain extends MIDlet {
protected void startApp() throws MIDletStateChangeException {
SnakeCanvas mc = new SnakeCanvas();
Display.getDisplay(this).setCurrent(mc);
}
protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// TODO 自动生成方法存根
}
protected void pauseApp() {
// TODO 自动生成方法存根
}
}