Flutter三方库适配OpenHarmony【color_memory】颜色记忆游戏项目完整实战

前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

color_memory 是一个典型的记忆类小游戏项目。它内置 8 种颜色,系统每一关会向序列中追加一个随机颜色,并按顺序高亮展示给玩家;玩家需要在自己的回合按相同顺序点击颜色。如果玩家点击正确,就进入下一关并增加分数;如果点击错误,游戏结束并更新最高分。

这类小游戏很适合练习 Flutter 的 状态机设计、异步序列播放、GridView 网格布局、AnimatedContainer 动画反馈、GestureDetector 点击输入、分数统计和 OpenHarmony 触摸交互适配。虽然代码量不大,但它包含了一个完整游戏循环的关键结构。

在这里插入图片描述

图示说明:本文围绕 Flutter 工程中的 color_memory 项目展开,重点分析颜色序列生成、展示阶段、玩家输入校验、得分系统、游戏结束页面和 OpenHarmony 适配关注点。

记忆游戏的核心不是颜色方块本身,而是状态流转:系统展示序列、玩家复现序列、逐步校验输入、成功后进入下一关、失败后结束游戏。

本文将基于项目真实源码展开,核心内容包括:

  • ColorMemoryApp 的应用入口与紫色 Material 3 主题
  • _colors 如何定义 8 色游戏面板
  • _sequence_userSequence 如何保存系统序列和玩家输入
  • _showSequence() 如何异步播放高亮序列
  • _onColorTap() 如何逐步判断点击是否正确
  • _level_score_highScore 如何构成分数系统
  • _isShowingSequence_isUserTurn_isGameOver 如何组成状态机
  • 当前初始流程没有触发 _startGame() 的真实边界
  • OpenHarmony 适配时需要验证的网格、动画、触摸和图标能力

一、项目背景与目标

1.1 游戏定位

color_memory 的定位是一个颜色序列记忆游戏。它类似经典 Simon 记忆玩法:系统先展示一个颜色序列,玩家记住顺序后进行复现。每通过一关,序列长度增加 1,难度逐步提升。

从玩家视角看,完整游戏流程应该是:

  1. 开始游戏。
  2. 系统展示颜色序列。
  3. 玩家按顺序点击颜色。
  4. 点击正确则进入下一关。
  5. 点击错误则游戏结束。
  6. 玩家可以重新开始。

从工程视角看,它对应的是:

  1. 维护一个系统颜色序列。
  2. 每关追加一个随机颜色。
  3. 异步逐个高亮展示系统序列。
  4. 开放玩家输入。
  5. 每次点击后立即比较当前位置。
  6. 完整匹配后加分并升级。
  7. 匹配失败后更新游戏结束状态。

1.2 当前功能概览

功能 当前实现 技术点
应用入口 runApp(const ColorMemoryApp()) Flutter 启动流程
应用主题 紫色 Material 3 ColorScheme.fromSeed
颜色面板 8 种颜色 List<Color>
系统序列 _sequence 随机颜色列表
玩家输入 _userSequence 点击颜色列表
随机追加 math.Random() 生成下一关颜色
序列展示 _showSequence() 异步高亮
输入校验 _onColorTap() 逐位比较
得分 level * 10 关卡递增
最高分 _highScore 内存中保存
结束页 _buildGameOverView() Game Over 状态

1.3 适合学习的能力

这个项目适合学习:

  • 小游戏状态机
  • 随机序列生成
  • 异步流程控制
  • GridView.builder 网格布局
  • AnimatedContainer 颜色动画
  • GestureDetector 点击输入
  • 列表序列比较
  • 游戏分数和最高分管理

二、环境准备与工程结构

2.1 技术栈概览

项目使用 Flutter SDK 自带能力,不依赖复杂游戏引擎。

类别 当前使用 说明
开发语言 Dart Flutter 应用主语言
UI 框架 Flutter Material 页面、卡片、按钮、图标
随机数 dart:math 随机生成颜色
状态管理 StatefulWidget + setState 游戏状态刷新
网格布局 GridView.builder 颜色方块排列
动画组件 AnimatedContainer 展示阶段高亮过渡
点击输入 GestureDetector 玩家点击颜色
目标适配 Flutter / OpenHarmony UI、动画、触摸验证

2.2 pubspec 关键配置

工程配置如下:

environment:
  sdk: ^3.9.2

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.8

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^5.0.0

flutter:
  uses-material-design: true

这里需要关注:

  1. 当前项目没有接入音效、震动或游戏引擎。
  2. Material Icons 依赖 uses-material-design: true
  3. 游戏主要依靠 Flutter Framework 组件实现。

2.3 主源码结构

核心代码集中在 lib/main.dart

import 'package:flutter/material.dart';
import 'dart:math' as math;

void main() {
  runApp(const ColorMemoryApp());
}

主要结构如下:

结构 类型 作用
ColorMemoryApp StatelessWidget 应用根组件
ColorMemoryHomePage StatefulWidget 游戏首页
_ColorMemoryHomePageState State 管理序列、分数、状态

2.4 常用运行命令

完成 Flutter 环境准备后,可以执行:

flutter pub get
flutter analyze
flutter test
flutter run

OpenHarmony 环境运行时,需要结合本地 Flutter OpenHarmony 发行版、DevEco Studio、设备连接和签名配置。

三、应用入口与主题配置

3.1 main 函数

应用入口如下:

void main() {
  runApp(const ColorMemoryApp());
}

runApp 会把根组件挂载到 Flutter 渲染树。

3.2 ColorMemoryApp 根组件

根组件代码如下:

class ColorMemoryApp extends StatelessWidget {
  const ColorMemoryApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Color Memory',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
        useMaterial3: true,
      ),
      home: const ColorMemoryHomePage(title: 'Color Memory'),
    );
  }
}

它完成了:

  1. 设置应用标题为 Color Memory
  2. 使用紫色作为 Material 3 种子色。
  3. 将首页绑定为 ColorMemoryHomePage

3.3 紫色主题的意义

紫色常用于娱乐、益智和游戏类界面。项目中紫色主要体现在:

  • 根主题 seedColor。
  • Play Again 按钮。
  • AppBar 主题色。
  • 游戏整体氛围。

四、颜色面板与序列数据

4.1 颜色列表

游戏内置 8 种颜色:

final List<Color> _colors = [
  Colors.red,
  Colors.blue,
  Colors.green,
  Colors.yellow,
  Colors.purple,
  Colors.orange,
  Colors.pink,
  Colors.cyan,
];

这些颜色用于生成系统序列,也用于构建玩家可点击的颜色网格。

4.2 序列状态

系统序列和玩家输入分别保存:

List<Color> _sequence = [];
List<Color> _userSequence = [];

字段含义如下:

字段 作用
_sequence 系统生成的目标颜色序列
_userSequence 玩家当前回合已点击的颜色序列

4.3 为什么分开保存

分开保存有两个好处:

  1. 系统序列保持不变,作为正确答案。
  2. 玩家输入逐步增长,每次点击都能即时比较。

点击第 1 个颜色时只比较第 1 位;点击第 2 个颜色时再比较第 2 位。这样错误可以被立即发现,不需要等玩家点完整个序列。

五、游戏状态机设计

5.1 状态字段

项目使用多个字段表示游戏状态:

int _level = 1;
int _score = 0;
int _highScore = 0;
bool _isShowingSequence = false;
bool _isGameOver = false;
bool _isUserTurn = false;
int _currentIndex = 0;

字段含义如下:

字段 作用
_level 当前关卡
_score 当前分数
_highScore 最高分
_isShowingSequence 是否正在展示系统序列
_isGameOver 是否游戏结束
_isUserTurn 是否轮到玩家输入
_currentIndex 展示阶段当前高亮的序列位置

5.2 三种主状态

当前页面主要在三种状态之间切换:

状态 条件 页面
展示序列 _isShowingSequence == true _buildSequenceView()
玩家输入 非 game over 且非展示阶段 _buildColorGrid()
游戏结束 _isGameOver == true _buildGameOverView()

代码体现如下:

child: _isGameOver
    ? _buildGameOverView()
    : _isShowingSequence
        ? _buildSequenceView()
        : _buildColorGrid()

5.3 当前初始流程边界

当前源码中 _startGame() 没有在 initState() 中调用,也没有在初始页面提供 Start 按钮。初始状态为:

bool _isGameOver = false;
bool _isUserTurn = false;
bool _isShowingSequence = false;

因此首次打开页面时会显示颜色网格,但 _onColorTap() 会因为 _isUserTurnfalse 直接返回:

if (!_isUserTurn || _isGameOver) return;

这意味着首次进入页面无法真正开始游戏。这是当前源码最重要的真实边界。

六、启动游戏与随机追加

6.1 _startGame 方法

启动游戏方法如下:

void _startGame() {
  setState(() {
    _sequence = [];
    _userSequence = [];
    _level = 1;
    _score = 0;
    _isGameOver = false;
  });
  _addToSequence();
}

它会清空序列、重置关卡和分数,然后追加第一个颜色。

6.2 _addToSequence 方法

追加颜色逻辑如下:

void _addToSequence() {
  final random = math.Random();
  _sequence.add(_colors[random.nextInt(_colors.length)]);
  _showSequence();
}

它从 8 种颜色中随机选择一个追加到 _sequence,随后调用 _showSequence() 展示完整序列。

6.3 随机序列特点

当前随机逻辑允许连续出现相同颜色。例如序列可能是:

red, red, blue, green

这符合记忆游戏的常见规则,因为玩家不仅要记住颜色,还要记住每一步的位置。

6.4 启动流程修正思路

可以在 initState() 中启动游戏:


void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    _startGame();
  });
}

也可以增加一个初始 Start 按钮,由用户主动开始。两种方式都比初始网格不可点击更自然。

七、序列展示阶段

7.1 _showSequence 方法

展示序列的方法如下:

void _showSequence() async {
  setState(() {
    _isShowingSequence = true;
    _isUserTurn = false;
  });

  await Future.delayed(const Duration(seconds: 1));

  for (int i = 0; i < _sequence.length; i++) {
    if (!mounted) return;
    setState(() {
      _currentIndex = i;
    });
    await Future.delayed(const Duration(milliseconds: 600));
  }

  if (!mounted) return;
  setState(() {
    _isShowingSequence = false;
    _isUserTurn = true;
    _userSequence = [];
    _currentIndex = -1;
  });
}

这段代码实现了:

  1. 进入展示阶段。
  2. 禁止用户输入。
  3. 等待 1 秒。
  4. 按顺序高亮每个颜色 600ms。
  5. 展示结束后进入用户回合。
  6. 清空玩家输入序列。

7.2 mounted 判断

异步过程中多次使用:

if (!mounted) return;

这是必要的保护。如果页面已经销毁,就不能再调用 setState

7.3 展示阶段页面

展示阶段使用 _buildSequenceView()

Widget _buildSequenceView() {
  return GridView.builder(
    shrinkWrap: true,
    padding: const EdgeInsets.all(16),
    gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 4,
      crossAxisSpacing: 8,
      mainAxisSpacing: 8,
    ),
    itemCount: _colors.length,
    itemBuilder: (context, index) {
      final isHighlighted = _currentIndex >= 0 &&
          index == _colors.indexOf(_sequence[_currentIndex]);
      return AnimatedContainer(
        duration: const Duration(milliseconds: 200),
        decoration: BoxDecoration(
          color: isHighlighted
              ? _colors[index]
              : _colors[index].withValues(alpha: 0.3),
          borderRadius: BorderRadius.circular(12),
        ),
      );
    },
  );
}

7.4 AnimatedContainer 的作用

AnimatedContainer 会在颜色变化时自动过渡:

AnimatedContainer(
  duration: const Duration(milliseconds: 200),
)

展示阶段未高亮颜色使用透明度降低的版本,高亮颜色使用原色。

八、玩家输入与逐步校验

8.1 玩家点击入口

玩家点击颜色时调用:

onTap: () => _onColorTap(_colors[index])

每个颜色方块都由 GestureDetector 包裹。

8.2 输入前置判断

点击处理方法开头是:

if (!_isUserTurn || _isGameOver) return;

这确保:

  • 展示序列时不能点击。
  • 游戏结束后不能继续输入。
  • 只有玩家回合才处理点击。

8.3 添加玩家输入

玩家点击后,颜色会被加入 _userSequence

setState(() {
  _userSequence.add(color);
});

随后计算当前输入位置:

final currentIndex = _userSequence.length - 1;

8.4 即时比较

每次点击后立即比较当前位置:

if (_userSequence[currentIndex] != _sequence[currentIndex]) {
  _endGame();
  return;
}

如果不匹配,游戏立即结束。

8.5 完整匹配后升级

当玩家输入长度等于系统序列长度时,说明本关通过:

if (_userSequence.length == _sequence.length) {
  _score += _level * 10;
  setState(() {
    _level++;
    _userSequence = [];
  });
  Future.delayed(const Duration(seconds: 1), () {
    _addToSequence();
  });
}

通过后会加分、关卡加 1,并在 1 秒后追加新颜色进入下一关。

九、颜色网格与点击区域

9.1 玩家回合网格

玩家回合显示 _buildColorGrid()

Widget _buildColorGrid() {
  return GridView.builder(
    shrinkWrap: true,
    padding: const EdgeInsets.all(16),
    gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 4,
      crossAxisSpacing: 8,
      mainAxisSpacing: 8,
    ),
    itemCount: _colors.length,
    itemBuilder: (context, index) {
      return GestureDetector(
        onTap: () => _onColorTap(_colors[index]),
        child: Container(
          decoration: BoxDecoration(
            color: _colors[index],
            borderRadius: BorderRadius.circular(12),
            boxShadow: [
              BoxShadow(
                color: _colors[index].withValues(alpha: 0.5),
                blurRadius: 8,
                offset: const Offset(2, 2),
              ),
            ],
          ),
        ),
      );
    },
  );
}

9.2 4 列布局

网格设置为 4 列:

crossAxisCount: 4

8 种颜色会排列成 2 行 4 列,适合移动端屏幕展示。

9.3 方块视觉设计

每个方块包含:

  • 原始颜色。
  • 12 像素圆角。
  • 与自身颜色一致的阴影。
  • 8 像素横纵间距。

这让颜色按钮更有游戏感,也便于用户点击。

9.4 当前点击反馈边界

玩家点击颜色后,当前源码没有额外闪烁、音效或震动反馈。玩家只能通过后续状态变化判断是否正确。

如果要增强体验,可以在点击时增加短暂缩放、边框高亮、音效或震动。

十、分数与最高分

10.1 分数字段

分数相关字段如下:

int _level = 1;
int _score = 0;
int _highScore = 0;

顶部统计卡片展示 Level、Score 和 Best。

10.2 得分公式

每通过一关增加:

_score += _level * 10;

示例:

通过关卡 本关加分
1 10
2 20
3 30
4 40

10.3 最高分更新

游戏结束时更新最高分:

if (_score > _highScore) {
  _highScore = _score;
}

最高分只保存在内存中,应用重启后会恢复为 0。

10.4 统计卡片

统计卡片由 _buildStatCard() 复用:

Widget _buildStatCard(String label, String value) {
  return Card(
    child: Container(
      padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
      child: Column(
        children: [
          Text(label, style: TextStyle(color: Colors.grey.shade600)),
          const SizedBox(height: 4),
          Text(
            value,
            style: const TextStyle(
              fontSize: 24,
              fontWeight: FontWeight.bold,
            ),
          ),
        ],
      ),
    ),
  );
}

十一、游戏结束页面

11.1 _endGame 方法

游戏结束方法如下:

void _endGame() {
  setState(() {
    _isGameOver = true;
    _isUserTurn = false;
    if (_score > _highScore) {
      _highScore = _score;
    }
  });
}

它会进入 Game Over 状态,并关闭用户输入。

11.2 Game Over 视图

结束页面如下:

Widget _buildGameOverView() {
  return Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      const Icon(Icons.sentiment_dissatisfied, size: 80, color: Colors.red),
      const SizedBox(height: 16),
      const Text(
        'Game Over!',
        style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
      ),
      const SizedBox(height: 16),
      Text(
        'You reached level $_level',
        style: const TextStyle(fontSize: 18),
      ),
      Text(
        'Score: $_score',
        style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
      ),
    ],
  );
}

11.3 Play Again 按钮

游戏结束后底部显示:

ElevatedButton.icon(
  onPressed: _startGame,
  icon: const Icon(Icons.refresh),
  label: const Text('Play Again'),
)

点击后会重新开始游戏。

11.4 当前初始按钮缺失

Play Again 只在 _isGameOver 为 true 时显示。首次进入页面时 _isGameOver 为 false,因此不会显示这个按钮。

这也说明初始状态需要额外的开始入口,否则玩家第一次进入时无法触发 _startGame()

十二、OpenHarmony 适配要点

12.1 适配关注范围

color_memory 没有平台插件,适配重点在 Flutter UI、触摸、动画和异步流程。

适配项 涉及源码 验证重点
MaterialApp 根组件 应用启动和主题
AppBar 标题栏 颜色和文字
Card 统计卡片 布局、阴影
GridView 颜色网格 4 列布局
AnimatedContainer 高亮动画 颜色过渡
GestureDetector 玩家点击 触摸响应
Future.delayed 序列播放 异步节奏
ElevatedButton Play Again 按钮显示和点击
Material Icons 刷新和失败图标 字体资源

12.2 图标资源验证

项目使用了:

Icons.refresh
Icons.sentiment_dissatisfied

OpenHarmony 设备上需要验证:

  1. 图标是否可见。
  2. 图标大小是否符合预期。
  3. 图标颜色是否正确。
  4. Material Icons 字体是否随包体加载。

12.3 动画验证

展示阶段依赖 AnimatedContainer

AnimatedContainer(
  duration: const Duration(milliseconds: 200),
)

需要观察:

  • 颜色是否逐个高亮。
  • 未高亮颜色是否变浅。
  • 高亮过渡是否自然。
  • 长序列播放时是否卡顿。

12.4 触摸验证

玩家输入依赖 GestureDetector

GestureDetector(
  onTap: () => _onColorTap(_colors[index]),
)

需要验证:

  • 点击区域是否足够大。
  • 快速点击时状态是否稳定。
  • 展示阶段点击是否被忽略。
  • 游戏结束后点击是否被忽略。

十三、测试与验证

13.1 静态分析

建议执行:

flutter analyze

重点关注:

  • 初始状态是否能进入游戏流程。
  • 异步回调中是否检查 mounted
  • Future.delayed 后是否可能在已结束游戏状态下继续追加序列。
  • 高分是否只存在内存中。

13.2 组件测试方向

可以执行:

flutter test

适合覆盖的行为包括:

  1. 页面显示 Level、Score、Best。
  2. 初始状态是否有开始入口。
  3. 启动游戏后进入展示阶段。
  4. 展示结束后进入玩家回合。
  5. 错误点击进入 Game Over。
  6. Game Over 后显示 Play Again。

13.3 示例测试代码

下面是一段验证初始标题和统计卡片的测试思路:

testWidgets('shows color memory dashboard', (tester) async {
  await tester.pumpWidget(const ColorMemoryApp());

  expect(find.text('Color Memory'), findsOneWidget);
  expect(find.text('Level'), findsOneWidget);
  expect(find.text('Score'), findsOneWidget);
  expect(find.text('Best'), findsOneWidget);
});

这能确认页面基础结构正常。

13.4 启动流程测试思路

如果添加 Start 按钮,可以验证:

testWidgets('starts game from initial state', (tester) async {
  await tester.pumpWidget(const ColorMemoryApp());

  await tester.tap(find.text('Start Game'));
  await tester.pump();

  expect(find.text('Watch the sequence...'), findsOneWidget);
});

这类测试能避免初始页面不可交互的问题再次出现。

13.5 手动验证流程

手动验证可以按如下顺序进行:

  1. 启动应用,确认是否能触发游戏开始。
  2. 观察系统颜色序列是否逐个高亮。
  3. 展示阶段尝试点击颜色,确认不会误记录。
  4. 展示结束后按顺序点击颜色。
  5. 正确复现后确认 Level 和 Score 增加。
  6. 故意点错颜色,确认进入 Game Over。
  7. 点击 Play Again,确认游戏重置并重新开始。
  8. 连续玩多轮,确认 Best 会更新。

十四、常见问题与优化建议

14.1 为什么首次进入页面点击颜色没有反应

因为初始状态下 _isUserTurn 为 false,而 _startGame() 没有被触发:

if (!_isUserTurn || _isGameOver) return;

因此首次点击会被直接忽略。可以在初始页面增加 Start 按钮,或在页面加载后自动调用 _startGame()

14.2 为什么最高分重启后会清零

因为 _highScore 是内存状态:

int _highScore = 0;

应用重启后 State 会重新创建,最高分恢复为 0。要长期保存,需要接入本地存储。

14.3 为什么没有音效或震动反馈

当前源码只实现颜色高亮和页面状态切换,没有音效、震动或触觉反馈。记忆游戏如果增加音效,会更接近经典玩法,但也需要额外适配平台能力。

14.4 随机颜色可以连续重复吗

可以。当前随机逻辑没有排除上一次颜色:

_colors[random.nextInt(_colors.length)]

连续重复颜色是合法序列,玩家需要记住每一步位置。

14.5 是否需要游戏引擎

当前项目是轻量 UI 游戏,不需要 Flame 或其他游戏引擎。使用 Flutter 组件就能完成网格、动画和点击输入。如果后续加入复杂音效、粒子、排行榜和关卡系统,再考虑更专业的游戏架构。

十五、工程扩展方向

15.1 增加 Start Game 初始入口

可以增加一个初始状态:

bool _hasStarted = false;

首次页面显示 Start 按钮:

if (!_hasStarted)
  ElevatedButton(
    onPressed: () {
      setState(() {
        _hasStarted = true;
      });
      _startGame();
    },
    child: const Text('Start Game'),
  )

这样流程更完整。

15.2 持久化最高分

可以把最高分保存到本地:

Future<void> saveHighScore(int score) async {
  // save score locally
}

读取时在页面初始化阶段恢复:

Future<void> loadHighScore() async {
  // load score locally
}

这样玩家重启应用后仍能看到历史最高分。

15.3 增加点击反馈

可以在玩家点击时增加视觉反馈:

setState(() {
  _currentIndex = _colors.indexOf(color);
});

再短暂延迟后恢复。也可以加入缩放动画或边框高亮。

15.4 增加音效和震动

记忆游戏常见扩展包括:

  • 每种颜色对应不同音效。
  • 点击正确时轻微震动。
  • 点击错误时错误音效。
  • 通关时播放成功反馈。

这些能力需要额外平台适配。

15.5 增加难度设置

可以让玩家选择展示速度:

final Duration _showDuration = const Duration(milliseconds: 600);

后续可以设置为 Easy、Normal、Hard:

难度 展示时长
Easy 800ms
Normal 600ms
Hard 400ms

十六、完整核心代码回顾

16.1 应用入口

void main() {
  runApp(const ColorMemoryApp());
}

入口负责启动根组件。

16.2 根组件

class ColorMemoryApp extends StatelessWidget {
  const ColorMemoryApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Color Memory',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
        useMaterial3: true,
      ),
      home: const ColorMemoryHomePage(title: 'Color Memory'),
    );
  }
}

根组件负责主题和首页绑定。

16.3 随机追加颜色

void _addToSequence() {
  final random = math.Random();
  _sequence.add(_colors[random.nextInt(_colors.length)]);
  _showSequence();
}

每一关都会追加一个随机颜色。

16.4 展示序列

for (int i = 0; i < _sequence.length; i++) {
  if (!mounted) return;
  setState(() {
    _currentIndex = i;
  });
  await Future.delayed(const Duration(milliseconds: 600));
}

这段代码按顺序高亮系统序列。

16.5 校验点击

if (_userSequence[currentIndex] != _sequence[currentIndex]) {
  _endGame();
  return;
}

每次点击都会立即比较当前位置。

16.6 游戏结束

void _endGame() {
  setState(() {
    _isGameOver = true;
    _isUserTurn = false;
    if (_score > _highScore) {
      _highScore = _score;
    }
  });
}

游戏结束时会更新最高分并关闭玩家输入。

总结

color_memory 用 Flutter 组件实现了一个完整的颜色记忆游戏核心:它通过 _colors 提供 8 色面板,通过 _sequence 保存系统序列,通过 _userSequence 保存玩家输入,通过 _showSequence() 异步展示高亮,通过 _onColorTap() 逐位校验点击,并通过 _level_score_highScore 构建关卡和分数系统。

从 OpenHarmony 适配角度看,这个项目覆盖了 Material 主题、统计卡片、GridView 网格、AnimatedContainer 动画、GestureDetector 触摸、Future.delayed 异步流程和 Material Icons 等基础能力,很适合验证 Flutter 小游戏页面在 OpenHarmony 上的表现。

当前源码也有几个真实边界:首次进入页面没有触发 _startGame(),导致初始颜色网格不可交互;最高分只保存在内存中;玩家点击没有额外音效或震动;随机序列没有难度设置。这些边界不影响它作为状态机和小游戏入门案例使用,但在继续工程化时需要优先完善启动入口。

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!


相关资源:

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐