打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
android游戏开发入门: 贪吃蛇 源代码分析


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

这里的贪吃蛇的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);                }            }        }    }}



SnakeView.java

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 ArrayList mSnakeTrail = 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());    }}



本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
从Android-sdk自带的例子快速学习Android--贪吃蛇
Android实例剖析笔记(七)
Android开发网上的一些重要知识点
Android高手进阶教程(二十二)之---Android中几种图像特效处理的集锦!! -...
第52章、Bitmap图像处理(从零开始学Android)
android Bitmap学习总结
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服