本文是snake on a phone 贪吃蛇游戏的项目解析系列文章之5,基于google sample code 改编而成项目链接接上一篇文章手势操作的Snake游戏_游戏视图分析之SnakeView
上一节我们对SnakeView框架进行了分析,本节对游戏逻辑细节进行解析,也就是对update中的具体细节分析。
public void update() {
if (mMode == RUNNING) {
long now = System.currentTimeMillis();
if (now - mLastMove > mMoveDelay) {
clearTiles();//所有网格归零
updateWalls();//赋值墙壁 绿色标示
updateSnake();//更新蛇的坐标
updateApples();//更新苹果的坐标
mLastMove = now;//记录最后一次运动时间
}
mRedrawHandler.sleep(mMoveDelay);
}
}
主要逻辑在updateSnake和updateApples中。
private ArrayList mSnakeTrail = new ArrayList();
private ArrayList mAppleList = new ArrayList();
mSnakeTrail表示蛇身的坐标集合,从头到尾。
mAppleList所有出现过的苹果的坐标,也就是被蛇吃过的位置。
游戏开始的时候我们对他们进行初始化
private void initNewGame() {
mSnakeTrail.clear();
mAppleList.clear();
//生成如下坐标构成的蛇,下一个方向向上
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;
}
更新蛇的时候,需要进行碰撞检测,一是和周围墙壁是否碰撞,二是和自身是否接触,然后再看是否吃到苹果,根据结果决定是否添加苹果,以及是否去掉蛇尾,以下代码在保持源示例代码的基础上加了自己的一些注释。
private void updateSnake() {
boolean growSnake = false;
// grab the snake by the head
//找到头部
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;
}
}
// 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
//判断是否吃到苹果,吃到苹果则在苹果列表中移除对应苹果坐标,并随即生成添加一个新的苹果
//吃掉苹果的设定growSnake标志true,在更新蛇的时候会用到
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
//如果growSnake不是true,说明没有吃到苹果,蛇身长度一定,加头则要去尾
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++;
}
}
由于在updateSnake中对苹果坐标集合mAppleList已经进行了数值更新,这里只需进行绘制即可。
private void updateApples() {
for (Coordinate c : mAppleList) {
setTile(YELLOW_STAR, c.x, c.y);
}
}
updateSnake中涉及到随机添加一个苹果坐标的情况(游戏初始化的时候也会随机添加两个苹果),利用了随机函数来生成。
private void addRandomApple() {
Coordinate newCoord = null;
boolean found = false;
while (!found) {
// Choose a new location for our apple
//随机生成坐标,必须在墙内 nextInt表示[0,n) newx-> [1,mXTileCount-1)
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);
}
nextInt(a,b)函数生成的数值范围[a,b),所以newx-> [1,mXTileCount-1),坐标必须是墙内坐标。这里利用穷举的方式,随机生成一个苹果,判断是否和蛇重叠,直到找到合理的坐标为止。
不清楚贪吃蛇游戏最后能够玩到什么程度,如果在addRandomApple中真的一个都找不到的话,蛇长应该等于x*y,有兴趣的可以去分析一下是什么场景,我们的update函数中对于刷新是有一个时间参数的,而且随着游戏进行,速度加快,游戏难度也加大。