Flutter for OpenHarmony 实战:碰撞检测算法与游戏结束处理

Flutter for OpenHarmony 实战:碰撞检测算法与游戏结束处理

文章目录

  • [Flutter for OpenHarmony 实战:碰撞检测算法与游戏结束处理](#Flutter for OpenHarmony 实战:碰撞检测算法与游戏结束处理)

一、前言

碰撞检测是游戏开发中的核心机制。在贪吃蛇游戏中,需要检测两种碰撞:撞墙(边界碰撞)和撞自己(自身碰撞)。本文将详细讲解这两种碰撞的检测算法、游戏结束处理流程以及状态管理。

二、碰撞检测概述

贪吃蛇游戏中需要检测的碰撞类型:

2.1 墙壁碰撞

  • 蛇头超出网格边界
  • x坐标 < 0 或 x ≥ 30
  • y坐标 < 0 或 y ≥ 20

2.2 自身碰撞

  • 蛇头与蛇身任意一节坐标重合
  • 不包括蛇头本身(索引0)

2.3 检测时机

每次蛇移动后,在插入新头部之前检测:

dart 复制代码
void _update() {
  Point newHead = _getNewHead();  // 计算新头部位置

  if (_checkCollision(newHead)) {  // 碰撞检测
    _gameOver();
    return;
  }

  snake.insert(0, newHead);  // 无碰撞才插入
}

三、墙壁碰撞检测

3.1 边界判断算法

dart 复制代码
bool _checkCollision(Point point) {
  // 检查墙壁碰撞
  if (point.x < 0 || point.x >= gridWidth ||
      point.y < 0 || point.y >= gridHeight) {
    return true;
  }

  // 检查自身碰撞(稍后讲解)
  // ...

  return false;
}

判断条件分析:

条件 含义 示例
point.x < 0 超出左边界 x=-1
point.x >= gridWidth 超出右边界 x=30
point.y < 0 超出上边界 y=-1
point.y >= gridHeight 超出下边界 y=20

为什么使用>=而不是>?

  • 有效坐标范围:0到gridWidth-1(即0到29)
  • gridWidth=30时,x=30已经超出边界
  • 所以判断条件是 x >= 30

3.2 坐标越界示例

dart 复制代码
// 示例1:蛇头在右边缘,向右移动
Point head = Point(29, 10);  // 最右边
direction = Direction.right;
Point newHead = Point(30, 10);  // 越界!
_checkCollision(newHead);  // 返回true

// 示例2:蛇头在上边缘,向上移动
Point head = Point(15, 0);   // 最上边
direction = Direction.up;
Point newHead = Point(15, -1);  // 越界!
_checkCollision(newHead);  // 返回true

3.3 代码实现

完整的墙壁碰撞检测:

dart 复制代码
bool _checkCollision(Point point) {
  // 墙壁碰撞检测
  if (point.x < 0 || point.x >= gridWidth ||
      point.y < 0 || point.y >= gridHeight) {
    return true;
  }

  // 自身碰撞检测
  for (var segment in snake) {
    if (segment.x == point.x && segment.y == point.y) {
      return true;
    }
  }

  return false;
}

四、自身碰撞检测

4.1 蛇身遍历算法

dart 复制代码
// 自身碰撞检测
for (var segment in snake) {
  if (segment.x == point.x && segment.y == segment.y) {
    return true;  // 发现碰撞
  }
}

算法说明:

  1. 遍历snake列表中的每一节
  2. 将新头部坐标与每节坐标比较
  3. 如果x和y都相等,说明碰撞
  4. 返回true表示碰撞

4.2 头部与身体比较

重要: 蛇头移动到的新位置,可能与蛇身的任意一节重合。

dart 复制代码
// 示例:蛇身坐标
snake = [
  Point(5, 10),  // 索引0:当前头部
  Point(4, 10),  // 索引1:第二节
  Point(3, 10),  // 索引2:第三节
];

// 蛇向右移动,新头部
Point newHead = Point(6, 10);

// 检测:newHead与snake[0/1/2]比较
// (6,10) vs (5,10) → 不同
// (6,10) vs (4,10) → 不同
// (6,10) vs (3,10) → 不同
// 结果:无碰撞

碰撞示例:

dart 复制代码
// 蛇绕成一圈
snake = [
  Point(5, 10),
  Point(5, 11),
  Point(5, 12),
  Point(6, 12),
  Point(7, 12),
];

// 蛇头向上移动
Point newHead = Point(5, 9);
// 然后向右、向下移动...
Point newHead = Point(7, 11);
// 最终撞到自己的身体:(7,11) 与 (7,12) 相邻但y不同

4.3 时间复杂度分析

dart 复制代码
for (var segment in snake) {  // O(n)
  if (segment.x == point.x && segment.y == segment.y) {
    return true;
  }
}

时间复杂度:O(n)

  • n = snake.length(蛇的长度)
  • 最坏情况:遍历整个蛇身
  • 最好情况:第一节就碰撞(不太可能,因为蛇头不会撞到当前头部位置)

优化思路:

  • 蛇较短时(<100节),O(n)可接受
  • 蛇很长时,可以用HashSet存储身体坐标,O(1)查询
  • 但贪吃蛇游戏中蛇一般不会太长,O(n)足够

五、碰撞响应机制

5.1 游戏结束状态设置

dart 复制代码
void _gameOver() {
  isGameOver = true;
  gameTimer?.cancel();
  setState(() {});
}

状态变化:

  • isGameOver:false → true
  • 停止定时器
  • 更新UI显示游戏结束信息

5.2 定时器清理流程

dart 复制代码
void _gameOver() {
  isGameOver = true;          // 标记游戏结束
  gameTimer?.cancel();         // 取消定时器
  setState(() {});             // 触发UI更新
}

为什么必须取消定时器?

  • 定时器会持续触发_update()
  • 如果不取消,会不断检测碰撞
  • 导致_gameOver()被重复调用
  • 浪费CPU资源

定时器清理时机:

  1. 游戏结束时:gameTimer?.cancel();
  2. 速度变化时:先cancel再重新创建
  3. 组件销毁时:dispose中cancel

5.3 UI状态更新

dart 复制代码
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      children: [
        Expanded(
          child: CustomPaint(
            painter: GamePainter(...),
          ),
        ),
        // 游戏结束时显示
        if (isGameOver)
          Container(
            child: Column(
              children: [
                Text('游戏结束!'),
                Text('最终得分: $score'),
                ElevatedButton(
                  onPressed: () => _initGame(),
                  child: Text('重新开始'),
                ),
              ],
            ),
          ),
      ],
    ),
  );
}

UI变化:

  • 游戏区域:定格在最后状态
  • 游戏结束面板:从隐藏变为显示
  • 控制按钮:隐藏(isGameOver为true时不显示)

六、完整代码实现

dart 复制代码
class _GameHomePageState extends State<GameHomePage> {
  // ...

  bool _checkCollision(Point point) {
    // 墙壁碰撞检测
    if (point.x < 0 || point.x >= gridWidth ||
        point.y < 0 || point.y >= gridHeight) {
      return true;
    }

    // 自身碰撞检测
    for (var segment in snake) {
      if (segment.x == point.x && segment.y == point.y) {
        return true;
      }
    }

    return false;
  }

  void _gameOver() {
    isGameOver = true;
    gameTimer?.cancel();
    setState(() {});
  }

  void _update() {
    if (nextDirection != null) {
      direction = nextDirection!;
    }

    Point newHead = _getNewHead();

    // 碰撞检测
    if (_checkCollision(newHead)) {
      _gameOver();
      return;
    }

    snake.insert(0, newHead);

    if (newHead.x == food!.x && newHead.y == food!.y) {
      score += 10;
      if (speed > 80) {
        speed -= 5;
        gameTimer?.cancel();
        _startGame();
      }
      _spawnFood();
    } else {
      snake.removeLast();
    }

    setState(() {});
  }
}

七、测试用例

测试1:墙壁碰撞

dart 复制代码
// 边界测试
test('右边界碰撞', () {
  snake = [Point(29, 10)];
  direction = Direction.right;
  Point newHead = Point(30, 10);
  expect(_checkCollision(newHead), true);
});

test('上边界碰撞', () {
  snake = [Point(15, 0)];
  direction = Direction.up;
  Point newHead = Point(15, -1);
  expect(_checkCollision(newHead), true);
});

测试2:自身碰撞

dart 复制代码
test('撞到身体', () {
  snake = [
    Point(5, 10),
    Point(4, 10),
    Point(3, 10),
  ];
  // 蛇头移动到(3,10),与第三节重合
  Point newHead = Point(3, 10);
  expect(_checkCollision(newHead), true);
});

八、总结

本文讲解了碰撞检测算法:

  1. 墙壁碰撞:检测坐标是否超出0-29和0-19范围
  2. 自身碰撞:遍历蛇身,比较坐标
  3. 游戏结束:设置标志、取消定时器、更新UI
  4. 时间复杂度:O(n),n为蛇身长度

关键要点:

  • 碰撞检测在移动前执行,防止无效移动
  • 定时器必须清理,避免资源浪费
  • 使用isGameOver标志控制UI状态

下篇预告:《Flutter for OpenHarmony 实战:食物生成算法与难度递增系统》

社区支持

欢迎加入开源 OpenHarmony 跨平台社区,获取更多技术支持和资源:

如果本文对您有帮助,欢迎点赞、收藏和评论。您的支持是我持续创作的动力!

相关推荐
炽烈小老头2 分钟前
【每天学习一点算法 2026/05/20】省份数量
学习·算法
乐迪信息3 分钟前
乐迪信息:港口夜间船舶巡查难,AI摄像机法全天候监测
人工智能·物联网·算法·计算机视觉·目标跟踪
sali-tec4 分钟前
C# 基于OpenCv的视觉工作流-章74-线-线距离
图像处理·人工智能·opencv·算法·计算机视觉
故事和你918 分钟前
洛谷-【图论2-3】最小生成树1
开发语言·数据结构·c++·算法·动态规划·图论
故事和你9110 分钟前
洛谷-【图论2-3】最小生成树2
开发语言·数据结构·c++·算法·动态规划·图论
guygg8811 分钟前
贝叶斯非局部均值降噪算法C语言实现
c语言·算法·均值算法
量子炒饭大师12 分钟前
【优化算法】滑动窗口的「义体化」重构 ——【滑动窗口】何为滑动窗口?滑动窗口算法的核心目的是什么?
c++·算法·重构·优化算法·双指针·滑动窗口
玖釉-14 分钟前
C++ 中的 buckets 详解:从哈希桶到 unordered_map 底层原理
算法·哈希算法·散列表
计算机安禾14 分钟前
【c++面向对象编程】第35篇:构造函数与异常:如何避免资源泄露?
开发语言·javascript·c++·算法·性能优化
z2005093015 分钟前
今日算法(二叉树剪枝)
数据结构·c++·算法·剪枝