相信大家都玩過Nokia手機上的貪吃蛇游戲。在該游戲中,玩家操縱一條貪吃的蛇在迷宮裡行走,貪吃蛇按玩家所按的方向鍵折行,蛇頭吃到各種食物(比如大力丸)後,會有各種反應(比如蛇身變長),如果貪吃蛇碰上牆壁或者自身的話,就GameOver了(當然也可能是減去一條生命)。
要實現該游戲其實並不麻煩,關鍵就是要找到一個合適的核心算法。本文就給出一個參考實現,你可以基於該demo做擴展。要說明的一點是:本文只演示最核心的算法,要實現一個完整的游戲,你還需要做很多的擴展,重構。
實例代碼
該程序包括3個Java文件。一個是SnakeMIDlet,另外2個分別是一個Canvas(SnakeCanvas)和一個代表貪吃蛇的類Snake: SnakeMIDlet.Java
import Javax.microedition.lcdui.Display;
import Javax.microedition.midlet.MIDlet;
import Javax.microedition.midlet.
MIDletStateChangeException;
/**
* @author JagIE
*/
public class SnakeMIDlet extends MIDlet
{
protected void startApp()
throws MIDletStateChangeException
{
// TODO Auto-generated method stub
Display.getDisplay(this)
.setCurrent(new SnakeCanvas());
}
/* (non-Javadoc)
* @see Javax.microedition
.midlet.MIDlet#pauseApp()
*/
protected void pauseApp()
{
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see Javax.microedition
.midlet.MIDlet#destroyApp(boolean)
*/
protected void destroyApp(boolean arg0)
throws MIDletStateChangeException
{
// TODO Auto-generated method stub
}
}
SnakeCanvas.Java
import Javax.microedition.lcdui.Canvas;
import Javax.microedition.lcdui.Graphics;
/**
* @author JagIE
*
*/
public class SnakeCanvas extends
Canvas implements Runnable
{
Snake snake=new Snake();
SnakeCanvas(){
snake.init();
new Thread(this).start();
}
protected void paint(Graphics g)
{
g.setColor(0);
g.fillRect(0,0,this.getWidth(),
this.getHeight());
snake.paint(g);
}
/**
* 游戲主線程,驅動蛇移動
*/
public void run()
{
while(true){
snake.move();
repaint();
try
{
Thread.sleep(50);
} catch (InterruptedException e)
{
// TODO Auto-generated
catch block
e.printStackTrace();
}
}
}
/**
* 按鍵相應,產生新蛇頭
*/
protected void keyPressed(int c)
{
int ga=this.getGameAction(c);
switch (ga)
{
case Canvas.UP:
snake.breakInto(1);
break;
case Canvas.DOWN:
snake.breakInto(3);
break;
case Canvas.LEFT:
snake.breakInto(4);
break;
case Canvas.RIGHT:
snake.breakInto(2);
break;
}
}
}
Snake.Java
import Java.util.Vector;
import Javax.microedition.lcdui.Graphics;
/**
*
* @author JagIE
* 貪吃蛇
*/
public class Snake
{
//蛇環節,每個環節為一個int[] sec
//sec[0]:環節起點x,sec:
環節起點y,sec:環節方向,sec:
環節長度
Vector sections = new Vector();
/**
* 初始化sections
* 開始的時候,整條蛇只有一段。
*
*/
public void init()
{
int[] head =
{ 10, 10, 2, 50 };
sections.addElement(head);
}
/**
* 繪制
* @param g
*/
public synchronized
void paint(Graphics g)
{
if (sections.isEmpty())
{
return;
}
g.setColor(0, 255, 0);
for (int i = 0; i < sections.size();
i++)
{
int[] sec = (int[])
sections.elementAt(i);
//sec[0]:起點x,sec:
起點y,sec:方向,sec:
長度
switch (sec) {
case 1:
g.drawLine(sec[0], sec,
sec[0], sec - sec);
break;
case 2:
g.drawLine(sec[0], sec,
sec[0] + sec, sec);
break;
case 3:
g.drawLine(sec[0], sec,
sec[0], sec + sec);
break;
case 4:
g.drawLine(sec[0], sec,
sec[0] - sec, sec);
break;
}
}
}
/**
*
* @author JagIE
*
* 蛇的爬行。本質上是蛇頭長度++,蛇尾長度--。
同時移動蛇尾起點。如果蛇尾長度小於0,則去掉蛇尾。
*/
public synchronized void move()
{
if (sections.isEmpty())
{
return;
}
//蛇尾
int[] tail = (int[])
sections.elementAt
(sections.size() - 1);
//蛇頭
int[] head = (int[])
sections.elementAt(0);
//根據蛇尾環節的方向移動蛇尾。
switch (tail)
{
case 1:
tail--;
break;
case 2:
tail[0]++;
break;
case 3:
tail++;
break;
case 4:
tail[0]--;
break;
}
//蛇尾縮短
tail--;
//蛇頭增長
head++;
//蛇尾<0,則去掉蛇尾
if (tail <= 0)
{
sections.removeElement(tail);
}
}
/**
* 蛇分段
* @param dir 新蛇頭的方向
*/
public synchronized void
breakInto(int dir)
{
if (sections.isEmpty())
{
return;
}
int[] head = (int[])
sections.elementAt(0);
//新蛇頭方向和舊蛇頭方向一致,
則無反應。
//TODO 可以考慮加速。
if (dir == head)
{
return;
}
//增加新蛇頭
int[] newhead=new int;
//新蛇頭的起始位置,
與舊蛇頭的運動方向有關。
switch (head)
{
case 1:
newhead[0]=head[0];
newhead=head-head;
newhead=dir;
newhead=0;
//蛇頭總是第一個元素
sections.insertElementAt(newhead, 0);
break;
case 2:
newhead[0]=head[0]+head;
newhead=head;
newhead=dir;
newhead=0;
sections.insertElementAt(newhead, 0);
break;
case 3:
newhead[0]=head[0];
newhead=head+head;
newhead=dir;
newhead=0;
sections.insertElementAt(newhead, 0);
break;
case 4:
newhead[0]=head[0]-head;
newhead=head;
newhead=dir;
newhead=0;
sections.insertElementAt(newhead, 0);
break;
}
}
}