zoukankan      html  css  js  c++  java
  • androidSnake(阅读源码)

    /MySnake/src/edu/hhxy/android/snake/Snake.java

    package edu.hhxy.android.snake;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.TextView;
    
    public class Snake extends Activity {
        private SnakeView mSnakeView;
        private static String ICICLE_KEY = "snake-view";
        @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
            mSnakeView.setMode(SnakeView.PAUSE);
        }
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
            // Store the game state
            outState.putBundle(ICICLE_KEY, mSnakeView.saveState());
        }
    }

    /MySnake/src/edu/hhxy/android/snake/SnakeView.java

    package edu.hhxy.android.snake;
    
    import java.util.ArrayList;
    import java.util.Random;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.content.res.Resources;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.KeyEvent;
    import android.view.View;
    import android.widget.TextView;
    import edu.hhxy.android.utils.Coordinate;
    import edu.hhxy.android.utils.SnakeDataUtils;
    
    @SuppressLint("HandlerLeak")
    public class SnakeView extends TileView {
        private SnakeDataUtils snakeDataUtils = new SnakeDataUtils();
        private static final String TAG = "SnakeView";
        // mMode一个变量表示,用于确定游戏是PAUSE、READY、RUNNING、LOSE
        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;
    
        // mDirection 当前蛇头的方向,蛇头方向有NORTH、SOUTH、EAST、WEST
        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;
    
        // 什么颜色的苹果RED_STAR红、YELLOW_STAR黄、GREEN_STAR绿
        private static final int RED_STAR = 1;
        private static final int YELLOW_STAR = 2;
        private static final int GREEN_STAR = 3;
    
        // mScore:当前得分
        private long mScore = 0;
        // 刷新屏幕的时间
        private long mMoveDelay = 600;
        // 最后一次移动的时间,用now-mLastMove可知已经刷新了多长时间
        private long mLastMove;
    
        /**
         * mStatusText: text shows to the user in some run states
         */
        private TextView mStatusText;
    
        /**
         * mSnakeTrail: 蛇链表 mAppleList: 苹果链表
         */
        private ArrayList<Coordinate> mSnakeTrail = new ArrayList<Coordinate>();
        private ArrayList<Coordinate> mAppleList = new ArrayList<Coordinate>();
        // 随机数产生类
        private static final Random RNG = new Random();
    
        // 线程,定时刷新屏幕
        private RefreshHandler mRedrawHandler = new RefreshHandler();
    
        class RefreshHandler extends Handler {
            public void handleMessage(Message msg) {
                SnakeView.this.update();
                SnakeView.this.invalidate();
            }
    
            public void sleep(long delayMillis) {
                this.removeMessages(0);
                sendMessageDelayed(obtainMessage(0), delayMillis);
            }
        };
    
        // 重写父类构造方法
        public SnakeView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initSnakeView();
        }
    
        public SnakeView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            initSnakeView();
        }
    
        // 初始化
        private void initSnakeView() {
            setFocusable(true);
    
            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();// 新游戏开始,清空list
            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;
    
            // 添加两个苹果,准备被蛇吃
            addRandomApple();
            addRandomApple();
    
            mMoveDelay = 600;
            mScore = 0;
        }
    
        // 保存下蛇和苹果的坐标,蛇的方向,分数,延迟时间等信息
        public Bundle saveState() {
            Bundle map = new Bundle();
    
            map.putIntArray("mAppleList",
                    snakeDataUtils.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",
                    snakeDataUtils.coordArrayListToArray(mSnakeTrail));
    
            return map;
        }
    
        // 存储数据
        public void restoreState(Bundle icicle) {
            setMode(PAUSE);
    
            mAppleList = snakeDataUtils.coordArrayToArrayList(icicle
                    .getIntArray("mAppleList"));
            mDirection = icicle.getInt("mDirection");
            mNextDirection = icicle.getInt("mNextDirection");
            mMoveDelay = icicle.getLong("mMoveDelay");
            mScore = icicle.getLong("mScore");
            mSnakeTrail = snakeDataUtils.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.
         * 
         * (non-Javadoc)
         * 
         * @see android.view.View#onKeyDown(int, android.os.KeyEvent)
         */
        @Override
        public boolean onKeyDown(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();
                    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.onKeyDown(keyCode, msg);
        }
    
        /**
         * Sets the TextView that will be used to give information (such as "Game
         * Over" to the user.
         * 
         * @param newView
         */
        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);
                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);
        }
    
        // 添加一个随机的苹果
        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);
    
                // 保证苹果不出现在蛇身上
                boolean collision = false;
                int snakelength = mSnakeTrail.size();
                for (int index = 0; index < snakelength; index++) {
                    if (mSnakeTrail.get(index).equals(newCoord)) {
                        collision = true;
                    }
                }
                // 如果出现两个苹果,保证苹果重复
                int applelength = mAppleList.size();
                for (int index = 0; index < applelength; index++) {
                    if (mAppleList.get(index).equals(newCoord)) {
                        collision = true;
                    }
                }
                found = !collision;
            }
            if (newCoord == null) {
                Log.e(TAG, "Somehow ended up with a null newCoord!");
            }
            mAppleList.add(newCoord);
        }
    
        // 更新信息
        public void update() {
            if (mMode == RUNNING) {
                long now = System.currentTimeMillis();
    
                if (now - mLastMove > mMoveDelay) {
                    clearTiles();
                    updateWalls();
                    updateSnake();
                    updateApples();
                    mLastMove = now;
                }
                mRedrawHandler.sleep(mMoveDelay);
            }
        }
    
        /**
         * 给mTileGrid赋值,标识墙
         */
        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);
            }
        }
    
        /**
         * 给mTileGrid赋值,标识苹果
         */
        private void updateApples() {
            for (Coordinate c : mAppleList) {
                setTile(YELLOW_STAR, c.x, c.y);
            }
        }
    
        /*
         * 更新蛇,因为蛇要吃苹果
         */
        private void updateSnake() {
            boolean growSnake = false;
    
            Coordinate head = mSnakeTrail.get(0);// 蛇头当前的位置
            Coordinate newHead = new Coordinate(1, 1);// 新蛇头,因为一会会为其赋值,所以此处可以不初始化
    
            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;
            }
            }
    
            // 检测蛇是否撞墙
            if ((newHead.x < 1) || (newHead.y < 1) || (newHead.x > mXTileCount - 2)
                    || (newHead.y > mYTileCount - 2)) {
                setMode(LOSE);
                return;
    
            }
    
            // 检测蛇是否撞自身
            int snakelength = mSnakeTrail.size();
            for (int snakeindex = 0; snakeindex < snakelength; snakeindex++) {
                Coordinate c = mSnakeTrail.get(snakeindex);
                if (c.equals(newHead)) {
                    setMode(LOSE);
                    return;
                }
            }
    
            // 吃苹果,自身涨一截,屏幕刷新变快,也就是速度变快
            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.99;
    
                    growSnake = true;// growSnake表示蛇是否迟到苹果,判定是否自身增长
                }
            }
            /*
             * 蛇移动的过程其实就是把蛇尾放到蛇头前,然后去掉蛇尾,这期间,如果蛇吃了苹果,则蛇自身增长,否则自身不增长
             */
            mSnakeTrail.add(0, newHead);
            // 如果蛇不需要增长,减去蛇尾即可,如果需要增长,则把增长放到蛇头,不减蛇尾
            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++;
            }
        }
    }

    /MySnake/src/edu/hhxy/android/snake/TileView.java

    package edu.hhxy.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:一个继承View的基类
     */
    public class TileView extends View {
        protected static int mTileSize;// 图片大小
        protected static int mXTileCount;// 总的列数
        protected static int mYTileCount;// 总的行数
        private static int mXOffset;// x边距
        private static int mYOffset;// y边距
        private Bitmap[] mTileArray;// 图片数组
    
        /**
         * 二维数组,根据其值用来确定是否要画苹果,以及要画什么颜色的苹果
         */
        private int[][] mTileGrid;// 二维数组
    
        private final Paint mPaint = new Paint();
    
        /*
         * 构造方法
         */
        public TileView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            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();
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            // w宽,h长
            mXTileCount = (int) Math.floor(w / mTileSize);
            mYTileCount = (int) Math.floor(h / mTileSize);
    
            mXOffset = ((w - (mTileSize * mXTileCount)) / 2);
            mYOffset = ((h - (mTileSize * mYTileCount)) / 2);
    
            mTileGrid = new int[mXTileCount][mYTileCount];
            clearTiles();
        }
    
        /**
         * 初始化
         */
    
        public void resetTiles(int tilecount) {
            mTileArray = new Bitmap[tilecount];
        }
    
        /**
         * 加载图片,使mTitleArray[]表示不同的图片
         */
        public void loadTile(int key, Drawable tile) {
            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;
        }
    
        /**
         * 初始化mTitleGrid[][]
         */
        public void clearTiles() {
            for (int x = 0; x < mXTileCount; x++) {
                for (int y = 0; y < mYTileCount; y++) {
                    setTile(0, x, y);
                }
            }
        }
    
        /**
         * 把某一位置的坐标置为tileindex,以确定mTitleGrid[][]处要画的图片
         */
        public void setTile(int tileindex, int x, int y) {
            mTileGrid[x][y] = tileindex;
        }
    
        @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);
                    }
                }
            }
        }
    }

    /MySnake/src/edu/hhxy/android/utils/Coordinate.java

    package edu.hhxy.android.utils;
    
    public class Coordinate {
    
        /**
         * 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.
         * 
         */
        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 + "]";
        }
    }


    /MySnake/src/edu/hhxy/android/utils/SnakeDataUtils.java

    package edu.hhxy.android.utils;
    
    import java.util.ArrayList;
    
    public class SnakeDataUtils {
        /**
         * 数据转换成以int [x1,y1,x2,y2,x3,y3...]的形式便于保存下蛇和苹果的坐标
         */
        public int[] coordArrayListToArray(ArrayList<Coordinate> 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;
        }
    
        /**
         * 数据从int [x1,y1,x2,y2,x3,y3...]恢复
         */
        public ArrayList<Coordinate> coordArrayToArrayList(int[] rawArray) {
            ArrayList<Coordinate> coordArrayList = new ArrayList<Coordinate>();
    
            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;
        }
    
    }
  • 相关阅读:
    AngularJS之ng-repeat指令
    AngularJS之ng-controller指令
    HttpClient通过Post上传文件
    response.setHeader()的用法
    response 设置响应头的常用几种方法
    理解HTTP session原理及应用
    mysqldump 利用rr隔离实现一致性备份
    Struts标签、Ognl表达式、el表达式、jstl标签库这四者之间的关系和各自作用
    AngularJS之ng-model指令
    AngularJS之ng-app指令
  • 原文地址:https://www.cnblogs.com/jianfengyun/p/3773714.html
Copyright © 2011-2022 走看看