先看看效果圖
貪吃蛇設計思路
貪吃蛇分為3個物件:
-
蛇
-
食物
-
舞臺
舞臺我們可以看作為一個二維陣列 蛇和食物 都是陣列中的元素
蛇是一串陣列中的連續的元素 分為蛇的頭元素和蛇身長度
食物可以看作是陣列中的一個元素
蛇的移動
蛇可以向上,向下,向左,向右移動
蛇移動 頭元素+1 尾元素-1
碰撞檢測
當蛇的頭部元素碰撞到食物 則吃掉食物 蛇長度+1。 如果碰撞到蛇身 遊戲結束,到舞臺邊界 直接穿過去
隨機生成食物
使用Random
生成食物座標 x,和y 生成後判斷是不是在蛇身座標上,如果是重新生成。
程式碼實現
首先我們使用自定義View
來實現
public class SnakePanelView extends View {
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪製介面...
}
}複製程式碼
然後我們定義格子元素
public class GridSquare {
private int mType;//元素型別
public GridSquare(int type) {
mType = type;
}
public int getColor() {
switch (mType) {
case GameType.GRID://空格子
return Color.WHITE;
case GameType.FOOD://食物
return Color.BLUE;
case GameType.SNAKE://蛇
return Color.parseColor("#FF4081");
}
return Color.WHITE;
}
public void setType(int type) {
mType = type;
}
}
複製程式碼
這個就是格子物件
然後我們定義出的二維陣列舞臺定義如下:
private List> mGridSquare = new ArrayList<>();
複製程式碼
定義出了舞臺陣列 我們需要定義食物的位置和蛇的位置。所以我們需要定義一個物件
這個物件需要兩個成員變數 x
和 y
來標示在舞臺中的位置
public class GridPosition {
private int x;
private int y;
public GridPosition(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
複製程式碼
然後我們需要定義蛇頭位置、蛇身List
和食物的位置
private List mSnakePositions = new ArrayList<>();
private GridPosition mSnakeHeader;//蛇頭部位置
private GridPosition mFoodPosition;//食物的位置
複製程式碼
然後我們需要定義蛇的一系列屬性:速度、蛇身長度和運動方向。
private int mSnakeLength = 3;//蛇身長度
private long mSpeed = 8;//執行速度 值越大 速度越快
private int mSnakeDirection = GameType.RIGHT;//執行方向
複製程式碼
讓蛇運動起來我們需要一個遊戲主迴圈 我們可以簡單定義一個迴圈
private class GameMainThread extends Thread {
@Override
public void run() {
while (true) {
//todo...
}
}
}
複製程式碼
我們在迴圈裡處理蛇移動、碰撞檢測和食物生產等操作。
處理蛇的移動方法:
private void moveSnake(int snakeDirection) {
switch (snakeDirection) {
case GameType.LEFT:
if (mSnakeHeader.getX() - 1 < 0) {//邊界判斷:如果到了最左邊 讓他穿過螢幕到最右邊 mGridSize 為舞臺大小
mSnakeHeader.setX(mGridSize - 1);
} else {
mSnakeHeader.setX(mSnakeHeader.getX() - 1);
}
mSnakePositions.add(new GridPosition(mSnakeHeader.getX(), mSnakeHeader.getY()));
break;
case GameType.TOP:
if (mSnakeHeader.getY() - 1 < 0) {
mSnakeHeader.setY(mGridSize - 1);
} else {
mSnakeHeader.setY(mSnakeHeader.getY() - 1);
}
mSnakePositions.add(new GridPosition(mSnakeHeader.getX(), mSnakeHeader.getY()));
break;
case GameType.RIGHT:
if (mSnakeHeader.getX() + 1 >= mGridSize) {
mSnakeHeader.setX(0);
} else {
mSnakeHeader.setX(mSnakeHeader.getX() + 1);
}
mSnakePositions.add(new GridPosition(mSnakeHeader.getX(), mSnakeHeader.getY()));
break;
case GameType.BOTTOM:
if (mSnakeHeader.getY() + 1 >= mGridSize) {
mSnakeHeader.setY(0);
} else {
mSnakeHeader.setY(mSnakeHeader.getY() + 1);
}
mSnakePositions.add(new GridPosition(mSnakeHeader.getX(), mSnakeHeader.getY()));
break;
}
}
複製程式碼
處理碰撞:
//檢測碰撞
private void checkCollision() {
//檢測是否咬到自己
GridPosition headerPosition = mSnakePositions.get(mSnakePositions.size() - 1);
for (int i = 0; i < mSnakePositions.size() - 2; i++) {
GridPosition position = mSnakePositions.get(i);
if (headerPosition.getX() == position.getX() && headerPosition.getY() == position.getY()) {
//咬到自己 停止遊戲
mIsEndGame = true;
showMessageDialog();
return;
}
}
//判斷是否吃到食物
if (headerPosition.getX() == mFoodPosition.getX()
&& headerPosition.getY() == mFoodPosition.getY()) {
mSnakeLength++;
generateFood();
}
}
複製程式碼
隨機生成食物:
private void generateFood() {
Random random = new Random();
int foodX = random.nextInt(mGridSize - 1);
int foodY = random.nextInt(mGridSize - 1);
for (int i = 0; i < mSnakePositions.size() - 1; i++) {
if (foodX == mSnakePositions.get(i).getX() && foodY == mSnakePositions.get(i).getY()) {
//不能生成在蛇身上
foodX = random.nextInt(mGridSize - 1);
foodY = random.nextInt(mGridSize - 1);
//重新迴圈
i = 0;
}
}
mFoodPosition.setX(foodX);
mFoodPosition.setY(foodY);
refreshFood(mFoodPosition);
}
複製程式碼
基本的遊戲邏輯已經完成。
工程已經放在GITHUb