先看看效果图
贪吃蛇设计思路
贪吃蛇分为 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);
- }
基本的游戏逻辑已经完成
来源: https://juejin.im/post/5a7d463f5188257a5d2b27aa