返回

基于Flutter的像素级贪吃蛇

前端

贪吃蛇,作为经典的街机游戏,凭借其简单的规则和令人上瘾的游戏玩法,几十年来一直深受玩家的喜爱。现在,我们可以利用Flutter的强大功能,在移动设备上构建一个像素级贪吃蛇游戏,让玩家在手机上也能重温经典。

游戏规则

贪吃蛇游戏的规则非常简单:玩家控制一条贪吃蛇在网格中移动,贪吃蛇每移动一步,就会在身后留下一个身体块。贪吃蛇的目标是吃掉屏幕上出现的食物,每吃掉一个食物,贪吃蛇的身体就会增长一个块。玩家需要避免让贪吃蛇撞到自己身体或网格的边界,否则游戏就会结束。

游戏实现

为了使用Flutter构建贪吃蛇游戏,我们需要遵循以下步骤:

  1. 创建一个新的Flutter项目,并在项目目录中创建一个名为“lib”的文件夹。
  2. 在“lib”文件夹中创建一个名为“main.dart”的文件,并添加以下代码:
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SnakeGame(),
    );
  }
}

class SnakeGame extends StatefulWidget {
  @override
  _SnakeGameState createState() => _SnakeGameState();
}

class _SnakeGameState extends State<SnakeGame> {
  // 游戏变量
  List<SnakeBody> snakeBody = [];
  Food food;
  Direction direction = Direction.right;

  // 游戏初始化
  @override
  void initState() {
    super.initState();

    // 初始化贪吃蛇身体
    snakeBody.add(SnakeBody(x: 5, y: 5));
    snakeBody.add(SnakeBody(x: 4, y: 5));
    snakeBody.add(SnakeBody(x: 3, y: 5));

    // 初始化食物
    food = Food(x: 10, y: 10);

    // 开始游戏循环
    Timer.periodic(Duration(milliseconds: 100), (timer) {
      // 更新贪吃蛇的身体
      updateSnakeBody();

      // 检查贪吃蛇是否吃到食物
      if (snakeBody[0].x == food.x && snakeBody[0].y == food.y) {
        // 吃到食物后,增长贪吃蛇的身体并生成新的食物
        growSnakeBody();
        generateFood();
      }

      // 检查贪吃蛇是否撞到自己身体或网格边界
      if (isGameOver()) {
        // 游戏结束,停止游戏循环
        timer.cancel();
      }
    });
  }

  // 更新贪吃蛇的身体
  void updateSnakeBody() {
    // 将旧的身体块移动到新的位置
    for (int i = snakeBody.length - 1; i > 0; i--) {
      snakeBody[i].x = snakeBody[i - 1].x;
      snakeBody[i].y = snakeBody[i - 1].y;
    }

    // 根据方向移动贪吃蛇的头
    switch (direction) {
      case Direction.up:
        snakeBody[0].y--;
        break;
      case Direction.down:
        snakeBody[0].y++;
        break;
      case Direction.left:
        snakeBody[0].x--;
        break;
      case Direction.right:
        snakeBody[0].x++;
        break;
    }
  }

  // 增长贪吃蛇的身体
  void growSnakeBody() {
    // 在贪吃蛇的身体末尾添加一个新的身体块
    snakeBody.add(SnakeBody(x: snakeBody[snakeBody.length - 1].x, y: snakeBody[snakeBody.length - 1].y));
  }

  // 生成新的食物
  void generateFood() {
    // 在网格中随机生成一个新的食物位置
    food = Food(x: Random().nextInt(15), y: Random().nextInt(15));
  }

  // 检查贪吃蛇是否撞到自己身体或网格边界
  bool isGameOver() {
    // 检查贪吃蛇是否撞到自己身体
    for (int i = 1; i < snakeBody.length; i++) {
      if (snakeBody[0].x == snakeBody[i].x && snakeBody[0].y == snakeBody[i].y) {
        return true;
      }
    }

    // 检查贪吃蛇是否撞到网格边界
    if (snakeBody[0].x < 0 || snakeBody[0].x > 14 || snakeBody[0].y < 0 || snakeBody[0].y > 14) {
      return true;
    }

    return false;
  }

  // 渲染游戏界面
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Center(
        child: Container(
          width: 300,
          height: 300,
          child: CustomPaint(
            painter: SnakePainter(snakeBody, food),
          ),
        ),
      ),
    );
  }
}

// 贪吃蛇身体类
class SnakeBody {
  int x;
  int y;

  SnakeBody({required this.x, required this.y});
}

// 食物类
class Food {
  int x;
  int y;

  Food({required this.x, required this.y});
}

// 贪吃蛇画笔类
class SnakePainter extends CustomPainter {
  List<SnakeBody> snakeBody;
  Food food;

  SnakePainter(this.snakeBody, this.food);

  @override
  void paint(Canvas canvas, Size size) {
    // 绘制贪吃蛇的身体
    for (SnakeBody body in snakeBody) {
      canvas.drawRect(
        Rect.fromLTWH(
          body.x * 10,
          body.y * 10,
          10,
          10,
        ),
        Paint()..color = Colors.white,
      );
    }

    // 绘制食物
    canvas.drawRect(
      Rect.fromLTWH(
        food.x * 10,
        food.y * 10,
        10,
        10,
      ),
      Paint()..color = Colors.red,
    );
  }

  @override
  bool shouldRepaint(SnakePainter oldDelegate) {
    return true;
  }
}

// 方向枚举
enum Direction {
  up,
  down,
  left,
  right,
}
  1. 在“lib”文件夹中创建一个名为“snake_painter.dart”的文件,并添加以下代码:
import 'package:flutter/material.dart';

class SnakePainter extends CustomPainter {
  List<SnakeBody> snakeBody;
  Food food;

  SnakePainter(this.snakeBody, this.food);

  @override
  void paint(Canvas canvas, Size size) {
    // 绘制贪吃蛇的身体
    for (SnakeBody body in snakeBody) {
      canvas.drawRect(
        Rect.fromLTWH(
          body.x * 10,
          body.y * 10,
          10,
          10,
        ),
        Paint()..color = Colors.white,
      );
    }

    // 绘制食物
    canvas.drawRect(
      Rect.fromLTWH(
        food.x * 10,
        food.y * 10,
        10,
        10,
      ),
      Paint()..color = Colors.red,
    );
  }

  @override
  bool shouldRepaint(SnakePainter oldDelegate) {
    return true;
  }
}

// 贪吃蛇身体类
class SnakeBody {
  int x;
  int y;

  SnakeBody({required this.x, required this.y});
}

// 食物类
class Food {
  int x;
  int y;

  Food({required this.x, required this.y});
}
  1. 在“lib”文件夹中创建一个名为“snake_body.dart”的文件,并添加以下代码:
// 贪吃蛇身体类
class SnakeBody {
  int x;
  int y