开发Android的贪吃蛇游戏

[ 39143 / 339 / 40 ]

最近闲来无事,一心学习Android,移动技术开发。言归正传。

项目结果如下:

开发Android的贪吃蛇游戏_26193

src源码目录:

Snake.java为主界面类。

SnakeView 为贪吃蛇类的视图主要逻辑控制和绘制类。

TitleView 为界面的整体视图。

gen系统自动生成的常量类信息

R为系统根据layout,values等配置文件自动生成的常量类。

res为资源目录

snake_layout.xml为贪吃蛇的主要图示的界面元素配置类。

attrs.xml和strings.xml为字符串常量t配置类。

AndroidManifest.xml为Android项目的配置类。

tests为Android测试目录。

运行结果如下:

开发Android的贪吃蛇游戏_26194
TOP

snake类为Activty的类,是Android项目的主类。
  1. package com.easyway.dev.android.snake;

  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.Window;
  5. import android.widget.TextView;

  6. /**
  7. * 贪吃蛇游戏(代码来源自Android的源代码中)
  8. *
  9. * 每一种移动开发环境都有自己的基类。如J2ME应用程序的基类是midlets,BREW的基类是applets,
  10. * 而Android程序的基类是Activity。这个activity为我们提供了对移动操作系统的基本功能和事件
  11. * 的访问。这个类包含了基本的构造方法,键盘处理,挂起来恢复功能,以及其他底层的手持设备的访问。
  12. * 实质上,我们的应用程序将是一个Activity类的扩展。在本文中读者将会通过例子学习到如何使用
  13. * Activity类来编写Android程序。
  14. *
  15. * 这事Android的Activty类的子类,一个Activty类是一个简单的启动程序和控制程序的类。
  16. * 它可以根据需要创建界面,但是不是必须。在Android程序中,用户界面是由叫做views类来组织的,一个
  17. * view可以简单理解为可以绘制的对象,
  18. */
  19. public class Snake extends Activity {

  20.     private SnakeView mSnakeView;
  21.    
  22.     private static String ICICLE_KEY = "snake-view";

  23.    
  24.     /**
  25.     * onCreate方法将在应用程序第一次开始时调用。Bundle对象包含了任何用于建立参数或环境数据
  26.     * 所需要的基本信息。Activity可以是全屏的,或是悬浮的。它们可以是嵌套的,但是每一部分基
  27.     * 本是独立的。
  28.     *
  29.     * 在Activity类被调用时首先被创建,关闭标题栏,设置视图内容,激活视图页面
  30.     * Called when Activity is first created. Turns off the title bar, sets up
  31.     * the content views, and fires up the SnakeView.
  32.     *
  33.     */
  34.     @Override
  35.     public void onCreate(Bundle savedInstanceState) {
  36.         super.onCreate(savedInstanceState);
  37.         //setTitle("程序标题");

  38.         // No Title bar
  39.         //设置标题栏
  40.         requestWindowFeature(Window.FEATURE_NO_TITLE);
  41.         //Activty的setContentView()方法指示系统要用哪个view作为Activty的界面,
  42.         //如果一个Activty类的没有执行这个方法,将会没有界面并且显示白屏。
  43.         setContentView(R.layout.snake_layout);
  44.         //查找设置视图
  45.         mSnakeView = (SnakeView) findViewById(R.id.snake);
  46.         mSnakeView.setTextView((TextView) findViewById(R.id.text));
  47.         //视图状态的为空创建一个新视图,设置为准备状态
  48.         if (savedInstanceState == null) {
  49.             // We were just launched -- set up a new game
  50.             mSnakeView.setMode(SnakeView.READY);
  51.         } else {
  52.             // We are being restored
  53.             //获取资源的信息
  54.             Bundle map = savedInstanceState.getBundle(ICICLE_KEY);
  55.             if (map != null) {
  56.                 mSnakeView.restoreState(map);
  57.             } else {
  58.                 //暂停状态
  59.                 mSnakeView.setMode(SnakeView.PAUSE);
  60.             }
  61.         }
  62.     }
  63.     /**
  64.     * 暂停的操作
  65.     */
  66.     @Override
  67.     protected void onPause() {
  68.         super.onPause();
  69.         // Pause the game along with the activity
  70.         mSnakeView.setMode(SnakeView.PAUSE);
  71.     }
  72.    
  73.     @Override
  74.     public void onSaveInstanceState(Bundle outState) {
  75.         //Store the game state
  76.         outState.putBundle(ICICLE_KEY, mSnakeView.saveState());
  77.     }

  78. }
复制代码
TOP

视图VIew的类的Snake的,主要关注的学习的类。
  1. package com.easyway.dev.android.snake;

  2. import java.util.ArrayList;
  3. import java.util.Random;

  4. import android.content.Context;
  5. import android.content.res.Resources;
  6. import android.os.Bundle;
  7. import android.os.Handler;
  8. import android.os.Message;
  9. import android.util.AttributeSet;
  10. import android.util.Log;
  11. import android.view.KeyEvent;
  12. import android.view.View;
  13. import android.widget.TextView;

  14. /**
  15. * View类是Android的一个超类,这个类几乎包含了所有的屏幕类型。但它们之间有一些不同。每一个view
  16. * 都有一个用于绘画的画布。这个画布可以用来进行任意扩展。
  17. *
  18. * 一个项目的R.java文件是一个定义所有资源的索引文件。 使用这个类就像使用一种速记方式来引用你项
  19. * 目中包含的资源。这个有点特别的强大像对于Eclipse这类IDE的代码编译特性,因为它使你快速的,互动
  20. * 式的定位你正在寻找的特定引用。
  21. *
  22. * 到目前需要注意的重要事情是叫做”layout”的内部类和他的成员变量”main”, 插件会通知你添加一个新
  23. * 的XML布局文件,然后从新产生这个R.java文件,比如你添加了新的资源到你的项目,你将会看到R.java
  24. * 也相应的改变了。放在你的项目目录的res/ 文件夹下。 “res”是”resources”的缩写,它是存放所有非
  25. * 代码资源的文件夹,包含象图片,本地化字符串和XML布局文件。
  26. *
  27. *
  28. * SnakeView: implementation of a simple game of Snake
  29. * 创建的view中一般要传入一个Context的实例,Context 可以控制系统的调用,它提供了诸如资源解析
  30. * ,访问数据库等。Activty类继承自Context类。
  31. *
  32. * 视图也可以被嵌套,但和J2ME不同,我们可以将定制的视图和Android团队发布的Widgets一起使用。
  33. * 在J2ME中,开发人员被迫选择GameCanvas和J2ME应用程序画布。这就意味着如果我们想要一个定制
  34. * 的效果,就必须在GameCanvas上重新设计我们所有的widget。Android还不仅仅是这些,视图类型
  35. * 也可以混合使用。Android还带了一个widget库,这个类库包括了滚动条,文本实体,进度条以及其
  36. * 他很多控件。这些标准的widget可以被重载或被按着我们的习惯定制。
  37. *
  38. */
  39. public class SnakeView extends TileView {

  40.     private static final String TAG = "SnakeView";

  41.     /**
  42.     * Current mode of application: READY to run, RUNNING, or you have already
  43.     * lost. static final ints are used instead of an enum for performance
  44.     * reasons.
  45.     */
  46.     private int mMode = READY;
  47.     public static final int PAUSE = 0;
  48.     public static final int READY = 1;
  49.     public static final int RUNNING = 2;
  50.     public static final int LOSE = 3;

  51.     /**
  52.     * 设置方向
  53.     * Current direction the snake is headed.
  54.     */
  55.     private int mDirection = NORTH;
  56.     private int mNextDirection = NORTH;
  57.     private static final int NORTH = 1;
  58.     private static final int SOUTH = 2;
  59.     private static final int EAST = 3;
  60.     private static final int WEST = 4;

  61.     /**
  62.     * Labels for the drawables that will be loaded into the TileView class
  63.     */
  64.     private static final int RED_STAR = 1;
  65.     private static final int YELLOW_STAR = 2;
  66.     private static final int GREEN_STAR = 3;

  67.     /**
  68.     * mScore: used to track the number of apples captured mMoveDelay: number of
  69.     * milliseconds between snake movements. This will decrease as apples are
  70.     * captured.
  71.     */
  72.     private long mScore = 0;
  73.     private long mMoveDelay = 600;
  74.     /**
  75.     * mLastMove: tracks the absolute time when the snake last moved, and is used
  76.     * to determine if a move should be made based on mMoveDelay.
  77.     */
  78.     private long mLastMove;
  79.    
  80.     /**
  81.     * mStatusText: text shows to the user in some run states
  82.     */
  83.     private TextView mStatusText;

  84.     /**
  85.     * 用于存储贪吃蛇中,苹果和蛇的点阵的坐标的信息的集合
  86.     * mSnakeTrail: a list of Coordinates that make up the snake's body
  87.     * mAppleList: the secret location of the juicy apples the snake craves.
  88.     */
  89.     private ArrayList<Coordinate> mSnakeTrail = new ArrayList<Coordinate>();
  90.     private ArrayList<Coordinate> mAppleList = new ArrayList<Coordinate>();

  91.     /**
  92.     * 为创建苹果坐标使用随机对象
  93.     * Everyone needs a little randomness in their life
  94.     */
  95.     private static final Random RNG = new Random();

  96.     /**
  97.     * 刷新界面处理器
  98.     * Create a simple handler that we can use to cause animation to happen.  We
  99.     * set ourselves as a target and we can use the sleep()
  100.     * function to cause an update/invalidate to occur at a later date.
  101.     */
  102.     private RefreshHandler mRedrawHandler = new RefreshHandler();
  103.     /**
  104.     * 实现刷新的功能:
  105.     *在J2ME中,刷新都是在canvas中通过调用线程结合repaint()来刷新, 他们使线程不断去循环,
  106.     *去调用canvas, 笔者在android 入门时也曾经想用J2ME的模式用在android 中,结果报异常了,
  107.     *为什么呢? 很多人认为Dalvik虚拟机是一个Java虚拟机,因为Android的编程语言恰恰就是Java语言。
  108.     *但是这种说法并不准确,因为Dalvik虚拟机并不是按照Java虚拟机的规范来实现的,两者并不兼容;
  109.     *同时还要两个明显的不同: Java虚拟机运行的是Java字节码,而Dalvik虚拟机运行的则是其专有的
  110.     *文件格式DEX(Dalvik Executable)。所以在以前JAVA 里面能使用的模式, 可能在android
  111.     *里面用不起来 。在自带的例子里面他是通过消息的机制来刷新的。而在android的消定义比较广泛,
  112.     * 比如,手机的暂停, 启动, 来电话、短信,键盘按下,弹起都是一个消息。总的来说, 事件就是消息;
  113.     * 只要继承Handler类就可以对消息进行控制,或者处理, 根据具体情况进行具体处理:
  114.     *
  115.     * @author Administrator
  116.     * @date 2010-5-24
  117.     * @version 1.0
  118.     * @since JDK6.0
  119.     */
  120.     class RefreshHandler extends Handler {

  121.         /**
  122.         * 响应消息
  123.         */
  124.         @Override
  125.         public void handleMessage(Message msg) {
  126.             SnakeView.this.update();
  127.             SnakeView.this.invalidate();
  128.         }
  129.         /**
  130.         * 向外提供人工的调用消息的接口
  131.         * @param delayMillis
  132.         */
  133.         public void sleep(long delayMillis) {
  134.             //注销消息
  135.             this.removeMessages(0);
  136.             //添加消息
  137.             sendMessageDelayed(obtainMessage(0), delayMillis);
  138.         }
  139.     };


  140.     /**
  141.     * Constructs a SnakeView based on inflation from XML
  142.     *
  143.     * @param context
  144.     * @param attrs
  145.     */
  146.     public SnakeView(Context context, AttributeSet attrs) {
  147.         super(context, attrs);
  148.         initSnakeView();
  149.   }

  150.     public SnakeView(Context context, AttributeSet attrs, int defStyle) {
  151.         super(context, attrs, defStyle);
  152.         initSnakeView();
  153.     }
  154.     /**
  155.     * 初始化界面的
  156.     */
  157.     private void initSnakeView() {
  158.         setFocusable(true);

  159.         Resources r = this.getContext().getResources();
  160.        
  161.         resetTiles(4);
  162.         loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));
  163.         loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar));
  164.         loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar));
  165.        
  166.     }
  167.    
  168.     /**
  169.     * 初始化新的游戏
  170.     */
  171.     private void initNewGame() {
  172.         mSnakeTrail.clear();
  173.         mAppleList.clear();

  174.         // For now we're just going to load up a short default eastbound snake
  175.         // that's just turned north
  176.         //设置初始化蛇的位置
  177.        
  178.         mSnakeTrail.add(new Coordinate(7, 7));
  179.         mSnakeTrail.add(new Coordinate(6, 7));
  180.         mSnakeTrail.add(new Coordinate(5, 7));
  181.         mSnakeTrail.add(new Coordinate(4, 7));
  182.         mSnakeTrail.add(new Coordinate(3, 7));
  183.         mSnakeTrail.add(new Coordinate(2, 7));
  184.         mNextDirection = NORTH;

  185.         // Two apples to start with
  186.         //设置苹果的位置
  187.         addRandomApple();
  188.         addRandomApple();
  189.         //
  190.         mMoveDelay = 600;
  191.         //设置积分的
  192.         mScore = 0;
  193.     }


  194.     /**
  195.     * Given a ArrayList of coordinates, we need to flatten them into an array of
  196.     * ints before we can stuff them into a map for flattening and storage.
  197.     *
  198.     * @param cvec : a ArrayList of Coordinate objects
  199.     * @return : a simple array containing the x/y values of the coordinates
  200.     * as [x1,y1,x2,y2,x3,y3...]
  201.     */
  202.     private int[] coordArrayListToArray(ArrayList<Coordinate> cvec) {
  203.         int count = cvec.size();
  204.         int[] rawArray = new int[count * 2];
  205.         for (int index = 0; index < count; index++) {
  206.             Coordinate c = cvec.get(index);
  207.             rawArray[2 * index] = c.x;
  208.             rawArray[2 * index + 1] = c.y;
  209.         }
  210.         return rawArray;
  211.     }

  212.     /**
  213.     * Save game state so that the user does not lose anything
  214.     * if the game process is killed while we are in the
  215.     * background.
  216.     *
  217.     * @return a Bundle with this view's state
  218.     */
  219.     public Bundle saveState() {
  220.         Bundle map = new Bundle();

  221.         map.putIntArray("mAppleList", coordArrayListToArray(mAppleList));
  222.         map.putInt("mDirection", Integer.valueOf(mDirection));
  223.         map.putInt("mNextDirection", Integer.valueOf(mNextDirection));
  224.         map.putLong("mMoveDelay", Long.valueOf(mMoveDelay));
  225.         map.putLong("mScore", Long.valueOf(mScore));
  226.         map.putIntArray("mSnakeTrail", coordArrayListToArray(mSnakeTrail));

  227.         return map;
  228.     }

  229.     /**
  230.     * Given a flattened array of ordinate pairs, we reconstitute them into a
  231.     * ArrayList of Coordinate objects
  232.     *
  233.     * @param rawArray : [x1,y1,x2,y2,...]
  234.     * @return a ArrayList of Coordinates
  235.     */
  236.     private ArrayList<Coordinate> coordArrayToArrayList(int[] rawArray) {
  237.         ArrayList<Coordinate> coordArrayList = new ArrayList<Coordinate>();

  238.         int coordCount = rawArray.length;
  239.         for (int index = 0; index < coordCount; index += 2) {
  240.             Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1]);
  241.             coordArrayList.add(c);
  242.         }
  243.         return coordArrayList;
  244.     }

  245.     /**
  246.     * Restore game state if our process is being relaunched
  247.     *
  248.     * @param icicle a Bundle containing the game state
  249.     */
  250.     public void restoreState(Bundle icicle) {
  251.         setMode(PAUSE);
  252.         //从资源中获取ArrayList
  253.         mAppleList = coordArrayToArrayList(icicle.getIntArray("mAppleList"));
  254.         mDirection = icicle.getInt("mDirection");
  255.         mNextDirection = icicle.getInt("mNextDirection");
  256.         mMoveDelay = icicle.getLong("mMoveDelay");
  257.         mScore = icicle.getLong("mScore");
  258.         mSnakeTrail = coordArrayToArrayList(icicle.getIntArray("mSnakeTrail"));
  259.     }

  260.     /**
  261.     * 重点的控制代码
  262.     *
  263.     * 实现键盘事件: 键盘主要起操作作用, 在任何的手机游戏中键盘都是起重要的用,要本游戏中,
  264.     *  他就是起控制蛇的行走方向: 现在我们分析他的代码:
  265.     *  就是通过判断那个键按下, 然后再给要走的方向(mDirection)赋值。
  266.     * 
  267.     * handles key events in the game. Update the direction our snake is traveling
  268.     * based on the DPAD. Ignore events that would cause the snake to immediately
  269.     * turn back on itself.
  270.     *
  271.     * (non-Javadoc)
  272.     *
  273.     * @see android.view.View#onKeyDown(int, android.os.KeyEvent)
  274.     */
  275.     @Override
  276.     public boolean onKeyDown(int keyCode, KeyEvent msg) {

  277.         if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
  278.             if (mMode == READY | mMode == LOSE) {
  279.                 /*
  280.                 * At the beginning of the game, or the end of a previous one,
  281.                 * we should start a new game.
  282.                 */
  283.                 initNewGame();
  284.                 setMode(RUNNING);
  285.                 update();
  286.                 return (true);
  287.             }

  288.             if (mMode == PAUSE) {
  289.                 /*
  290.                 * If the game is merely paused, we should just continue where
  291.                 * we left off.
  292.                 */
  293.                 setMode(RUNNING);
  294.                 update();
  295.                 return (true);
  296.             }

  297.             if (mDirection != SOUTH) {
  298.                 mNextDirection = NORTH;
  299.             }
  300.             return (true);
  301.         }

  302.         if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
  303.             if (mDirection != NORTH) {
  304.                 mNextDirection = SOUTH;
  305.             }
  306.             return (true);
  307.         }

  308.         if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
  309.             if (mDirection != EAST) {
  310.                 mNextDirection = WEST;
  311.             }
  312.             return (true);
  313.         }

  314.         if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
  315.             if (mDirection != WEST) {
  316.                 mNextDirection = EAST;
  317.             }
  318.             return (true);
  319.         }

  320.         return super.onKeyDown(keyCode, msg);
  321.     }

  322.     /**
  323.     * Sets the TextView that will be used to give information (such as "Game
  324.     * Over" to the user.
  325.     *
  326.     * @param newView
  327.     */
  328.     public void setTextView(TextView newView) {
  329.         mStatusText = newView;
  330.     }

  331.     /**
  332.     * Updates the current mode of the application (RUNNING or PAUSED or the like)
  333.     * as well as sets the visibility of textview for notification
  334.     *
  335.     * @param newMode
  336.     */
  337.     public void setMode(int newMode) {
  338.         int oldMode = mMode;
  339.         mMode = newMode;

  340.         if (newMode == RUNNING & oldMode != RUNNING) {
  341.             mStatusText.setVisibility(View.INVISIBLE);
  342.             update();
  343.             return;
  344.         }

  345.         Resources res = getContext().getResources();
  346.         CharSequence str = "";
  347.         if (newMode == PAUSE) {
  348.             str = res.getText(R.string.mode_pause);
  349.         }
  350.         if (newMode == READY) {
  351.             str = res.getText(R.string.mode_ready);
  352.         }
  353.         if (newMode == LOSE) {
  354.             str = res.getString(R.string.mode_lose_prefix) + mScore
  355.                   + res.getString(R.string.mode_lose_suffix);
  356.         }

  357.         mStatusText.setText(str);
  358.         mStatusText.setVisibility(View.VISIBLE);
  359.     }

  360.     /**
  361.     *
  362.     * 生成苹果位置的代码:
  363.     * 苹果的位置就是更简单了,他是随机生成的, 而且必须在现在蛇的位置相对远距离。
  364.     *
  365.     * Selects a random location within the garden that is not currently covered
  366.     * by the snake. Currently _could_ go into an infinite loop if the snake
  367.     * currently fills the garden, but we'll leave discovery of this prize to a
  368.     * truly excellent snake-player.
  369.     *
  370.     */
  371.     private void addRandomApple() {
  372.         Coordinate newCoord = null;
  373.         boolean found = false;
  374.         while (!found) {
  375.             //随机生成新的X,Y位置
  376.             // Choose a new location for our apple
  377.             int newX = 1 + RNG.nextInt(mXTileCount - 2);
  378.             int newY = 1 + RNG.nextInt(mYTileCount - 2);
  379.             newCoord = new Coordinate(newX, newY);

  380.             // Make sure it's not already under the snake
  381.             boolean collision = false;
  382.             int snakelength = mSnakeTrail.size();
  383.             for (int index = 0; index < snakelength; index++) {
  384.                 //检查一下存放的位置是否合理
  385.                 if (mSnakeTrail.get(index).equals(newCoord)) {
  386.                     collision = true;
  387.                 }
  388.             }
  389.             // if we're here and there's been no collision, then we have
  390.             // a good location for an apple. Otherwise, we'll circle back
  391.             // and try again
  392.             found = !collision;
  393.         }
  394.         if (newCoord == null) {
  395.             Log.e(TAG, "Somehow ended up with a null newCoord!");
  396.         }
  397.         //添加苹果的列表中的
  398.         mAppleList.add(newCoord);
  399.     }


  400.     /**
  401.     * Handles the basic update loop, checking to see if we are in the running
  402.     * state, determining if a move should be made, updating the snake's location.
  403.     */
  404.     public void update() {
  405.         if (mMode == RUNNING) {
  406.             long now = System.currentTimeMillis();

  407.             if (now - mLastMove > mMoveDelay) {
  408.                 clearTiles();
  409.                 updateWalls();
  410.                 updateSnake();
  411.                 updateApples();
  412.                 mLastMove = now;
  413.             }
  414.             mRedrawHandler.sleep(mMoveDelay);
  415.         }

  416.     }

  417.     /**
  418.     * 调用以上的方法以循环的方式位置数组赋值以及图片的索引。
  419.     *
  420.     * Draws some walls.
  421.     *
  422.     */
  423.     private void updateWalls() {
  424.         for (int x = 0; x < mXTileCount; x++) {
  425.             setTile(GREEN_STAR, x, 0);  //设置顶部的界限的位置
  426.             setTile(GREEN_STAR, x, mYTileCount - 1);  //设置底部界限的位置
  427.         }
  428.         for (int y = 1; y < mYTileCount - 1; y++) {
  429.             setTile(GREEN_STAR, 0, y);            //设置顶部的界限的位置
  430.             setTile(GREEN_STAR, mXTileCount - 1, y);  //设置底部界限的位置
  431.         }
  432.     }

  433.     /**
  434.     * Draws some apples.
  435.     *
  436.     */
  437.     private void updateApples() {
  438.         for (Coordinate c : mAppleList) {
  439.             setTile(YELLOW_STAR, c.x, c.y);
  440.         }
  441.     }

  442.     /**
  443.     * 设置当前蛇的方向位置:
  444.     * 从以上的键盘代码我们可以看得出,程序中是通过触发来改变坐标(+1,-1)的方式来改蛇头的方向,
  445.     *  可见坐标在游戏编程中的作用, 这个也是根据手机的屏幕是点阵的方式来显示, 所以坐标就是一个
  446.     *  定位器。 在这里大家可能还有一个疑问。 就是就这个蛇什么能够以“7”字形来移动行走, 其实我们
  447.     *  稍微仔细观察一下就知道了,在这里面, 他们也是通过坐标的传递来实现的, 只要把头部的坐标点
  448.     *  依次赋给下一个点, 后面的每一个点都走过了头部所走过的点,而蛇的头部就是负责去获取坐标,整
  449.     *  个蛇的行走起来就很自然和连贯。  坐标的方向变换又是通过判断那个方向按键的按下来改变的, 这
  450.     *  样一来, 键盘的作用就发挥出来了。蛇吃苹果又是怎样去实现?上面我所说到的坐标就起了作用。在蛇
  451.     *  所经过的每一个坐标, 他们都要在苹果所在的(ArrayList<Coordinate> mAppleList = new
  452.     *  ArrayList<Coordinate>())坐标集里面集依次判断,若是坐标相同,那个这个苹果就被蛇吃了 。
  453.     * 
  454.     * Figure out which way the snake is going, see if he's run into anything (the
  455.     * walls, himself, or an apple). If he's not going to die, we then add to the
  456.     * front and subtract from the rear in order to simulate motion. If we want to
  457.     * grow him, we don't subtract from the rear.
  458.     *
  459.     */
  460.     private void updateSnake() {
  461.         boolean growSnake = false;

  462.         // grab the snake by the head
  463.         //获取蛇的头部
  464.         Coordinate head = mSnakeTrail.get(0);
  465.         //创建一个新的蛇的头部应该的位置
  466.         Coordinate newHead = new Coordinate(1, 1);
  467.         //根据当前的为方向设置坐标的信息
  468.         mDirection = mNextDirection;

  469.         switch (mDirection) {
  470.         case EAST: {
  471.             newHead = new Coordinate(head.x + 1, head.y);
  472.             break;
  473.         }
  474.         case WEST: {
  475.             newHead = new Coordinate(head.x - 1, head.y);
  476.             break;
  477.         }
  478.         case NORTH: {
  479.             newHead = new Coordinate(head.x, head.y - 1);
  480.             break;
  481.         }
  482.         case SOUTH: {
  483.             newHead = new Coordinate(head.x, head.y + 1);
  484.             break;
  485.         }
  486.         }

  487.         // Collision detection
  488.         // For now we have a 1-square wall around the entire arena
  489.         if ((newHead.x < 1) || (newHead.y < 1) || (newHead.x > mXTileCount - 2)
  490.                 || (newHead.y > mYTileCount - 2)) {
  491.             setMode(LOSE);
  492.             return;

  493.         }

  494.         // Look for collisions with itself
  495.         int snakelength = mSnakeTrail.size();
  496.         for (int snakeindex = 0; snakeindex < snakelength; snakeindex++) {
  497.             Coordinate c = mSnakeTrail.get(snakeindex);
  498.             if (c.equals(newHead)) {
  499.                 setMode(LOSE);
  500.                 return;
  501.             }
  502.         }

  503.         // Look for apples
  504.         //查找苹果设置苹果新的位置的信息
  505.         int applecount = mAppleList.size();
  506.         for (int appleindex = 0; appleindex < applecount; appleindex++) {
  507.             Coordinate c = mAppleList.get(appleindex);
  508.             if (c.equals(newHead)) {
  509.                 mAppleList.remove(c);
  510.                 addRandomApple();
  511.                
  512.                 mScore++;
  513.                 //设置的移动的速度
  514.                 mMoveDelay *= 0.9;

  515.                 growSnake = true;
  516.             }
  517.         }
  518.         //将蛇头的位置信息放在第一个的对象中方取值
  519.         // push a new head onto the ArrayList and pull off the tail
  520.         mSnakeTrail.add(0, newHead);
  521.         // except if we want the snake to grow
  522.         if (!growSnake) {
  523.             mSnakeTrail.remove(mSnakeTrail.size() - 1);
  524.         }

  525.         int index = 0;
  526.         for (Coordinate c : mSnakeTrail) {
  527.             if (index == 0) {
  528.                 setTile(YELLOW_STAR, c.x, c.y);
  529.             } else {
  530.                 setTile(RED_STAR, c.x, c.y);
  531.             }
  532.             index++;
  533.         }

  534.     }

  535.     /**
  536.     * 用于存储每一个位点的x,y坐标信息
  537.     * Simple class containing two integer values and a comparison function.
  538.     * There's probably something I should use instead, but this was quick and
  539.     * easy to build.
  540.     *
  541.     */
  542.     private class Coordinate {
  543.         public int x;
  544.         public int y;

  545.         public Coordinate(int newX, int newY) {
  546.             x = newX;
  547.             y = newY;
  548.         }

  549.         public boolean equals(Coordinate other) {
  550.             if (x == other.x && y == other.y) {
  551.                 return true;
  552.             }
  553.             return false;
  554.         }

  555.         @Override
  556.         public String toString() {
  557.             return "Coordinate: [" + x + "," + y + "]";
  558.         }
  559.     }
  560.    
  561. }
复制代码
TOP

  1. package com.easyway.dev.android.snake;

  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.graphics.Bitmap;
  5. import android.graphics.Canvas;
  6. import android.graphics.Paint;
  7. import android.graphics.drawable.Drawable;
  8. import android.util.AttributeSet;
  9. import android.view.View;


  10. /**
  11. * Android 平台裡,使用者介面都是透过 ViewGroup 或 View 类别来显示。
  12. * ViewGroup 和 View 是 Android 平台上最基本的使用者介面表达单元。我
  13. * 们可以透过程式直接呼叫的方法,调用描绘使用者介面,将萤幕上显示的介面元
  14. * 素,与构成应用程式主体的程式逻辑,溷合在一起编写。或是,也可以将介面显示
  15. * 与程式逻辑分离,照着 Android 提供的这个较优雅的方式,使用 XML 描述档,
  16. * 来描述介面元件的组织。
  17. *
  18. * 在 Android 系统中,我们使用 XML 来定义 UI。但是有些稍微有经验的开发者可能会有疑问:
  19. *「用 XML 来描述介面固然方便,但是对于手机程式来说,直接用 XML 档桉是不是太占空间了?」。
  20. *没错,如果 Android 是直接使用 XML 来储存介面描述到手机上的话,一定会佔用比起现在大的多
  21. *的档桉空间。解决的方法是Android 并不直接使用 XML 档桉,而是透过 Android 开发工具,
  22. *自动将 XML 描述档转换成资源档桉。一旦应用程式要操作某个介面元件,或是使用任何种类的资源
  23. *(字串、图片、图示、音效...),都使用索引来查询。
  24. *
  25. *
  26. *伟大的创意少之又少,多数时候只是一些小改进。小的改进也是好的。
  27. *
  28. *
  29. * TileView: a View-variant designed for handling arrays of "icons" or other
  30. * drawables.
  31. *
  32. */
  33. public class TileView extends View {

  34.     /**
  35.     * Parameters controlling the size of the tiles and their range within view.
  36.     * Width/Height are in pixels, and Drawables will be scaled to fit to these
  37.     * dimensions. X/Y Tile Counts are the number of tiles that will be drawn.
  38.     */

  39.     protected static int mTileSize;

  40.     protected static int mXTileCount;
  41.     protected static int mYTileCount;

  42.     private static int mXOffset;
  43.     private static int mYOffset;


  44.     /**
  45.     *
  46.     * A hash that maps integer handles specified by the subclasser to the
  47.     * drawable that will be used for that reference
  48.     */
  49.     private Bitmap[] mTileArray;

  50.     /**
  51.     * 声明用来存放绘画图像的x,y轴的位置的数组
  52.     * A two-dimensional array of integers in which the number represents the
  53.     * index of the tile that should be drawn at that locations
  54.     */
  55.     private int[][] mTileGrid;

  56.     private final Paint mPaint = new Paint();

  57.     public TileView(Context context, AttributeSet attrs, int defStyle) {
  58.         super(context, attrs, defStyle);

  59.         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);

  60.         mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
  61.        
  62.         a.recycle();
  63.     }

  64.     public TileView(Context context, AttributeSet attrs) {
  65.         super(context, attrs);

  66.         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);

  67.         mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
  68.        
  69.         a.recycle();
  70.     }

  71.    
  72.    
  73.     /**
  74.     * Rests the internal array of Bitmaps used for drawing tiles, and
  75.     * sets the maximum index of tiles to be inserted
  76.     *
  77.     * @param tilecount
  78.     */
  79.    
  80.     public void resetTiles(int tilecount) {
  81.             mTileArray = new Bitmap[tilecount];
  82.     }


  83.     @Override
  84.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  85.         mXTileCount = (int) Math.floor(w / mTileSize);
  86.         mYTileCount = (int) Math.floor(h / mTileSize);

  87.         mXOffset = ((w - (mTileSize * mXTileCount)) / 2);
  88.         mYOffset = ((h - (mTileSize * mYTileCount)) / 2);

  89.         mTileGrid = new int[mXTileCount][mYTileCount];
  90.         clearTiles();
  91.     }

  92.     /**
  93.     * Function to set the specified Drawable as the tile for a particular
  94.     * integer key.
  95.     *
  96.     * @param key
  97.     * @param tile
  98.     */
  99.     public void loadTile(int key, Drawable tile) {
  100.         Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);
  101.         Canvas canvas = new Canvas(bitmap);
  102.         tile.setBounds(0, 0, mTileSize, mTileSize);
  103.         tile.draw(canvas);
  104.        
  105.         mTileArray[key] = bitmap;
  106.     }

  107.     /**
  108.     * Resets all tiles to 0 (empty)
  109.     *
  110.     */
  111.     public void clearTiles() {
  112.         for (int x = 0; x < mXTileCount; x++) {
  113.             for (int y = 0; y < mYTileCount; y++) {
  114.                 setTile(0, x, y);
  115.             }
  116.         }
  117.     }

  118.     /**
  119.     * Used to indicate that a particular tile (set with loadTile and referenced
  120.     * by an integer) should be drawn at the given x/y coordinates during the
  121.     * next invalidate/draw cycle.
  122.     *
  123.     * @param tileindex 图片的索引
  124.     * @param x  x轴的位置
  125.     * @param y  y轴的位置
  126.     */
  127.     public void setTile(int tileindex, int x, int y) {
  128.         mTileGrid[x][y] = tileindex;
  129.     }

  130.     /**
  131.     * 重写VIEW 类里面的方法。 把界线画出。
  132.     *
  133.     * 地图其实就是由图片数组拼直面成的。 面图片又是通过他的图片索引找到,并
  134.     * 在mTileGrid[x][y],获取他们的位置索引来确定图片的位置。 这样在一个
  135.     * 手机的页面就形成了,
  136.     *
  137.     */
  138.     @Override
  139.     public void onDraw(Canvas canvas) {
  140.         super.onDraw(canvas);
  141.         for (int x = 0; x < mXTileCount; x += 1) {
  142.             for (int y = 0; y < mYTileCount; y += 1) {
  143.                 if (mTileGrid[x][y] > 0) {
  144.                     canvas.drawBitmap(mTileArray[mTileGrid[x][y]],
  145.                                     mXOffset + x * mTileSize,
  146.                                     mYOffset + y * mTileSize,
  147.                                     mPaint);
  148.                 }
  149.             }
  150.         }

  151.     }

  152. }
复制代码
TOP

贪吃蛇游戏的项目控制文件。AndroidManifest.xml内容如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!-- Copyright (C) 2007 The Android Open Source Project

  3.     Licensed under the Apache License, Version 2.0 (the "License");
  4.     you may not use this file except in compliance with the License.
  5.     You may obtain a copy of the License at

  6.           [url]http://www.apache.org/licenses/LICENSE-2.0[/url]

  7.     Unless required by applicable law or agreed to in writing, software
  8.     distributed under the License is distributed on an "AS IS" BASIS,
  9.     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10.     See the License for the specific language governing permissions and
  11.     limitations under the License.

  12. Declare the contents of this Android application.  The namespace
  13.     attribute brings in the Android platform namespace, and the package
  14.     supplies a unique name for the application.  When writing your
  15.     own application, the package name must be changed from "com.example.*"
  16.     to come from a domain that you own or have control over.
  17.     直观来看,每个 Activity 通常会负责处理一个萤幕的内容(包含介面、选单、弹出对话框、程式动作等)。
  18.     当我们需要从一个萤幕画面切换到另一个萤幕画面的时候,就涉及到了 Activity 切换的动作。 我们可以将
  19.       Activity 看成 MVC 模式中的 Control。Activity 负责管理 UI(详细的UI细节可以由资源档读入)
  20.       ,并接受事件触发。以是否需要与其他 Activity 交换资料来区分,Activity 可以粗分为两种类型:
  21.       「独立的 Activity」与「相依的 Activity」。
  22. 不同类型的 Activity,其动作也不尽相同:
  23. 独立的 Activity
  24.   独立的 Activity 是不需要从其他地方取得资料的 Activity。只是单纯的从一个萤幕跳到下个萤幕,不涉及
  25. 资料的交换。 从一个独立的 Activity 呼叫另一个独立的 Activity 时,我们只要填好 Intent 的内容和动作,
  26. 使用 startActivity 函式呼叫,即可唤起独立的 Activity。例如前几章中,用作开启特定网页的 Activity。

  27. 相依的 Activity
  28.     相依的 Activity 是需要与其他 Activity 交换资料的一种 Activity。相依的 Activity 又可再分为单向与双向。
  29. 从一个萤幕跳到下个萤幕时,携带资料供下一个萤幕(Activity)使用,就是单向相依的 Activity; 要在两个萤幕之
  30. 间切换,萤幕上的资料会因另一个萤幕的操作而改变的,就是双向相依的 Activity。 与独立的 Activity 比起来,相
  31. 依的 Activity 变化更加精采。

  32. 我们会在后续章节中,对相依的 Activity 做进一步的说明。

  33. 独立的 Activity
  34.     本章将继续透过改进 BMI 应用程式来讲解 Android 应用程式设计。在这个过程中,我们将使用到独立的
  35.     Activity。

  36.   这章中所做的改动都是为了介绍独立的 Activity,而不是为了让  程式变得更完整。因此你不妨先将写好的
  37.   BMI 程式先压缩备份到其他目录中,再随着后面的教学继续探索 Android。

  38. 本章的目的是介绍独立的 Activity,会用到两个萤幕,因此除了原本的一个 XML 描述档与一个程式码档桉之外,
  39. 我们还会额外再定义一个 XML 描述档与一个程式码档桉,以支援第二个萤幕的动作。
  40. 要完成独立的 Activity 的动作,我们要做几件事:
  41. 在程式码中建立新 Activity 类别档桉
  42. 在清单中新增 Activity 描述
  43. 在原 Activity 类别中加入 startActivity 函式
  44.     -->
  45. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  46.     package="com.easyway.dev.android.snake">
  47.     <application android:label="Snake on a Phone">
  48.       <!--
  49.       可以定义多个activty对象,但是可以设置是否为主界面
  50.       -->
  51.       <activity android:name="Snake"
  52.         android:screenOrientation="portrait"
  53.         android:configChanges="keyboardHidden|orientation">
  54.             <intent-filter>
  55.                 <action android:name="android.intent.action.MAIN" />
  56.                 <category android:name="android.intent.category.LAUNCHER" />
  57.             </intent-filter>
  58.         </activity>
  59.     </application>
  60. </manifest>
复制代码
下载Android的贪食蛇游戏源码:
附件: 亲,您没有权限下载或查看附件喔:-) 试试登录注册吧!
TOP

深情顶帖,楼主加油!
TOP

深情顶帖,楼主加油!
TOP

深情顶帖,楼主加油!
TOP

深情顶帖,楼主加油!
TOP

深情顶帖,楼主加油!
TOP