大概两个多月前,我用 Flutter 做了一个叫 SkyTank 的战机大战小游戏,目标是同一套代码跑通 Android、iOS 和 HarmonyOS 6.0 三个端。做完之后最大的感受是:Flutter 做跨端小游戏这条路完全走得通,但跟用 Unity 或 Cocos 做游戏完全是两套思路,坑也不少。这篇文章就把整个项目的选型、设计、编码和上架环节里踩过的坑、验证过的方法都写出来,给想用 Flutter 做小游戏、尤其是想兼容鸿蒙生态的朋友一个可以跟着落地的参考。
我假定你至少有 Flutter 基础,知道 Widget、State、pubspec 这些概念,但不需要你有游戏开发经验。整个项目用到的关键技术点包括:Flame 轻量游戏引擎、Flutter 与鸿蒙原生通道的交互、sqflite 本地数据库与后端同步、以及多端构建时的环境配置问题。下面直接进入正题,按我实际开发的顺序来聊。
1. 为什么小游戏项目我会选择 Flutter 而不是 Unity 或 Cocos
1.1 从需求反推引擎选型
先说结论:如果你的目标平台明确包含 HarmonyOS 6.0,并且游戏玩法偏向 2D 轻度休闲,Flutter 是一个性价比极高的选择。我当时立项时的需求其实很清晰:需要一套代码覆盖安卓、iOS 和鸿蒙,游戏体量不需要 3D 特效,玩法是经典的纵版战机射击,交互以触控拖拽为主。
Unity 和 Cocos 在游戏生态上确实成熟,但有个现实问题:它们对接鸿蒙的流程需要额外的导出工具链。Unity 要装 Huawei 的插件,Cocos Creator 也需要对着鸿蒙工程手动配置一堆东西。而如果用 Flutter,官方和社区已经证明了 Flutter 引擎可以在鸿蒙上跑通,再加上鸿蒙原生工程的集成方式也就是标准的 ohos 工程导入依赖,整个链路会顺畅很多。
另外,我这个小游戏本质上是一个“带游戏玩法的 App”:它需要登录、支付、排行榜、本地存档这些非游戏功能。这类业务功能如果写在 Unity 里,说实话,做 UI 和市政服务的效率远不如 Flutter。Flutter 的强项是业务界面和交互,Flame 引擎恰好补足了游戏循环和组件管理的能力,两者结合很适合做“轻游戏 + 重运营”的产品。
1.2 Flutter 做轻量级小游戏的渲染路径与优势
很多人有个误解,觉得 Flutter 不是游戏引擎,做不了游戏。其实 Skia/Impeller 渲染引擎处理 2D 精灵图完全不在话下,关键看你用什么方式组织游戏循环。
最原始的做法是直接用 Flutter 的 AnimationController 驱动游戏循环,每帧 setState 更新游戏状态,再用 CustomPaint 或 Stack + Positioned 来决定绘制。这个方案能做出来,但代码会很乱,碰撞检测、对象生命周期、音效调度都要自己造轮子。
我最终引入了一个叫 Flame 的轻量级 2D 游戏引擎,它的核心价值不是给你提供图形渲染能力,而是帮你把游戏循环、组件树、碰撞检测、输入事件这些“游戏通用底层”封装好。Flame 的 GameWidget 可以当作一个普通 Widget 嵌入 Flutter 的 Widget 树里,这就意味着游戏页面和业务页面可以在同一个 Navigator 下无缝切换。
对比一下传统方案:
| 对比维度 | Unity/Cocos | Flutter + Flame |
|---|---|---|
| 鸿蒙适配链路 | 需要额外插件与工程转换 | 直接走 Flutter 鸿蒙引擎,集成路径短 |
| 业务 UI 开发效率 | 一般,UGUI/预制体偏游戏向 | 高,Flutter 本身就是 UI 框架 |
| 2D 轻量玩法开发效率 | 高但学习成本大 | 够用,上手极快 |
| 包体大小 | 相对较大 | 比 Unity 小很多 |
| 社区资源 | 游戏资料多 | 混合模式下需要自己查 |
我选择 Flutter 还有一个重要原因:团队成员对 Dart 的熟悉程度远高于 C# 和 Lua。技术选型永远要综合考虑团队技能树,而不是只看引擎本身。
1.3 Flame 游戏引擎引入与架构分层
Flame 的集成非常简单,在 pubspec.yaml 里加依赖就行。我当时用的版本是 1.10 左右,现在应该有新版,接口大体兼容。
yaml复制dependencies:
flutter:
sdk: flutter
flame: ^1.10.0
sqflite: ^2.3.0
path: ^1.8.0
dio: ^5.3.0
provider: ^6.1.0
shared_preferences: ^2.2.0
我的项目结构长这样:
code复制lib/
main.dart # 入口,初始化数据库和全局状态
game/
sky_tank_game.dart # 游戏主类,继承 FlameGame
components/
tank_player.dart # 玩家战机组件
enemy_spawner.dart # 敌机生成器
bullet.dart # 子弹组件
explosion.dart # 爆炸特效组件
physics/
collision.dart # 碰撞检测逻辑
pages/
home_page.dart # 主菜单
game_page.dart # 游戏页面,嵌入 GameWidget
rank_page.dart # 排行榜页面
services/
storage_service.dart # 本地数据库封装
sync_service.dart # 后端同步封装
native/
harmony_bridge.dart # 鸿蒙原生通道封装
这个分层的好处是:Flutter 层完全不知道游戏内部用了 Flame,只知道 GameWidget 能打印游戏分数、暂停游戏;同理,Flame 层也不关心数据是存在本地还是
