贪吃蛇游戏设计与分析
贪吃蛇是一款非常经典的手机游戏,本文将使用MIDP实现这款著名的游戏。
基本概念介绍
节:一条蛇可以看成有许多正方形的“小格子”拼凑成,我把它称作节。
节是蛇身上最小的单位。
段:当许多节连成一条直线,我称它为段。
上图的贪吃蛇只有一段,如果它拐弯就变成两段。
链表:用来保存每一段的状态,链表的元素单位是段。
且链表的最后一个元素表示蛇的头部段。
坐标系:MIDP中的坐标以左上角那点为(0,0),向右则x递增,向下则y 递增。
一条完整的贪吃蛇是由一段一段组成的。
链表中保存的第一个元素是蛇的尾巴段,最后一个元素是蛇的头部段。
当蛇运动的时候,它头部段增加一节而尾段减少一节。
如果它吃到了食物,尾部段就不减少一节。
也就是说,蛇是从头部段开始长的。
SnakeCanvas类中有以下代码:
int[][] snake = new int[200][2];
int snakeNum;
int direction;
private final int DIRECTION_UP = 0;
private final int DIRECTION_DOWN = 1;
private final int DIRECTION_LEFT = 2;
private final int DIRECTION_RIGHT = 3;
先存储贪吃蛇节点坐标,其中第二维下标为0的代表x坐标,第二维下标是1的代表y坐标; int snakeNum 表示已经使用的节点数量.
int direction;
private final int DIRECTION_UP = 0;
private final int DIRECTION_DOWN = 1;
private final int DIRECTION_LEFT = 2;
private final int DIRECTION_RIGHT = 3;
这段代码是设定贪吃蛇的运动方向,0代表向上,1代表向下,2代表向左,3代表向右。
int width; //游戏区域宽度
int height; //游戏区域高度
private final byte SNAKEWIDTH = 4; //蛇身单元宽度
boolean isPaused = false; //是否处于暂停状态,true代表暂停
boolean isRun = true; //是否处于运行状态,true代表运行
private final int SLEEP_TIME = 300; //时间间隔
接下来就是设定食物的坐标:
int foodX; // 食物的X坐标
int foodY; // 食物的Y坐标
boolean b = true; // 食物的闪烁控制
再接着就构建一个构造方法来启动线程:
public SnakeCanvas() {
init(); //初始化
width = this.getWidth();
height = this.getHeight();
new Thread(this).start(); //启动线程
}
然后再初始化开始数据,代码如下:
private void init(){
snakeNum = 7; //初始化节点数量
for(int i = 0;i < snakeNum;i++){ //初始化节点数据
snake[i][0] = 100 - SNAKEWIDTH * i;
snake[i][1] = 40;
}
direction = DIRECTION_RIGHT; //初始化移动方向
foodX = 100; //初始化食物坐标
foodY = 100; //初始化食物坐标
}
然后创建一个画布类来绘制贪吃蛇和食物:
protected void paint(Graphics g) {
g.setColor(0xffffff); //清屏
g.fillRect(0,0,width,height);
g.setColor(0);
for(int i = 0;i < snakeNum;i++){ //绘制蛇身g.fillRect(snake[i][0],snake[i][1],SNAKEWIDTH,SNAKEWIDTH);
}
if(b){ //绘制食物
g.fillRect(foodX,foodY,SNAKEWIDTH,SNAKEWIDTH);
}
}
然后创建一个move类来实现贪吃蛇的移动,贪吃蛇吃掉食物后,自身的长度应该增长,所以创建一个eatFood类来实现这个效果,代码如下:
private void eatFood(){
//判别蛇头是否和食物重叠
if(snake[0][0] == foodX && snake[0][1] == foodY){
snakeNum++;
generateFood();
}
}
游戏中要随机产生食物,并且食物的坐标必须位于屏幕内,且不能和蛇身重合,于是创建一个generateFood 类来实现这个效果,代码如下:
private void generateFood(){
while(true){
foodX = Math.abs(random.nextInt() % (width - SNAKEWIDTH + 1))
/ SNAKEWIDTH * SNAKEWIDTH;
foodY = Math.abs(random.nextInt() % (height - SNAKEWIDTH + 1))
/ SNAKEWIDTH * SNAKEWIDTH;
boolean b = true;
for(int i = 0;i < snakeNum;i++){
if(foodX == snake[i][0] && snake[i][1] == foodY){
b = false;
break;
}
}
if(b){
break;
}
}
}
还必须有一个类来判断游戏是否结束了,结束的条件是蛇头超过边界或者是蛇头碰到自身,于是创建一个isGameOver 类来判断游戏是否结束了,代码如下:private boolean isGameOver(){
//边界判别
if(snake[0][0] < 0 || snake[0][0] > (width - SNAKEWIDTH) ||
snake[0][1] < 0 || snake[0][1] > (height - SNAKEWIDTH)){
return true;
}
//碰到自身
for(int i = 4;i < snakeNum;i++){
if(snake[0][0] == snake[i][0]
&& snake[0][1] == snake[i][1]){
return true;
}
}
return false;
}
游戏中,我们需要用键盘上的上下左右方向键来控制贪吃蛇的移动,所以创建一个keyPressed 类来实现这个功能,代码如下:
public void keyPressed(int keyCode){
int action = this.getGameAction(keyCode);
//改变方向
switch(action){
case UP:
if(direction != DIRECTION_DOWN){
direction = DIRECTION_UP;
}
break;
case DOWN:
if(direction != DIRECTION_UP){
direction = DIRECTION_DOWN;
}
break;
case LEFT:
if(direction != DIRECTION_RIGHT){
direction = DIRECTION_LEFT;
}
break;
case RIGHT:
if(direction != DIRECTION_LEFT){
direction = DIRECTION_RIGHT;
}
break;
case FIRE:
//暂停和继续
isPaused = !isPaused;
break;
}
}
最后再创建一个线程方法,这个贪吃蛇小游戏就完成了,代码如下:public void run(){
try{
while (isRun) {
//开始时间
long start = System.currentTimeMillis();
if(!isPaused){
//吃食物
eatFood();
//移动
move(direction);
//结束游戏
if(isGameOver()){
break;
}
//控制闪烁
b = !b;
}
//重新绘制
repaint();
long end = System.currentTimeMillis();
//延时
if(end - start < SLEEP_TIME){
Thread.sleep(SLEEP_TIME - (end - start));
}
}
}catch(Exception e){}
}
}。