贪吃蛇是一款足够经典的游戏。它的经典,在于用户操作的简单,在于技术实现的简介,在于他的经久不衰。
这里的贪吃蛇的android实现,是SDK Samples中的开源例程。可能各位都有看过~界面如下图啦~
作为一个刚入门或者还没入门的新手,着实花了我一些力气来理解这段代码。
对于各种不懂的地方,慢慢查询资料,对于新的方法,通过修改代码尝试效果。到现在终于能算个一知半解。
在代码中,对于自己有所收获的地方,我都做了相应的注释。
回过头来,觉得从这段代码中,能学到不少东西~~
包括android应用的基本架构,他的面向对象的思想,以及代码的简洁明了。
于是,我想到,何不将这些东西分享出来,如果碰巧对感兴趣的朋友们有搜帮助,那就更好了~
好了,闲话不说~代码和注释如下(处于对源码的敬意,原本的英文注释部分都没有删去~大家可以配合理解):
PS:最近我正在写自己的“贪吃蛇”,说事贪吃蛇,其实完全颠覆了这个经典版本的设计理念和操作方式。具体细节先卖一个关子,作品准备参加这次第二届大学生android应用开发大赛。
应该一个月内能完成,到时候也会开源出代码来~欢迎大家讨论指正·~
************************************************************************************************************************************
Snake工程中,总共有三个文件: *TileView是基于Android的View类实现的方块图类,用来支撑上层类的调用,绘制方块图的显示界面。通过这些代码,能打之了解如何 扩展View,实现特色的界面效果。 *SnakeView调用了TileView,实现了游戏逻辑 和 具体的显示。 *Snake为主Activity类。
建议大家按照上面的顺序看三个文件,可能逻辑上更舒服一点~~
下面贴上代码和注释。
PS: 调试版本为android2.2。 其他版本应该也没问题吧,不过得用虚拟机。因为它是上下左右按键操作,现在大多数android机是没有方向键的吧。
TileView.java
package com.example.android.snake;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import android.view.View;/** * TileView: a View-variant designed for handling arrays of 'icons' or other * drawables. * */public class TileView extends View { /** * Parameters controlling the size of the tiles and their range within view. * Width/Height are in pixels, and Drawables will be scaled to fit to these * dimensions. X/Y Tile Counts are the number of tiles that will be drawn. */ protected static int mTileSize; //每个tile的边长的像素数量 protected static int mXTileCount; //屏幕内能容纳的 X方向上方块的总数量 protected static int mYTileCount;//屏幕内能容纳的 Y方向上方块的总数量 private static int mXOffset; //原点坐标,按pixel计。 private static int mYOffset; /** * A hash that maps integer handles specified by the subclasser to the * drawable that will be used for that reference * 存储着不同种类的bitmap图。通过resetTiles,loadTile,将游戏中的方块加载到这个数组。 * 可以理解为 砖块字典 */ private Bitmap[] mTileArray; /** * A two-dimensional array of integers in which the number represents the * index of the tile that should be drawn at that locations * 存储整个界面内每个tile位置应该绘制的tile。 * 可看作是我们直接操作的画布。 * 通过setTile、clearTile 进行图形显示的修改操作。 * */ private int[][] mTileGrid; //画笔,canvas的图形绘制,需要画笔Paint实现。 private final Paint mPaint = new Paint(); public TileView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); //使用TypedArray,获取在attrs.xml中为TileView定义的新属性tileSize 。参考: http://weizhulin.blog.51cto.com/1556324/311453 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView); mTileSize = a.getInt(R.styleable.TileView_tileSize, 12); a.recycle(); } public TileView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView); mTileSize = a.getInt(R.styleable.TileView_tileSize, 12); a.recycle(); } /** * Rests the internal array of Bitmaps used for drawing tiles, and * sets the maximum index of tiles to be inserted * 重置清零mTileArray,在游戏初始的时候使用。 * 即清空砖块字典 * @param tilecount */ public void resetTiles(int tilecount) { mTileArray = new Bitmap[tilecount]; } /* * 当改变屏幕大小尺寸时,同时修改tile的相关计数指标。 */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { mXTileCount = (int) Math.floor(w / mTileSize); mYTileCount = (int) Math.floor(h / mTileSize); //mXOffset mYOffset是绘图的起点坐标。 mXOffset = ((w - (mTileSize * mXTileCount)) / 2); mYOffset = ((h - (mTileSize * mYTileCount)) / 2); mTileGrid = new int[mXTileCount][mYTileCount]; clearTiles(); } /** * Function to set the specified Drawable as the tile for a particular * integer key. * 加载具体的砖块图片 到 砖块字典。 * 即将对应的砖块的图片 对应的加载到 mTileArray数组中 * @param key * @param tile */ public void loadTile(int key, Drawable tile) { //这里做了一个 Drawable 到 bitmap 的转换。由于外部程序使用的时候是直接读取资源文件中的图片, //是drawable格式,而我们的数组是bitmap格式,方便最终的绘制。所以,需要进行一次到 bitmap的转换。 Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); tile.setBounds(0, 0, mTileSize, mTileSize); tile.draw(canvas); mTileArray[key] = bitmap; } /** * Used to indicate that a particular tile (set with loadTile and referenced * by an integer) should be drawn at the given x/y coordinates during the * next invalidate/draw cycle. * 在相应的坐标位置绘制相应的砖块 * 记得哦,mTileGrid其实就是我们直接操作的画布。 * @param tileindex * @param x * @param y */ public void setTile(int tileindex, int x, int y) { mTileGrid[x][y] = tileindex; } /** * Resets all tiles to 0 (empty) * 清空图形显示。 * 用以更新画面。 * 调用了绘图的setTile()。 */ public void clearTiles() { for (int x = 0; x < mxtilecount;="" x++)="" {="" for="" (int="" y="0;" y="">< mytilecount;="" y++)="" {="" settile(0,="" x,="" y);="" }="" }="" }/*="" *="" 将我们直接操作的画布绘制到手机界面上!="" *="" @see="" android.view.view#ondraw(android.graphics.canvas)="" */="" @override="" public="" void="" ondraw(canvas="" canvas)="" {="" super.ondraw(canvas);="" for="" (int="" x="0;" x="">< mxtilecount;="" x="" +="1)" {="" for="" (int="" y="0;" y="">< mytilecount;="" y="" +="1)" {="" if="" (mtilegrid[x][y]=""> 0) { canvas.drawBitmap(mTileArray[mTileGrid[x][y]], mXOffset + x * mTileSize, mYOffset + y * mTileSize, mPaint); } } } }}
package com.example.android.snake;import java.util.ArrayList;import java.util.Random;import android.content.Context;import android.content.res.Resources;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.os.Bundle;import android.util.Log;import android.view.KeyEvent;import android.view.View;import android.widget.TextView;/** * SnakeView: implementation of a simple game of Snake */public class SnakeView extends TileView { private static final String TAG = 'SnakeView'; /** * Current mode of application: READY to run, RUNNING, or you have already * lost. static final ints are used instead of an enum for performance * reasons. * 游戏的四种状态。初始时为 预备开始的状态。 */ private int mMode = READY; public static final int PAUSE = 0; //暂停 public static final int READY = 1; //准备好了,预备开始 public static final int RUNNING = 2;//正在运行 public static final int LOSE = 3; //结束,输了游戏 /** * Current direction the snake is headed. * 蛇体运动的方向标识。 */ private int mDirection = NORTH; private int mNextDirection = NORTH; private static final int NORTH = 1; private static final int SOUTH = 2; private static final int EAST = 3; private static final int WEST = 4; /** * Labels for the drawables that will be loaded into the TileView class * 游戏中仅有的三种砖块对应的数值。 */ private static final int RED_STAR = 1; private static final int YELLOW_STAR = 2; private static final int GREEN_STAR = 3; /** * mScore: used to track the number of apples captured mMoveDelay: number of * milliseconds between snake movements. This will decrease as apples are * captured. */ private long mScore = 0; //记录获得的分数。 private long mMoveDelay = 600; //每移动一步的延时。初始时设置为600ms,以后每吃一个果子,打个9折 //造成的结果是速度越来越快。 /** * mLastMove: tracks the absolute time when the snake last moved, and is used * to determine if a move should be made based on mMoveDelay. * 记录上次移动的确切时间。 * 同mMoveDelay一起处理与用户的异步操作的协同问题。 */ private long mLastMove; /** * mStatusText: text shows to the user in some run states * 用来显示游戏状态的TextView */ private TextView mStatusText; /** * mSnakeTrail: a list of Coordinates that make up the snake's body * mAppleList: the secret location of the juicy apples the snake craves. * 两个链表,分别用来存储 蛇体 和 果子的坐标。 * 每次蛇体的运动,蛇体的增长,产生新的苹果,被吃掉苹果,都会在这里记录。 */ private ArrayListmSnakeTrail = new ArrayList (); private ArrayList mAppleList = new ArrayList (); /** * Everyone needs a little randomness in their life * 随机数生成器。用来产生随机的苹果。在addRandomApple()中使用。 */ private static final Random RNG = new Random(); /** * Create a simple handler that we can use to cause animation to happen. We * set ourselves as a target and we can use the sleep() * function to cause an update/invalidate to occur at a later date. * 用Handler机制实现定时刷新。 * 为什么使用Handler呢?大家可以参考 android 的线程模型(注意UI线程不是线程安全的~) * 具体使用方法网上的资源很多,在此不赘述~ */ private RefreshHandler mRedrawHandler = new RefreshHandler(); class RefreshHandler extends Handler { //获取消息并处理 @Override public void handleMessage(Message msg) { SnakeView.this.update(); SnakeView.this.invalidate(); //刷新view为基类的界面 } //定时发送消息给UI线程,以此达到更新的效果。 public void sleep(long delayMillis) { this.removeMessages(0); //清空消息队列,Handler进入对新消息的等待 sendMessageDelayed(obtainMessage(0), delayMillis); //定时发送新消息,激活handler } }; public SnakeView(Context context, AttributeSet attrs) { super(context, attrs); initSnakeView(); //构造函数中,别忘了,初始化游戏~ } public SnakeView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initSnakeView(); } //初始化SnakeView类,注意,这根初始化游戏是不一样的。 private void initSnakeView() { setFocusable(true); //设置焦点,由于存在 文字界面 和 游戏界面的跳转。这个focus是不可或缺的。 //取得资源中的图片,加载到 砖块字典 中。 Resources r = this.getContext().getResources(); resetTiles(4); loadTile(RED_STAR, r.getDrawable(R.drawable.redstar)); loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar)); loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar)); } //如果不是从暂停中回复,就绪要 初始化游戏了。 private void initNewGame() { //清空保存蛇体和果子的数据结构。 mSnakeTrail.clear(); mAppleList.clear(); // For now we're just going to load up a short default eastbound snake // that's just turned north // 设定初始状态的蛇体的位置。 mSnakeTrail.add(new Coordinate(7, 7)); mSnakeTrail.add(new Coordinate(6, 7)); mSnakeTrail.add(new Coordinate(5, 7)); mSnakeTrail.add(new Coordinate(4, 7)); mSnakeTrail.add(new Coordinate(3, 7)); mSnakeTrail.add(new Coordinate(2, 7)); mNextDirection = NORTH; // Two apples to start with addRandomApple(); addRandomApple(); mMoveDelay = 600; mScore = 0; } /** * Given a ArrayList of coordinates, we need to flatten them into an array of * ints before we can stuff them into a map for flattening and storage. * * @param cvec : a ArrayList of Coordinate objects * @return : a simple array containing the x/y values of the coordinates * as [x1,y1,x2,y2,x3,y3...】 * 在游戏暂停时,需要通过Bundle方式保存数据。见saveState()。 * Bundle支持简单的数组。 * 所以需要将我们的部分数据结构,如蛇体和苹果位置的数组,转换成简单的序列化的int数组。 */ private int[] coordArrayListToArray(ArrayList cvec) { int count = cvec.size(); int[] rawArray = new int[count * 2]; for (int index = 0; index < count;="" index++)="" {="" coordinate="" c="cvec.get(index);" rawarray[2="" *="" index]="c.x;" rawarray[2="" *="" index="" +="" 1]="c.y;" }="" return="" rawarray;="" }="" *="" *="" save="" game="" state="" so="" that="" the="" user="" does="" not="" lose="" anything="" *="" if="" the="" game="" process="" is="" killed="" while="" we="" are="" in="" the="" *="" background.="" *="" 在意外情况下,暂时性保存游戏数据,在下次打开游戏时,可以继续游戏。如来电话了。="" *="" @return="" a="" bundle="" with="" this="" view's="" state="" */="" public="" bundle="" savestate()="" {="" bundle="" map="new" bundle();="" map.putintarray('mapplelist',="" coordarraylisttoarray(mapplelist));="" map.putint('mdirection',="" integer.valueof(mdirection));="" map.putint('mnextdirection',="" integer.valueof(mnextdirection));="" map.putlong('mmovedelay',="" long.valueof(mmovedelay));="" map.putlong('mscore',="" long.valueof(mscore));="" map.putintarray('msnaketrail',="" coordarraylisttoarray(msnaketrail));="" return="" map;="" }="" *="" *="" given="" a="" flattened="" array="" of="" ordinate="" pairs,="" we="" reconstitute="" them="" into="" a="" *="" arraylist="" of="" coordinate="" objects="" *="" 是coordarraylisttoarray()的逆过程,用来读取保存在bundle中的数据。="" *="" @param="" rawarray="" :="" [x1,y1,x2,y2,...]="" *="" @return="" a="" arraylist="" of="" coordinates="" */="" private=""> coordArrayToArrayList(int[] rawArray) { ArrayList coordArrayList = new ArrayList (); int coordCount = rawArray.length; for (int index = 0; index < coordcount;="" index="" +="2)" {="" coordinate="" c="new" coordinate(rawarray[index],="" rawarray[index="" +="" 1]);="" coordarraylist.add(c);="" }="" return="" coordarraylist;="" }="" *="" *="" restore="" game="" state="" if="" our="" process="" is="" being="" relaunched="" *="" 回复游戏数据。是savestate()的逆过程="" *="" @param="" icicle="" a="" bundle="" containing="" the="" game="" state="" */="" public="" void="" restorestate(bundle="" icicle)="" {="" setmode(pause);="" mapplelist="coordarraytoarraylist(icicle.getintarray('mapplelist'));" mdirection="icicle.getint('mdirection');" mnextdirection="icicle.getint('mnextdirection');" mmovedelay="icicle.getlong('mmovedelay');" mscore="icicle.getlong('mscore');" msnaketrail="coordarraytoarraylist(icicle.getintarray('msnaketrail'));" }="" *="" handles="" key="" events="" in="" the="" game.="" update="" the="" direction="" our="" snake="" is="" traveling="" *="" based="" on="" the="" dpad.="" ignore="" events="" that="" would="" cause="" the="" snake="" to="" immediately="" *="" turn="" back="" on="" itself.="" *="" 按键的监听。="" *="" 现在大多数的android手机都没有按键了。="" *="" 笔者就是在自己的模拟机上才能正常的使用这款小游戏的="" -="" -#="" *="" @see="" android.view.view#oncgdown(int,="" android.os.keyevent)="" */="" @override="" public="" boolean="" oncgdown(int="" keycode,="" keyevent="" msg)="" {="" 这里是游戏的基本逻辑。如果你还没尝试一下它,先玩玩再说吧。那有助于你对代码的理解~="" if="" (keycode="=" keyevent.keycode_dpad_up)="" {="" if="" (mmode="=" ready="" |="" mmode="=" lose)="" {="" *="" at="" the="" beginning="" of="" the="" game,="" or="" the="" end="" of="" a="" previous="" one,="" *="" we="" should="" start="" a="" new="" game.="" */="" initnewgame();="" setmode(running);="" update();="" update()实现了对游戏数据的更新,是整个游戏的推动力。="" return="" (true);="" }="" if="" (mmode="=" pause)="" {="" *="" if="" the="" game="" is="" merely="" paused,="" we="" should="" just="" continue="" where="" *="" we="" left="" off.="" */="" setmode(running);="" update();="" return="" (true);="" }="" if="" (mdirection="" !="south)" {="" 如果按键的方向="" 跟蛇本身的运动方向完全相反,则无法执行="" mnextdirection="north;" }="" return="" (true);="" }="" if="" (keycode="=" keyevent.keycode_dpad_down)="" {="" if="" (mdirection="" !="north)" {="" mnextdirection="south;" }="" return="" (true);="" }="" if="" (keycode="=" keyevent.keycode_dpad_left)="" {="" if="" (mdirection="" !="east)" {="" mnextdirection="west;" }="" return="" (true);="" }="" if="" (keycode="=" keyevent.keycode_dpad_right)="" {="" if="" (mdirection="" !="west)" {="" mnextdirection="east;" }="" return="" (true);="" }="" return="" super.oncgdown(keycode,="" msg);="" }="" *="" *="" sets="" the="" textview="" that="" will="" be="" used="" to="" give="" information="" (such="" as="" 'game="" *="" over'="" to="" the="" user.="" *="" 起初不明白这个方法有什么作用。删除了以后才发现错误。snake类会调用到它,来绑定到相应的textview.="" */="" public="" void="" settextview(textview="" newview)="" {="" mstatustext="newview;" }="" *="" *="" updates="" the="" current="" mode="" of="" the="" application="" (running="" or="" paused="" or="" the="" like)="" *="" as="" well="" as="" sets="" the="" visibility="" of="" textview="" for="" notification="" *="" *="" @param="" newmode="" */="" public="" void="" setmode(int="" newmode)="" {="" int="" oldmode="mmode;" mmode="newmode;" if="" (newmode="=" running="" &="" oldmode="" !="running)" {="" mstatustext.setvisibility(view.invisible);="" 游戏开始后,将textview的文字显示设置为不可见。="" update();="" 注意到,在initgame中也有update(),不过放心~="" 多次重复="" update不会影响效果的,="" 蛇的移动有mlastmove="" 和="" mmovedelay="" 来校验。这会在update()中体现。="" 当然,经过实验,注释掉这个update()似乎不会影响结果噢。="" return;="" }="" resources="" res="getcontext().getresources();" charsequence="" str='' ;="" if="" (newmode="=" pause)="" {="" str="res.gettext(r.string.mode_pause);" }="" if="" (newmode="=" ready)="" {="" str="res.gettext(r.string.mode_ready);" }="" if="" (newmode="=" lose)="" {="" str="res.getstring(r.string.mode_lose_prefix)" +="" mscore="" +="" res.getstring(r.string.mode_lose_suffix);="" }="" mstatustext.settext(str);="" mstatustext.setvisibility(view.visible);="" }="" *="" *="" selects="" a="" random="" location="" within="" the="" garden="" that="" is="" not="" currently="" covered="" *="" by="" the="" snake.="" currently="" _could_="" go="" into="" an="" infinite="" loop="" if="" the="" snake="" *="" currently="" fills="" the="" garden,="" but="" we'll="" leave="" discovery="" of="" this="" prize="" to="" a="" *="" truly="" excellent="" snake-player.="" *="" 在地图上随机的增加果子。注意苹果的位置不可以是蛇体所在噢~这里有个小bug,没有检测="" *="" 产生的果子位置="" 可能与="" 另一个果子位置重合。="" *="" 新产生的果子的坐标会增加到mapplist的数组上。="" */="" private="" void="" addrandomapple()="" {="" coordinate="" newcoord="null;" boolean="" found="false;" while="" (!found)="" {="" choose="" a="" new="" location="" for="" our="" apple="" 注意别产生在边框上的果子="" int="" newx="1" +="" rng.nextint(mxtilecount="" -="" 2);="" int="" newy="1" +="" rng.nextint(mytilecount="" -="" 2);="" newcoord="new" coordinate(newx,="" newy);="" make="" sure="" it's="" not="" already="" under="" the="" snake="" boolean="" collision="false;" int="" snakelength="msnaketrail.size();" for="" (int="" index="0;" index="">< snakelength;="" index++)="" {="" if="" (msnaketrail.get(index).equals(newcoord))="" {="" collision="true;" }="" }="" if="" we're="" here="" and="" there's="" been="" no="" collision,="" then="" we="" have="" a="" good="" location="" for="" an="" apple.="" otherwise,="" we'll="" circle="" back="" and="" try="" again="" found="!collision;" }="" if="" (newcoord="=" null)="" {="" log.e(tag,="" 'somehow="" ended="" up="" with="" a="" null="" newcoord!');="" }="" mapplelist.add(newcoord);="" }="" *="" *="" handles="" the="" basic="" update="" loop,="" checking="" to="" see="" if="" we="" are="" in="" the="" running="" *="" state,="" determining="" if="" a="" move="" should="" be="" made,="" updating="" the="" snake's="" location.="" *="" 刷新游戏状态。每次游戏画面的更新、游戏数据的更新,都是依靠这个update()来完成的。="" */="" public="" void="" update()="" {="" if="" (mmode="=" running)="" {="" long="" now="System.currentTimeMillis();" if="" (now="" -="" mlastmove=""> mMoveDelay) { //这里是对蛇体游戏刚开始时连续的两个移动速率的控制 //主要作用应该是mMode变化时,对update()正确效果的保障。 clearTiles(); //清空 界面画布。 updateWalls(); //重新绘制墙壁 updateSnake(); //对蛇的 游戏逻辑 的处理 以及绘制 updateApples(); //对果子的 游戏逻辑 的处理 以及绘制 mLastMove = now; } mRedrawHandler.sleep(mMoveDelay); //利用Handler进行 定时刷新的控制 } } /** * Draws some walls. * 用setTile绘制墙壁 */ private void updateWalls() { for (int x = 0; x < mxtilecount;="" x++)="" {="" settile(green_star,="" x,="" 0);="" settile(green_star,="" x,="" mytilecount="" -="" 1);="" }="" for="" (int="" y="1;" y="">< mytilecount="" -="" 1;="" y++)="" {="" settile(green_star,="" 0,="" y);="" settile(green_star,="" mxtilecount="" -="" 1,="" y);="" }="" }="" *="" *="" draws="" some="" apples.="" *="" 绘制果子="" */="" private="" void="" updateapples()="" {="" for="" (coordinate="" c="" :="" mapplelist)="" {="" settile(yellow_star,="" c.x,="" c.y);="" }="" }="" *="" *="" figure="" out="" which="" way="" the="" snake="" is="" going,="" see="" if="" he's="" run="" into="" anything="" (the="" *="" walls,="" himself,="" or="" an="" apple).="" if="" he's="" not="" going="" to="" die,="" we="" then="" add="" to="" the="" *="" front="" and="" subtract="" from="" the="" rear="" in="" order="" to="" simulate="" motion.="" if="" we="" want="" to="" *="" grow="" him,="" we="" don't="" subtract="" from="" the="" rear.="" *="" */="" private="" void="" updatesnake()="" {="" boolean="" growsnake="false;" 吃过果子的蛇会长长。这个变量即为它的标记。="" grab="" the="" snake="" by="" the="" head="" coordinate="" head="mSnakeTrail.get(0);" 头部很重要,只有头部可能碰到果子。="" coordinate="" newhead="new" coordinate(1,="" 1);="" 蛇下一步一定会前移,也就试newhead。长长只会从尾部增加。="" 那么为啥不用coordinate="" newhead="" 呢?反正肯定会给他赋值的。="" 注意到之后咱们的程序是在switch语句中给newhead赋值的,这个是编译无法通过的~="" mdirection="mNextDirection;" switch="" (mdirection)="" {="" case="" east:="" {="" newhead="new" coordinate(head.x="" +="" 1,="" head.y);="" break;="" }="" case="" west:="" {="" newhead="new" coordinate(head.x="" -="" 1,="" head.y);="" break;="" }="" case="" north:="" {="" newhead="new" coordinate(head.x,="" head.y="" -="" 1);="" break;="" }="" case="" south:="" {="" newhead="new" coordinate(head.x,="" head.y="" +="" 1);="" break;="" }="" }="" collision="" detection="" for="" now="" we="" have="" a="" 1-square="" wall="" around="" the="" entire="" arena="" 撞墙检测="" if="" ((newhead.x="">< 1)="" ||="" (newhead.y="">< 1)="" ||="" (newhead.x=""> mXTileCount - 2) || (newHead.y > mYTileCount - 2)) { setMode(LOSE); return; } // Look for collisions with itself //撞自己检测 int snakelength = mSnakeTrail.size(); for (int snakeindex = 0; snakeindex < snakelength;="" snakeindex++)="" {="" coordinate="" c="mSnakeTrail.get(snakeindex);" if="" (c.equals(newhead))="" {="" setmode(lose);="" return;="" }="" }="" look="" for="" apples="" 吃果子检测="" int="" applecount="mAppleList.size();" for="" (int="" appleindex="0;" appleindex="">< applecount;="" appleindex++)="" {="" coordinate="" c="mAppleList.get(appleindex);" if="" (c.equals(newhead))="" {="" mapplelist.remove(c);="" addrandomapple();="" mscore++;="" mmovedelay="" *="0.9;" growsnake="true;" }="" }="" push="" a="" new="" head="" onto="" the="" arraylist="" and="" pull="" off="" the="" tail="" 前进="" msnaketrail.add(0,="" newhead);="" except="" if="" we="" want="" the="" snake="" to="" grow="" if="" (!growsnake)="" {="" msnaketrail.remove(msnaketrail.size()="" -="" 1);="" }="" 绘制新的蛇体="" int="" index="0;" for="" (coordinate="" c="" :="" msnaketrail)="" {="" if="" (index="=" 0)="" {="" settile(yellow_star,="" c.x,="" c.y);="" }="" else="" {="" settile(red_star,="" c.x,="" c.y);="" }="" index++;="" }="" }="" *="" *="" simple="" class="" containing="" two="" integer="" values="" and="" a="" comparison="" function.="" *="" there's="" probably="" something="" i="" should="" use="" instead,="" but="" this="" was="" quick="" and="" *="" easy="" to="" build.="" *="" 这是坐标点的类。很简单的存储xy坐标。="" */="" private="" class="" coordinate="" {="" public="" int="" x;="" public="" int="" y;="" public="" coordinate(int="" newx,="" int="" newy)="" {="" x="newX;" y="newY;" }="" public="" boolean="" equals(coordinate="" other)="" {="" if="" (x="=" other.x="" &&="" y="=" other.y)="" {="" return="" true;="" }="" return="" false;="" }="" @override="" public="" string="" tostring()="" {="" return="" 'coordinate:="" ['="" +="" x="" +="" ','="" +="" y="" +="" ']';="" }="" }="">
Snake.java
package com.example.android.snake;import android.app.Activity;import android.os.Bundle;import android.view.Window;import android.widget.TextView;/** * Snake: a simple game that everyone can enjoy. * This is an implementation of the classic Game 'Snake', in which you control a * serpent roaming around the garden looking for apples. Be careful, though, * because when you catch one, not only will you become longer, but you'll move * faster. Running into yourself or the walls will end the game. */public class Snake extends Activity { private SnakeView mSnakeView; private static String ICICLE_KEY = 'snake-view'; /** * Called when Activity is first created. Turns off the title bar, sets up * the content views, and fires up the SnakeView. * */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.snake_layout); mSnakeView = (SnakeView) findViewById(R.id.snake); mSnakeView.setTextView((TextView) findViewById(R.id.text)); if (savedInstanceState == null) { // We were just launched -- set up a new game mSnakeView.setMode(SnakeView.READY); } else { // We are being restored Bundle map = savedInstanceState.getBundle(ICICLE_KEY); if (map != null) { mSnakeView.restoreState(map); } else { mSnakeView.setMode(SnakeView.PAUSE); } } } @Override protected void onPause() { super.onPause(); // Pause the game along with the activity mSnakeView.setMode(SnakeView.PAUSE); } @Override public void onSaveInstanceState(Bundle outState) { //Store the game state outState.putBundle(ICICLE_KEY, mSnakeView.saveState()); }}
联系客服