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 跨平台社区,获取更多技术支持和资源:

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

相关推荐
zl_vslam2 小时前
SLAM中的非线性优-3D图优化之绝对位姿SE3约束SO3/t形式(十八)
人工智能·算法·计算机视觉·3d
啊阿狸不会拉杆2 小时前
《计算机操作系统》 - 第九章 操作系统接口
人工智能·算法·计算机组成原理·os·计算机操作系统
鸣弦artha2 小时前
Flutter框架跨平台鸿蒙开发——GridView基础入门
flutter·华为·harmonyos
刃神太酷啦2 小时前
Linux 基础 IO 收官:库的构建与使用、进程地址空间及核心知识点全解----《Hello Linux!》(11)
java·linux·c语言·数据库·c++·算法·php
板面华仔2 小时前
机器学习入门(一)——KNN算法
人工智能·算法·机器学习
血色橄榄枝2 小时前
07 复盘一阶段掌握知识要点
flutter·开源·鸿蒙
子春一2 小时前
构建一个实时双向温度转换器:深入解析 Flutter 中的输入联动与状态控制
flutter
Three~stone2 小时前
Matlab R2024b 保姆级安装教程(附:解决win10问题)
开发语言·算法·matlab
ytttr8732 小时前
基于MATLAB的一维对流扩散方程数值求解
开发语言·算法·matlab