概述


  相信大家都玩过Nokia手机上的贪吃蛇游戏。在该游戏中,玩家操纵一条贪吃的蛇在迷宫里行走,贪吃蛇按玩家所按的方 向键折行,蛇头吃到各种食物(比如大力丸)后,会有各种反应(比如蛇身变长),如果贪吃蛇碰上墙壁或者自身的话,就GameOver了(当然也可能是减去 一条生命)。要实现该游戏其实并不麻烦,关键就是要找到一个合适的核心算法。本文就给出一个参考实现,你可以基于该demo做扩展。要说明的一点是:本文 只演示最核心的算法,要实现一个完整的游戏,你还需要做很多的扩展,重构。

实例代码


  该程序包括3个java文件。一个是SnakeMIDlet,另外2个分别是一个Canvas(SnakeCanvas)和一个代表贪吃蛇的类Snake

SnakeMIDlet.java



  1. import javax.microedition.lcdui.Display;
  2. import javax.microedition.midlet.MIDlet;
  3. import javax.microedition.midlet.MIDletStateChangeException;


  4. /**
  5.  * @author Jagie
  6.  */
  7. public class SnakeMIDlet extends MIDlet {

  8.     
  9.     protected void startApp() throws MIDletStateChangeException {
  10.         // TODO Auto-generated method stub
  11.         Display.getDisplay(this).setCurrent(new SnakeCanvas());

  12.     }

  13.     /* (non-Javadoc)
  14.      * @see javax.microedition.midlet.MIDlet#pauseApp()
  15.      */
  16.     protected void pauseApp() {
  17.         // TODO Auto-generated method stub

  18.     }

  19.     /* (non-Javadoc)
  20.      * @see javax.microedition.midlet.MIDlet#destroyApp(boolean)
  21.      */
  22.     protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
  23.         // TODO Auto-generated method stub

  24.     }

  25. }


SnakeCanvas.java



  1. import javax.microedition.lcdui.Canvas;
  2. import javax.microedition.lcdui.Graphics;


  3. /**
  4.  * @author Jagie
  5.  *
  6.  */
  7. public class SnakeCanvas extends Canvas implements Runnable {


  8.     
  9.     Snake snake=new Snake();
  10.     SnakeCanvas(){
  11.         snake.init();
  12.         new Thread(this).start();
  13.     }

  14.     protected void paint(Graphics g) {
  15.         
  16.         g.setColor(0);
  17.         g.fillRect(0,0,this.getWidth(),this.getHeight());
  18.         
  19.         snake.paint(g);

  20.     }
  21.     
  22.     /**
  23.      * 游戏主线程,驱动蛇移动
  24.      */
  25.     
  26.     public void run(){
  27.         while(true){
  28.             
  29.             snake.move();
  30.             repaint();
  31.             
  32.             try {
  33.                 Thread.sleep(50);
  34.             } catch (InterruptedException e) {
  35.                 // TODO Auto-generated catch block
  36.                 e.printStackTrace();
  37.             }
  38.         }
  39.     }

  40.     /**
  41.      * 按键相应,产生新蛇头
  42.      */
  43.     protected void keyPressed(int c) {
  44.         int ga=this.getGameAction(c);
  45.         
  46.         switch (ga) {
  47.         case Canvas.UP:
  48.             snake.breakInto(1);
  49.             break;

  50.         case Canvas.DOWN:
  51.             snake.breakInto(3);
  52.             break;
  53.         case Canvas.LEFT:
  54.             snake.breakInto(4);
  55.             break;
  56.         case Canvas.RIGHT:
  57.             snake.breakInto(2);
  58.             break;
  59.         }
  60.     }
  61. }


Snake.java



  1. import java.util.Vector;


  2. import javax.microedition.lcdui.Graphics;

  3. /**
  4.  * 
  5.  * @author Jagie
  6.  * 贪吃蛇
  7.  */
  8. public class Snake {
  9.     //蛇环节,每个环节为一个int[] sec
  10.     //sec[0]:环节起点x,sec[1]:环节起点y,sec[2]:环节方向,sec[3]:环节长度
  11.     Vector sections = new Vector();
  12.     
  13.     /**
  14.      * 初始化sections
  15.      * 开始的时候,整条蛇只有一段。
  16.      *
  17.      */
  18.     public void init() {
  19.         int[] head = { 10, 10, 2, 50 };
  20.         sections.addElement(head);
  21.     }
  22.     
  23.     /**
  24.      * 绘制
  25.      * @param g
  26.      */
  27.     public synchronized void paint(Graphics g) {
  28.         if (sections.isEmpty()) {
  29.             return;
  30.         }
  31.         g.setColor(0, 255, 0);
  32.         for (int i = 0; i < sections.size(); i++) {
  33.             int[] sec = (int[]) sections.elementAt(i);
  34.             //sec[0]:起点x,sec[1]:起点y,sec[2]:方向,sec[3]:长度
  35.             switch (sec[2]) {
  36.             case 1:
  37.                 g.drawLine(sec[0], sec[1], sec[0], sec[1] - sec[3]);
  38.                 break;
  39.             case 2:
  40.                 g.drawLine(sec[0], sec[1], sec[0] + sec[3], sec[1]);
  41.                 break;
  42.             case 3:
  43.                 g.drawLine(sec[0], sec[1], sec[0], sec[1] + sec[3]);
  44.                 break;
  45.             case 4:
  46.                 g.drawLine(sec[0], sec[1], sec[0] - sec[3], sec[1]);
  47.                 break;
  48.             }

  49.         }
  50.     }

  51.     /**
  52.      * 
  53.      * @author Jagie
  54.      *
  55.      * 蛇的爬行。本质上是蛇头长度++,蛇尾长度--.同时移动蛇尾起点。如果蛇尾长度小于0,则去掉蛇尾。
  56.      */
  57.     public synchronized void move() {
  58.         if (sections.isEmpty()) {
  59.             return;
  60.         }
  61.         //蛇尾
  62.         int[] tail = (int[]) sections.elementAt(sections.size() - 1);
  63.         //蛇头
  64.         int[] head = (int[]) sections.elementAt(0);
  65.         //根据蛇尾环节的方向移动蛇尾。
  66.         switch (tail[2]) {
  67.         case 1:
  68.             tail[1]--;
  69.             break;
  70.         case 2:
  71.             tail[0]++;
  72.             break;
  73.         case 3:
  74.             tail[1]++;
  75.             break;
  76.         case 4:
  77.             tail[0]--;
  78.             break;
  79.         }
  80.         //蛇尾缩短
  81.         tail[3]--;
  82.         //蛇头增长
  83.         head[3]++;
  84.         //蛇尾<0,则去掉蛇尾
  85.         if (tail[3] <= 0) {
  86.             sections.removeElement(tail);
  87.         }
  88.     }
  89.     
  90.     /**
  91.      * 蛇分段
  92.      * @param dir 新蛇头的方向
  93.      */

  94.     public synchronized void breakInto(int dir) {
  95.         if (sections.isEmpty()) {
  96.             return;
  97.         }
  98.         int[] head = (int[]) sections.elementAt(0);
  99.         //新蛇头方向和旧蛇头方向一致,则无反应。
  100.         //TODO 可以考虑加速。
  101.         if (dir == head[2]) {
  102.             return;
  103.         }
  104.         //增加新蛇头
  105.         int[] newhead=new int[4];
  106.         //新蛇头的起始位置,与旧蛇头的运动方向有关。
  107.         switch (head[2]) {
  108.         case 1:
  109.             newhead[0]=head[0];
  110.             newhead[1]=head[1]-head[3];
  111.             newhead[2]=dir;
  112.             newhead[3]=0;
  113.             //蛇头总是第一个元素
  114.             sections.insertElementAt(newhead, 0);
  115.             break;
  116.         case 2:
  117.             newhead[0]=head[0]+head[3];
  118.             newhead[1]=head[1];
  119.             newhead[2]=dir;
  120.             newhead[3]=0;
  121.             sections.insertElementAt(newhead, 0);
  122.             break;
  123.         case 3:
  124.             newhead[0]=head[0];
  125.             newhead[1]=head[1]+head[3];
  126.             newhead[2]=dir;
  127.             newhead[3]=0;
  128.             sections.insertElementAt(newhead, 0);
  129.             break;
  130.         case 4:
  131.             newhead[0]=head[0]-head[3];
  132.             newhead[1]=head[1];
  133.             newhead[2]=dir;
  134.             newhead[3]=0;
  135.             sections.insertElementAt(newhead, 0);
  136.             break;
  137.         }
  138.         

  139.     }

  140. }


小结



本文给出了一个简单的贪吃蛇的例子,演示了贪吃蛇游戏核心的一种参考算法

作者简介


陈万飞,网名Jagie,培训师,爱好java技术.可通过该邮件地址已受到反垃圾邮件插件保护。要显示它需要在浏览器中启用 JavaScript。与他联系.