一直想自己动手做一个手机游戏,安装在自己的手机上,尽管应用或许会看起来很简单效果也不是很拉风,可是自己做的,那心情那感觉终究是不一样。今天,让我们一起探秘贪吃蛇游戏,用自己的双手玩转java代码打造属于自己的游戏。

贪吃蛇是一款足够经典的游戏。它的经典,在于用户操作的简单,在于技术实现的简介,在于他的经久不衰,在于它的独领风骚。

这里的贪吃蛇的android实现,是SDK Samples中的开源例程。可能各位都有看过-界面如下图啦。。。

作为一个刚入门或者还没入门的新手,着实花了我一些力气来理解这段代码。

对于各种不懂的地方,慢慢查询资料,对于新的方法,通过修改代码尝试效果。到现在终于能算个一知半解。

在代码中,对于自己有所收获的地方,我都做了相应的注释。

回过头来,觉得从这段代码中,能学到不少东西~~

包括android应用的基本架构,他的面向对象的思想,以及代码的简洁明了。

于是,我想到,何不将这些东西分享出来,如果碰巧对感兴趣的朋友们有搜帮助,那就更好了~

好了,闲话不说~代码和注释如下(处于对源码的敬意,原本的英文注释部分都没有删去~大家可以配合理解):

************************************************************************************************************************************

Snake工程中,总共有三个文件: *TileView是基于Android的View类实现的方块图类,用来支撑上层类的调用,绘制方块图的显 示界面。通过这些代码,能打之了解如何 扩展View,实现特色的界面效果。 *SnakeView调用了TileView,实现了游戏逻辑 和 具体的显示。 *Snake为主Activity类。

建议大家按照上面的顺序看三个文件,可能逻辑上更舒服一点~~

下面贴上代码和注释。

PS:  调试版本为android2.2。 其他版本应该也没问题,不过得用虚拟机。

TileView.java

[java] view plaincopy
[java] view plaincopy
  1. package com.example.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. * TileView: a View-variant designed for handling arrays of "icons" or other
  12. * drawables.
  13. *
  14. */
  15. public class TileView extends View {
  16. /**
  17. * Parameters controlling the size of the tiles and their range within view.
  18. * Width/Height are in pixels, and Drawables will be scaled to fit to these
  19. * dimensions. X/Y Tile Counts are the number of tiles that will be drawn.
  20. */
  21. protected static int mTileSize; //每个tile的边长的像素数量
  22. protected static int mXTileCount; //屏幕内能容纳的 X方向上方块的总数量
  23. protected static int mYTileCount;//屏幕内能容纳的 Y方向上方块的总数量
  24. private static int mXOffset; //原点坐标,按pixel计。
  25. private static int mYOffset;
  26. /**
  27. * A hash that maps integer handles specified by the subclasser to the
  28. * drawable that will be used for that reference
  29. * 存储着不同种类的bitmap图。通过resetTiles,loadTile,将游戏中的方块加载到这个数组。
  30. * 可以理解为 砖块字典
  31. */
  32. private Bitmap[] mTileArray;
  33. /**
  34. * A two-dimensional array of integers in which the number represents the
  35. * index of the tile that should be drawn at that locations
  36. * 存储整个界面内每个tile位置应该绘制的tile。
  37. * 可看作是我们直接操作的画布。
  38. * 通过setTile、clearTile 进行图形显示的修改操作。
  39. *
  40. */
  41. private int[][] mTileGrid;
  42. //画笔,canvas的图形绘制,需要画笔Paint实现。
  43. private final Paint mPaint = new Paint();
  44. public TileView(Context context, AttributeSet attrs, int defStyle) {
  45. super(context, attrs, defStyle);
  46. //使用TypedArray,获取在attrs.xml中为TileView定义的新属性tileSize 。参考: http://weizhulin.blog.51cto.com/1556324/311453
  47. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
  48. mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
  49. a.recycle();
  50. }
  51. public TileView(Context context, AttributeSet attrs) {
  52. super(context, attrs);
  53. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
  54. mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
  55. a.recycle();
  56. }
  57. /**
  58. * Rests the internal array of Bitmaps used for drawing tiles, and
  59. * sets the maximum index of tiles to be inserted
  60. * 重置清零mTileArray,在游戏初始的时候使用。
  61. * 即清空砖块字典
  62. * @param tilecount
  63. */
  64. public void resetTiles(int tilecount) {
  65. mTileArray = new Bitmap[tilecount];
  66. }
  67. /*
  68. * 当改变屏幕大小尺寸时,同时修改tile的相关计数指标。
  69. */
  70. @Override
  71. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  72. mXTileCount = (int) Math.floor(w / mTileSize);
  73. mYTileCount = (int) Math.floor(h / mTileSize);
  74. //mXOffset mYOffset是绘图的起点坐标。
  75. mXOffset = ((w - (mTileSize * mXTileCount)) / 2);
  76. mYOffset = ((h - (mTileSize * mYTileCount)) / 2);
  77. mTileGrid = new int[mXTileCount][mYTileCount];
  78. clearTiles();
  79. }
  80. /**
  81. * Function to set the specified Drawable as the tile for a particular
  82. * integer key.
  83. * 加载具体的砖块图片 到 砖块字典。
  84. * 即将对应的砖块的图片 对应的加载到 mTileArray数组中
  85. * @param key
  86. * @param tile
  87. */
  88. public void loadTile(int key, Drawable tile) {
  89. //这里做了一个 Drawable 到 bitmap 的转换。由于外部程序使用的时候是直接读取资源文件中的图片,
  90. //是drawable格式,而我们的数组是bitmap格式,方便最终的绘制。所以,需要进行一次到 bitmap的转换。
  91. Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);
  92. Canvas canvas = new Canvas(bitmap);
  93. tile.setBounds(0, 0, mTileSize, mTileSize);
  94. tile.draw(canvas);
  95. mTileArray[key] = bitmap;
  96. }
  97. /**
  98. * Used to indicate that a particular tile (set with loadTile and referenced
  99. * by an integer) should be drawn at the given x/y coordinates during the
  100. * next invalidate/draw cycle.
  101. * 在相应的坐标位置绘制相应的砖块
  102. * 记得哦,mTileGrid其实就是我们直接操作的画布。
  103. * @param tileindex
  104. * @param x
  105. * @param y
  106. */
  107. public void setTile(int tileindex, int x, int y) {
  108. mTileGrid[x][y] = tileindex;
  109. }
  110. /**
  111. * Resets all tiles to 0 (empty)
  112. * 清空图形显示。
  113. * 用以更新画面。
  114. * 调用了绘图的setTile()。
  115. */
  116. public void clearTiles() {
  117. for (int x = 0; x < mXTileCount; x++) {
  118. for (int y = 0; y < mYTileCount; y++) {
  119. setTile(0, x, y);
  120. }
  121. }
  122. }
  123. /*
  124. * 将我们直接操作的画布绘制到手机界面上!
  125. * @see android.view.View#onDraw(android.graphics.Canvas)
  126. */
  127. @Override
  128. public void onDraw(Canvas canvas) {
  129. super.onDraw(canvas);
  130. for (int x = 0; x < mXTileCount; x += 1) {
  131. for (int y = 0; y < mYTileCount; y += 1) {
  132. if (mTileGrid[x][y] > 0) {
  133. canvas.drawBitmap(mTileArray[mTileGrid[x][y]],
  134. mXOffset + x * mTileSize,
  135. mYOffset + y * mTileSize,
  136. mPaint);
  137. }
  138. }
  139. }
  140. }
  141. }

SnakeView.java

[java] view plaincopy
  1. package com.example.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.Handler;
  7. import android.os.Message;
  8. import android.util.AttributeSet;
  9. import android.os.Bundle;
  10. import android.util.Log;
  11. import android.view.KeyEvent;
  12. import android.view.View;
  13. import android.widget.TextView;
  14. /**
  15. * SnakeView: implementation of a simple game of Snake
  16. */
  17. public class SnakeView extends TileView {
  18. private static final String TAG = "SnakeView";
  19. /**
  20. * Current mode of application: READY to run, RUNNING, or you have already
  21. * lost. static final ints are used instead of an enum for performance
  22. * reasons.
  23. * 游戏的四种状态。初始时为 预备开始的状态。
  24. */
  25. private int mMode = READY;
  26. public static final int PAUSE = 0;  //暂停
  27. public static final int READY = 1;  //准备好了,预备开始
  28. public static final int RUNNING = 2;//正在运行
  29. public static final int LOSE = 3;   //结束,输了游戏
  30. /**
  31. * Current direction the snake is headed.
  32. * 蛇体运动的方向标识。
  33. */
  34. private int mDirection = NORTH;
  35. private int mNextDirection = NORTH;
  36. private static final int NORTH = 1;
  37. private static final int SOUTH = 2;
  38. private static final int EAST = 3;
  39. private static final int WEST = 4;
  40. /**
  41. * Labels for the drawables that will be loaded into the TileView class
  42. * 游戏中仅有的三种砖块对应的数值。
  43. */
  44. private static final int RED_STAR = 1;
  45. private static final int YELLOW_STAR = 2;
  46. private static final int GREEN_STAR = 3;
  47. /**
  48. * mScore: used to track the number of apples captured mMoveDelay: number of
  49. * milliseconds between snake movements. This will decrease as apples are
  50. * captured.
  51. */
  52. private long mScore = 0;   //记录获得的分数。
  53. private long mMoveDelay = 600;  //每移动一步的延时。初始时设置为600ms,以后每吃一个果子,打个9折
  54. //造成的结果是速度越来越快。
  55. /**
  56. * mLastMove: tracks the absolute time when the snake last moved, and is used
  57. * to determine if a move should be made based on mMoveDelay.
  58. * 记录上次移动的确切时间。
  59. * 同mMoveDelay一起处理与用户的异步操作的协同问题。
  60. */
  61. private long mLastMove;
  62. /**
  63. * mStatusText: text shows to the user in some run states
  64. * 用来显示游戏状态的TextView
  65. */
  66. private TextView mStatusText;
  67. /**
  68. * mSnakeTrail: a list of Coordinates that make up the snake's body
  69. * mAppleList: the secret location of the juicy apples the snake craves.
  70. * 两个链表,分别用来存储 蛇体 和 果子的坐标。
  71. * 每次蛇体的运动,蛇体的增长,产生新的苹果,被吃掉苹果,都会在这里记录。
  72. */
  73. private ArrayList<Coordinate> mSnakeTrail = new ArrayList<Coordinate>();
  74. private ArrayList<Coordinate> mAppleList = new ArrayList<Coordinate>();
  75. /**
  76. * Everyone needs a little randomness in their life
  77. * 随机数生成器。用来产生随机的苹果。在addRandomApple()中使用。
  78. */
  79. private static final Random RNG = new Random();
  80. /**
  81. * Create a simple handler that we can use to cause animation to happen.  We
  82. * set ourselves as a target and we can use the sleep()
  83. * function to cause an update/invalidate to occur at a later date.
  84. * 用Handler机制实现定时刷新。
  85. * 为什么使用Handler呢?大家可以参考 android 的线程模型(注意UI线程不是线程安全的~)
  86. * 具体使用方法网上的资源很多,在此不赘述~
  87. */
  88. private RefreshHandler mRedrawHandler = new RefreshHandler();
  89. class RefreshHandler extends Handler {
  90. //获取消息并处理
  91. @Override
  92. public void handleMessage(Message msg) {
  93. SnakeView.this.update();
  94. SnakeView.this.invalidate(); //刷新view为基类的界面
  95. }
  96. //定时发送消息给UI线程,以此达到更新的效果。
  97. public void sleep(long delayMillis) {
  98. this.removeMessages(0); //清空消息队列,Handler进入对新消息的等待
  99. sendMessageDelayed(obtainMessage(0), delayMillis); //定时发送新消息,激活handler
  100. }
  101. };
  102. public SnakeView(Context context, AttributeSet attrs) {
  103. super(context, attrs);
  104. initSnakeView();  //构造函数中,别忘了,初始化游戏~
  105. }
  106. public SnakeView(Context context, AttributeSet attrs, int defStyle) {
  107. super(context, attrs, defStyle);
  108. initSnakeView();
  109. }
  110. //初始化SnakeView类,注意,这根初始化游戏是不一样的。
  111. private void initSnakeView() {
  112. setFocusable(true); //设置焦点,由于存在 文字界面 和 游戏界面的跳转。这个focus是不可或缺的。
  113. //取得资源中的图片,加载到 砖块字典 中。
  114. Resources r = this.getContext().getResources();
  115. resetTiles(4);
  116. loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));
  117. loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar));
  118. loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar));
  119. }
  120. //如果不是从暂停中回复,就绪要 初始化游戏了。
  121. private void initNewGame() {
  122. //清空保存蛇体和果子的数据结构。
  123. mSnakeTrail.clear();
  124. mAppleList.clear();
  125. // For now we're just going to load up a short default eastbound snake
  126. // that's just turned north
  127. // 设定初始状态的蛇体的位置。
  128. mSnakeTrail.add(new Coordinate(7, 7));
  129. mSnakeTrail.add(new Coordinate(6, 7));
  130. mSnakeTrail.add(new Coordinate(5, 7));
  131. mSnakeTrail.add(new Coordinate(4, 7));
  132. mSnakeTrail.add(new Coordinate(3, 7));
  133. mSnakeTrail.add(new Coordinate(2, 7));
  134. mNextDirection = NORTH;
  135. // Two apples to start with
  136. addRandomApple();
  137. addRandomApple();
  138. mMoveDelay = 600;
  139. mScore = 0;
  140. }
  141. /**
  142. * Given a ArrayList of coordinates, we need to flatten them into an array of
  143. * ints before we can stuff them into a map for flattening and storage.
  144. *
  145. * @param cvec : a ArrayList of Coordinate objects
  146. * @return : a simple array containing the x/y values of the coordinates
  147. * as [x1,y1,x2,y2,x3,y3...】
  148. * 在游戏暂停时,需要通过Bundle方式保存数据。见saveState()。
  149. * Bundle支持简单的数组。
  150. * 所以需要将我们的部分数据结构,如蛇体和苹果位置的数组,转换成简单的序列化的int数组。
  151. */
  152. private int[] coordArrayListToArray(ArrayList<Coordinate> cvec) {
  153. int count = cvec.size();
  154. int[] rawArray = new int[count * 2];
  155. for (int index = 0; index < count; index++) {
  156. Coordinate c = cvec.get(index);
  157. rawArray[2 * index] = c.x;
  158. rawArray[2 * index + 1] = c.y;
  159. }
  160. return rawArray;
  161. }
  162. /**
  163. * Save game state so that the user does not lose anything
  164. * if the game process is killed while we are in the
  165. * background.
  166. * 在意外情况下,暂时性保存游戏数据,在下次打开游戏时,可以继续游戏。如来电话了。
  167. * @return a Bundle with this view's state
  168. */
  169. public Bundle saveState() {
  170. Bundle map = new Bundle();
  171. map.putIntArray("mAppleList", coordArrayListToArray(mAppleList));
  172. map.putInt("mDirection", Integer.valueOf(mDirection));
  173. map.putInt("mNextDirection", Integer.valueOf(mNextDirection));
  174. map.putLong("mMoveDelay", Long.valueOf(mMoveDelay));
  175. map.putLong("mScore", Long.valueOf(mScore));
  176. map.putIntArray("mSnakeTrail", coordArrayListToArray(mSnakeTrail));
  177. return map;
  178. }
  179. /**
  180. * Given a flattened array of ordinate pairs, we reconstitute them into a
  181. * ArrayList of Coordinate objects
  182. * 是coordArrayListToArray()的逆过程,用来读取保存在Bundle中的数据。
  183. * @param rawArray : [x1,y1,x2,y2,...]
  184. * @return a ArrayList of Coordinates
  185. */
  186. private ArrayList<Coordinate> coordArrayToArrayList(int[] rawArray) {
  187. ArrayList<Coordinate> coordArrayList = new ArrayList<Coordinate>();
  188. int coordCount = rawArray.length;
  189. for (int index = 0; index < coordCount; index += 2) {
  190. Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1]);
  191. coordArrayList.add(c);
  192. }
  193. return coordArrayList;
  194. }
  195. /**
  196. * Restore game state if our process is being relaunched
  197. * 回复游戏数据。是saveState()的逆过程
  198. * @param icicle a Bundle containing the game state
  199. */
  200. public void restoreState(Bundle icicle) {
  201. setMode(PAUSE);
  202. mAppleList = coordArrayToArrayList(icicle.getIntArray("mAppleList"));
  203. mDirection = icicle.getInt("mDirection");
  204. mNextDirection = icicle.getInt("mNextDirection");
  205. mMoveDelay = icicle.getLong("mMoveDelay");
  206. mScore = icicle.getLong("mScore");
  207. mSnakeTrail = coordArrayToArrayList(icicle.getIntArray("mSnakeTrail"));
  208. }
  209. /*
  210. * handles key events in the game. Update the direction our snake is traveling
  211. * based on the DPAD. Ignore events that would cause the snake to immediately
  212. * turn back on itself.
  213. * 按键的监听。
  214. * 现在大多数的android手机都没有按键了。
  215. * 笔者就是在自己的模拟机上才能正常的使用这款小游戏的 - -#
  216. * @see android.view.View#onKeyDown(int, android.os.KeyEvent)
  217. */
  218. @Override
  219. public boolean onKeyDown(int keyCode, KeyEvent msg) {
  220. //这里是游戏的基本逻辑。如果你还没尝试一下它,先玩玩再说吧。那有助于你对代码的理解~
  221. if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
  222. if (mMode == READY | mMode == LOSE) {
  223. /*
  224. * At the beginning of the game, or the end of a previous one,
  225. * we should start a new game.
  226. */
  227. initNewGame();
  228. setMode(RUNNING);
  229. update(); //update()实现了对游戏数据的更新,是整个游戏的推动力。
  230. return (true);
  231. }
  232. if (mMode == PAUSE) {
  233. /*
  234. * If the game is merely paused, we should just continue where
  235. * we left off.
  236. */
  237. setMode(RUNNING);
  238. update();
  239. return (true);
  240. }
  241. if (mDirection != SOUTH) {  //如果按键的方向 跟蛇本身的运动方向完全相反,则无法执行
  242. mNextDirection = NORTH;
  243. }
  244. return (true);
  245. }
  246. if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
  247. if (mDirection != NORTH) {
  248. mNextDirection = SOUTH;
  249. }
  250. return (true);
  251. }
  252. if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
  253. if (mDirection != EAST) {
  254. mNextDirection = WEST;
  255. }
  256. return (true);
  257. }
  258. if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
  259. if (mDirection != WEST) {
  260. mNextDirection = EAST;
  261. }
  262. return (true);
  263. }
  264. return super.onKeyDown(keyCode, msg);
  265. }
  266. /**
  267. * Sets the TextView that will be used to give information (such as "Game
  268. * Over" to the user.
  269. * 起初不明白这个方法有什么作用。删除了以后才发现错误。Snake类会调用到它,来绑定到相应的textview.
  270. */
  271. public void setTextView(TextView newView) {
  272. mStatusText = newView;
  273. }
  274. /**
  275. * Updates the current mode of the application (RUNNING or PAUSED or the like)
  276. * as well as sets the visibility of textview for notification
  277. *
  278. * @param newMode
  279. */
  280. public void setMode(int newMode) {
  281. int oldMode = mMode;
  282. mMode = newMode;
  283. if (newMode == RUNNING & oldMode != RUNNING) {
  284. mStatusText.setVisibility(View.INVISIBLE); //游戏开始后,将TextView的文字显示设置为不可见。
  285. update(); //注意到,在initGame中也有update(),不过放心~ 多次重复 update不会影响效果的,
  286. //蛇的移动有mLastMove 和 mMoveDelay 来校验。这会在Update()中体现。
  287. //当然,经过实验,注释掉这个update()似乎不会影响结果噢。
  288. return;
  289. }
  290. Resources res = getContext().getResources();
  291. CharSequence str = "";
  292. if (newMode == PAUSE) {
  293. str = res.getText(R.string.mode_pause);
  294. }
  295. if (newMode == READY) {
  296. str = res.getText(R.string.mode_ready);
  297. }
  298. if (newMode == LOSE) {
  299. str = res.getString(R.string.mode_lose_prefix) + mScore
  300. + res.getString(R.string.mode_lose_suffix);
  301. }
  302. mStatusText.setText(str);
  303. mStatusText.setVisibility(View.VISIBLE);
  304. }
  305. /**
  306. * Selects a random location within the garden that is not currently covered
  307. * by the snake. Currently _could_ go into an infinite loop if the snake
  308. * currently fills the garden, but we'll leave discovery of this prize to a
  309. * truly excellent snake-player.
  310. * 在地图上随机的增加果子。注意苹果的位置不可以是蛇体所在噢~这里有个小bug,没有检测
  311. * 产生的果子位置 可能与 另一个果子位置重合。
  312. * 新产生的果子的坐标会增加到mApplist的数组上。
  313. */
  314. private void addRandomApple() {
  315. Coordinate newCoord = null;
  316. boolean found = false;
  317. while (!found) {
  318. // Choose a new location for our apple
  319. //注意别产生在边框上的果子
  320. int newX = 1 + RNG.nextInt(mXTileCount - 2);
  321. int newY = 1 + RNG.nextInt(mYTileCount - 2);
  322. newCoord = new Coordinate(newX, newY);
  323. // Make sure it's not already under the snake
  324. boolean collision = false;
  325. int snakelength = mSnakeTrail.size();
  326. for (int index = 0; index < snakelength; index++) {
  327. if (mSnakeTrail.get(index).equals(newCoord)) {
  328. collision = true;
  329. }
  330. }
  331. // if we're here and there's been no collision, then we have
  332. // a good location for an apple. Otherwise, we'll circle back
  333. // and try again
  334. found = !collision;
  335. }
  336. if (newCoord == null) {
  337. Log.e(TAG, "Somehow ended up with a null newCoord!");
  338. }
  339. mAppleList.add(newCoord);
  340. }
  341. /**
  342. * Handles the basic update loop, checking to see if we are in the running
  343. * state, determining if a move should be made, updating the snake's location.
  344. * 刷新游戏状态。每次游戏画面的更新、游戏数据的更新,都是依靠这个update()来完成的。
  345. */
  346. public void update() {
  347. if (mMode == RUNNING) {
  348. long now = System.currentTimeMillis();
  349. if (now - mLastMove > mMoveDelay) {  //这里是对蛇体游戏刚开始时连续的两个移动速率的控制
  350. //主要作用应该是mMode变化时,对update()正确效果的保障。
  351. clearTiles();       //清空 界面画布。
  352. updateWalls();      //重新绘制墙壁
  353. updateSnake();     //对蛇的 游戏逻辑 的处理 以及绘制
  354. updateApples();   //对果子的 游戏逻辑 的处理 以及绘制
  355. mLastMove = now;
  356. }
  357. mRedrawHandler.sleep(mMoveDelay);   //利用Handler进行 定时刷新的控制
  358. }
  359. }
  360. /**
  361. * Draws some walls.
  362. * 用setTile绘制墙壁
  363. */
  364. private void updateWalls() {
  365. for (int x = 0; x < mXTileCount; x++) {
  366. setTile(GREEN_STAR, x, 0);
  367. setTile(GREEN_STAR, x, mYTileCount - 1);
  368. }
  369. for (int y = 1; y < mYTileCount - 1; y++) {
  370. setTile(GREEN_STAR, 0, y);
  371. setTile(GREEN_STAR, mXTileCount - 1, y);
  372. }
  373. }
  374. /**
  375. * Draws some apples.
  376. * 绘制果子
  377. */
  378. private void updateApples() {
  379. for (Coordinate c : mAppleList) {
  380. setTile(YELLOW_STAR, c.x, c.y);
  381. }
  382. }
  383. /**
  384. * Figure out which way the snake is going, see if he's run into anything (the
  385. * walls, himself, or an apple). If he's not going to die, we then add to the
  386. * front and subtract from the rear in order to simulate motion. If we want to
  387. * grow him, we don't subtract from the rear.
  388. *
  389. */
  390. private void updateSnake() {
  391. boolean growSnake = false;  //吃过果子的蛇会长长。这个变量即为它的标记。
  392. // grab the snake by the head
  393. Coordinate head = mSnakeTrail.get(0);  //头部很重要,只有头部可能碰到果子。
  394. Coordinate newHead = new Coordinate(1, 1); //蛇下一步一定会前移,也就试newHead。长长只会从尾部增加。
  395. //那么为啥不用Coordinate newHead 呢?反正肯定会给他赋值的。
  396. //注意到之后咱们的程序是在switch语句中给newHead赋值的,这个是编译无法通过的~
  397. mDirection = mNextDirection;
  398. switch (mDirection) {
  399. case EAST: {
  400. newHead = new Coordinate(head.x + 1, head.y);
  401. break;
  402. }
  403. case WEST: {
  404. newHead = new Coordinate(head.x - 1, head.y);
  405. break;
  406. }
  407. case NORTH: {
  408. newHead = new Coordinate(head.x, head.y - 1);
  409. break;
  410. }
  411. case SOUTH: {
  412. newHead = new Coordinate(head.x, head.y + 1);
  413. break;
  414. }
  415. }
  416. // Collision detection
  417. // For now we have a 1-square wall around the entire arena
  418. //撞墙检测
  419. if ((newHead.x < 1) || (newHead.y < 1) || (newHead.x > mXTileCount - 2)
  420. || (newHead.y > mYTileCount - 2)) {
  421. setMode(LOSE);
  422. return;
  423. }
  424. // Look for collisions with itself
  425. //撞自己检测
  426. int snakelength = mSnakeTrail.size();
  427. for (int snakeindex = 0; snakeindex < snakelength; snakeindex++) {
  428. Coordinate c = mSnakeTrail.get(snakeindex);
  429. if (c.equals(newHead)) {
  430. setMode(LOSE);
  431. return;
  432. }
  433. }
  434. // Look for apples
  435. //吃果子检测
  436. int applecount = mAppleList.size();
  437. for (int appleindex = 0; appleindex < applecount; appleindex++) {
  438. Coordinate c = mAppleList.get(appleindex);
  439. if (c.equals(newHead)) {
  440. mAppleList.remove(c);
  441. addRandomApple();
  442. mScore++;
  443. mMoveDelay *= 0.9;
  444. growSnake = true;
  445. }
  446. }
  447. // push a new head onto the ArrayList and pull off the tail
  448. //前进
  449. mSnakeTrail.add(0, newHead);
  450. // except if we want the snake to grow
  451. if (!growSnake) {
  452. mSnakeTrail.remove(mSnakeTrail.size() - 1);
  453. }
  454. //绘制新的蛇体
  455. int index = 0;
  456. for (Coordinate c : mSnakeTrail) {
  457. if (index == 0) {
  458. setTile(YELLOW_STAR, c.x, c.y);
  459. } else {
  460. setTile(RED_STAR, c.x, c.y);
  461. }
  462. index++;
  463. }
  464. }
  465. /**
  466. * Simple class containing two integer values and a comparison function.
  467. * There's probably something I should use instead, but this was quick and
  468. * easy to build.
  469. * 这是坐标点的类。很简单的存储XY坐标。
  470. */
  471. private class Coordinate {
  472. public int x;
  473. public int y;
  474. public Coordinate(int newX, int newY) {
  475. x = newX;
  476. y = newY;
  477. }
  478. public boolean equals(Coordinate other) {
  479. if (x == other.x && y == other.y) {
  480. return true;
  481. }
  482. return false;
  483. }
  484. @Override
  485. public String toString() {
  486. return "Coordinate: [" + x + "," + y + "]";
  487. }
  488. }
  489. }

Snake.java

[java] view plaincopy
  1. package com.example.android.snake;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.Window;
  5. import android.widget.TextView;
  6. /**
  7. * Snake: a simple game that everyone can enjoy.
  8. * This is an implementation of the classic Game "Snake", in which you control a
  9. * serpent roaming around the garden looking for apples. Be careful, though,
  10. * because when you catch one, not only will you become longer, but you'll move
  11. * faster. Running into yourself or the walls will end the game.
  12. */
  13. public class Snake extends Activity {
  14. private SnakeView mSnakeView;
  15. private static String ICICLE_KEY = "snake-view";
  16. /**
  17. * Called when Activity is first created. Turns off the title bar, sets up
  18. * the content views, and fires up the SnakeView.
  19. *
  20. */
  21. @Override
  22. public void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.snake_layout);
  25. mSnakeView = (SnakeView) findViewById(R.id.snake);
  26. mSnakeView.setTextView((TextView) findViewById(R.id.text));
  27. if (savedInstanceState == null) {
  28. // We were just launched -- set up a new game
  29. mSnakeView.setMode(SnakeView.READY);
  30. } else {
  31. // We are being restored
  32. Bundle map = savedInstanceState.getBundle(ICICLE_KEY);
  33. if (map != null) {
  34. mSnakeView.restoreState(map);
  35. } else {
  36. mSnakeView.setMode(SnakeView.PAUSE);
  37. }
  38. }
  39. }
  40. @Override
  41. protected void onPause() {
  42. super.onPause();
  43. // Pause the game along with the activity
  44. mSnakeView.setMode(SnakeView.PAUSE);
  45. }
  46. @Override
  47. public void onSaveInstanceState(Bundle outState) {
  48. //Store the game state
  49. outState.putBundle(ICICLE_KEY, mSnakeView.saveState());
  50. }
  51. }

转载于:https://www.cnblogs.com/love-in-paradise/p/3403861.html

自做贪吃蛇游戏的android实现相关推荐

  1. 一小时学会用 Opencv 做贪吃蛇游戏(Python版)

    点击上方"小白学视觉",选择加"星标"或"置顶"重磅干货,第一时间送达 作者 | 源代码•宸 来源丨CSDN博客 使用Mediapipe 水 ...

  2. 【课件】使用Python写贪吃蛇游戏(pygame)

    使用Python写贪吃蛇游戏(pygame) 课件地址:https://blog.csdn.net/a772304419/article/details/130087202 本地路径: cd /D/W ...

  3. 用Python做贪吃蛇小游戏

    用Python做贪吃蛇小游戏 简介 引言 游戏预览 结构图 代码框架图 代码讲解 main主函数-开始工作 show_start_info()欢迎进入游戏 running_game-让我们开始游戏吧 ...

  4. 【python实战】用pygame做个贪吃蛇游戏

    嗨,大家好,我是一条. 告诉大家一个好消息,一条IT访问量突破20w,达到申请博客专家的条件.感谢大家的支持,一条会创作更多的优质内容. 为了让更多的人看到一条的分享,一条准备报名原力计划,报名条件是 ...

  5. 手机java做贪吃蛇_如何用Java写一个贪吃蛇游戏

    这是一位拓胜学员用Java写贪吃蛇游戏的心得:今天课程设计终于结束了自己学java没以前学C+那么用功了觉得我学习在哪里都是个开口向上的抛物线,现在应该在右半边吧,好了进入正题. 写java贪吃蛇也是 ...

  6. Java贪吃蛇暂停怎么做_Java实现贪吃蛇游戏(1小时学会)

    今天就来拿贪吃蛇小游戏来练练手吧! 贪吃蛇游戏规则: 1.按下空格键(游戏未结束)则游戏暂停或开始: 2.按下空格键(游戏结束后)则游戏重新开始: 3.当贪吃蛇的头部撞到身体时则贪吃蛇死亡(游戏结束) ...

  7. 如何用html做一个贪吃蛇,如何用HTML5制作贪吃蛇游戏

    如何用HTML5制作贪吃蛇游戏 发布时间:2020-07-09 15:09:59 来源:亿速云 阅读:122 作者:Leah 如何用HTML5制作贪吃蛇游戏?很多新手对此不是很清楚,为了帮助大家解决这 ...

  8. java gui怎么做游戏_Java中关于GUI实现贪吃蛇游戏的简单方法

    这篇文章主要介绍了Java GUI编程之贪吃蛇游戏简单实现方法,详细分析了贪吃蛇游戏的具体实现步骤与相关注意事项,并附带demo源码供读者下载参考,需要的朋友可以参考下 本文实例讲述了Java GUI ...

  9. java gui怎么做游戏_Java GUI编程之贪吃蛇游戏简单实现方法【附demo源码下载】

    本文实例讲述了Java GUI编程之贪吃蛇游戏简单实现方法.分享给大家供大家参考,具体如下: 例子简单,界面简陋 请见谅 项目结构如下 Constant.jvava 代码如下: package sna ...

最新文章

  1. 润乾V5手机报表说明文档
  2. 机器人学习--Robotics 4:Perception(宾夕法尼亚大学COURSERA课程)
  3. 执行SQL-获取缓存
  4. 多元回归求解 机器学习_金融领域里的机器学习算法介绍:人工神经网络
  5. django - settings.py
  6. Linux系统定时任务Crond
  7. 美国慈善机构Kars4Kids意外泄露了上万名捐赠者的个人信息
  8. 如何做好产品需求设计和开发
  9. 如何打开设置了密码的ZIP文件?
  10. 爬虫小白第一篇 西刺代理
  11. 设备发现[Airplay投屏应用]-mdns协议简介
  12. 怎么用计算机直接截图,电脑截图快捷键怎么使用,电脑怎么快捷键截图
  13. android按键精灵兼容性,为什么逍遥android模拟器安装按键精灵总是出错
  14. 论文投稿指南——中国(中文EI)期刊推荐(第6期)
  15. Nvme硬盘完美安装官方原版win10教程(含激活)
  16. Python爬虫获取企查查公开的企业信息
  17. 埃尔米特三次样条插值算法-JAVA版本实现
  18. java编写md5加密解密算法
  19. Java | 由天数计算日期:长整型long转换为日期
  20. 【洞察人性】 理解行为背后的动机

热门文章

  1. Java:实现剪枝算法(附完整源码)
  2. 将centos系统时间修改为上海时区(CST)
  3. 模拟扑克牌,随机抽取五张牌,判断是否为同花顺
  4. 微积分的魅力,无敌!
  5. 为什么手机上wifi都用sdio接口,不用usb接口?
  6. Linux共享Windows文件
  7. 关于亚马逊云的负载均衡器(AWS-LD)的相关问题记录
  8. 神武3手游怎么授权位置服务器,《神武3》手游新区开放 萌新如何紧跟第一梯队?...
  9. JavaWeb过滤器解决中文乱码问题
  10. php 5.6 vc10,深度测评剖析德尔玛vc20对比vc10评测区别大不大?哪个好?使用分享爆料...