这个系列的前四篇文章介绍了Android sdk中自带的NotePad程序,算是开了个头,这篇开始介绍Android sdk中自带的第二个示例程序—Snake(贪食蛇)。本文将主要介绍我对这个示例程序进行的一些修改。

游戏暂停/继续机制

      由于原来的代码中在游戏运行时没有提供控制选项(比如暂停/继续),因此除非你死了,否则只能玩到底。我这里对代码进行一些修改,加入一个Option Menu来提供暂停/继续机制。

      首先加入一个变量记录游戏当前状态

  1. private int mState = SnakeView.READY; 

  然后重载onCreateOptionsMenu函数,创建一个控制菜单项,并对其进行处理,提供暂停/继续机制。

  1. /*  
  2.   * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)  
  3.   * @Author:phinecos  
  4.   * @Date:2009-08-28  
  5.   */ 
  6.  @Override 
  7.  public boolean onOptionsItemSelected(MenuItem item)  
  8.  {  
  9.      switch (item.getItemId())  
  10.      {  
  11.          case MENU_CONTROL:  
  12.          {  
  13.              if (mState == SnakeView.PAUSE)  
  14.              {//此前状态是"停止",则转为"运行"  
  15.                  mState = SnakeView.RUNNING;  
  16.                  mSnakeView.setMode(SnakeView.RUNNING);  
  17.                  item.setIcon(android.R.drawable.ic_media_pause).setTitle(R.string.cmd_pause);  
  18.              }  
  19.              else if(mState == SnakeView.RUNNING)  
  20.              {//此前状态是"运行",则转为“暂停"  
  21.                  mState = SnakeView.PAUSE;  
  22.                  mSnakeView.setMode(SnakeView.PAUSE);  
  23.                  item.setIcon(android.R.drawable.ic_media_play).setTitle(R.string.cmd_run);  
  24.              }  
  25.              else if(mState == SnakeView.READY)  
  26.              {//此前是"初始状态",则转为"运行"  
  27.                  mState = SnakeView.RUNNING;  
  28.              }  
  29.  return true;  
  30.          }  
  31.      }  
  32.      return super.onOptionsItemSelected(item);  
  33.  }  
  34.  /*  
  35.   * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)  
  36.   * @Author:phinecos  
  37.   * @Date:2009-08-28  
  38.   */ 
  39.  @Override 
  40.  public boolean onCreateOptionsMenu(Menu menu)   
  41.  {  
  42.       super.onCreateOptionsMenu(menu);  
  43.       menu.add(0, MENU_CONTROL, 0, R.string.cmd_pause).setIcon(android.R.drawable.ic_media_pause);  
  44.       return true;  
  45.  } 

修改后运行截图如下:

 

 

当然,这段代码还是有问题的,游戏刚开始时,必须先点击菜单确认,再按上方向键才能开始。(以后再来修改。。。)

穿墙贪食蛇

      第二个修改是把这个普通的贪食蛇改成可以穿墙(呵呵,这样就可以不死了。。。)。想必要修改的就是撞墙检测那段代码?没错,就是下面这段!

  1. // Collision detection  
  2. // For now we have a 1-square wall around the entire arena  
  3. if ((newHead.x < 1) || (newHead.y < 1) || (newHead.x > mXTileCount - 2)|| (newHead.y > mYTileCount - 2))   
  4. {//撞墙  
  5.      setMode(LOSE);  
  6.      return;  

原来的版本是发现撞墙时就直接判定为失败,我这里做个小小的修改,让它可以穿墙而去:

  1. private void updateSnake()  
  2.     {  
  3.         boolean growSnake = false;  
  4.  
  5.         // grab the snake by the head  
  6.         Coordinate head = mSnakeTrail.get(0);  
  7.         Coordinate newHead = new Coordinate(1, 1);  
  8.  
  9.         mDirection = mNextDirection;  
  10.  
  11.         switch (mDirection)   
  12.         {  
  13.         case EAST:   
  14.         {  
  15.             newHead = new Coordinate(head.x + 1, head.y);  
  16.             break;  
  17.         }  
  18.         case WEST:   
  19.         {  
  20.             newHead = new Coordinate(head.x - 1, head.y);  
  21.             break;  
  22.         }  
  23.         case NORTH:   
  24.         {  
  25.             newHead = new Coordinate(head.x, head.y - 1);  
  26.             break;  
  27.         }  
  28.         case SOUTH:   
  29.         {  
  30.             newHead = new Coordinate(head.x, head.y + 1);  
  31.             break;  
  32.         }  
  33.         }  
  34.  
  35.         //穿墙的处理  
  36.         if (newHead.x == 0)  
  37.         {//穿左边的墙  
  38.             newHead.x = mXTileCount - 2;  
  39.         }  
  40.         else if (newHead.y == 0)  
  41.         {//穿上面的墙  
  42.             newHead.y = mYTileCount - 2;  
  43.         }  
  44.         else if (newHead.x == mXTileCount - 1)  
  45.         {//穿右边的墙  
  46.             newHead.x = 1;  
  47.         }  
  48.         else if (newHead.y == mYTileCount - 1)  
  49.         {//穿下面的墙  
  50.             newHead.y = 1;  
  51.         }  
  52.         // 判断是否撞到自己  
  53.         int snakelength = mSnakeTrail.size();  
  54.         for (int snakeindex = 0; snakeindex < snakelength; snakeindex++)   
  55.         {  
  56.             Coordinate c = mSnakeTrail.get(snakeindex);  
  57.             if (c.equals(newHead))  
  58.             {  
  59.                 setMode(LOSE);  
  60.                 return;  
  61.             }  
  62.         }  
  63.         // 判断是否吃掉“苹果”  
  64.         int applecount = mAppleList.size();  
  65.         for (int appleindex = 0; appleindex < applecount; appleindex++)  
  66.         {  
  67.             Coordinate c = mAppleList.get(appleindex);  
  68.             if (c.equals(newHead))   
  69.             {  
  70.                 mAppleList.remove(c);  
  71.                 addRandomApple();  
  72.                 mScore++;  
  73.                 mMoveDelay *= 0.9;  
  74.                 growSnake = true;  
  75.             }  
  76.         }  
  77.         // push a new head onto the ArrayList and pull off the tail  
  78.         mSnakeTrail.add(0, newHead);  
  79.         // except if we want the snake to grow  
  80.         if (!growSnake)   
  81.         {  
  82.             mSnakeTrail.remove(mSnakeTrail.size() - 1);  
  83.         }  
  84.         int index = 0;  
  85.         for (Coordinate c : mSnakeTrail)   
  86.         {  
  87.             if (index == 0)  
  88.             {  
  89.                 setTile(YELLOW_STAR, c.x, c.y);  
  90.             }   
  91.             else   
  92.             {  
  93.                 setTile(RED_STAR, c.x, c.y);  
  94.             }  
  95.             index++;  
  96.         }  

    其实修改后的代码非常简单,就是把新节点的值做些处理,让它移动到对应的行/列的头部或尾部即可。

下面就是修改后的穿墙贪食蛇的运行截图:

 

全屏机制

      游戏一般都是全屏的,原始代码也考虑到标题栏太过难看了,于是使用下面这句代码就去掉了标题栏:

  1. requestWindowFeature(Window.FEATURE_NO_TITLE); 

可还是没有达到全屏的效果,在Android1.5中实现全屏效果非常简单,只需要一句代码即可实现:

  1. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 

运行效果如下图所示:

   

接下来的修改目标是加入得分排行榜机制,再加入一个启动画面和选择菜单。当然,这一篇文章水了点,仅仅记录了自己做的一些小实验,并没有涉及到Snake的代码分析,不过请继续期待下一篇文章。。。