/**
* <p>Title: Snake</p>
* <p>Copyright: (C) 2007 The Android Open Source Project. Licensed under the Apache License, Version 2.0 (the "License")</p>
* @author Gavin 标注
*/
package com.deaboway.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 = "Deaboway";
/**
* 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;
// 游戏中蛇的前进方向,默认值北方
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;
/**
* mLastMove: tracks the absolute time when the snake last moved, and is
* used to determine if a move should be made based on mMoveDelay.
*/
// 最后一次移动时的毫秒时刻
private long mLastMove;
/**
* mStatusText: text shows to the user in some run states
*/
// 显示游戏状态的文本组件
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<Coordinate> mSnakeTrail = new ArrayList<Coordinate>();
// 苹果数组(数组以坐标对象为元素)
private ArrayList<Coordinate> mAppleList = new ArrayList<Coordinate>();
/**
* Everyone needs a little randomness in their life
*/
// 随机数
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.
*/
// 创建一个Refresh Handler来产生动画: 通过sleep()来实现
private RefreshHandler mRedrawHandler = new RefreshHandler();
// 一个Handler
class RefreshHandler extends Handler {
// 处理消息队列
@Override
public void handleMessage(Message msg) {
// 更新View对象
SnakeView.this.update();
// 强制重绘
SnakeView.this.invalidate();
}
// 延迟发送消息
public void sleep(long delayMillis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), delayMillis);
}
};
/**
* Constructs a SnakeView based on inflation from XML
*
* @param context
* @param attrs
*/
// 构造函数
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);
// 把三种图片存到Bitmap对象数组
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() {
// 清空ArrayList列表
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;
// 2个随机位置的苹果
addRandomApple();
addRandomApple();
// 移动延迟
mMoveDelay = 600;
// 初始得分0
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...]
*/
// 坐标数组转整数数组,把Coordinate对象的x y放到一个int数组中——用来保存状态
private 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;
}
/**
* 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
*
* @param rawArray
* : [x1,y1,x2,y2,...]
* @return a ArrayList of Coordinates
*/
// 整数数组转坐标数组,把一个int数组中的x y放到Coordinate对象数组中——用来恢复状态
private 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;
}
/**
* Restore game state if our process is being relaunched
*
* @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.
*
* (non-Javadoc)