一、先说数字
2025 年 12 月底,GoGBA 上架 App Store 和 Google Play。
到今天大半年,月活最高 8.3k,目前稳定在 7.4k。
一分钱付费广告都没投过。
这个数字不大,但它有一个特点:全部是自己找上来的人。这半年我最常在评论里看到的一句话是 ------ best gba emulator ever。
一个人做的东西,被 7400 个陌生人每个月打开一次。这件事值得写下来。
二、我为什么只做 GBA
先讲私心。
学生时代手里那台 GBA,装着超级机器人大战、火焰纹章、宝可梦、恶魔城、塞尔达。周末因为它变得值得期待。
后来 NDS、3DS、PSP、PSVita、Switch 一代代出来,GBA 完成了它的历史使命。PC 上最早的模拟器叫 VisualBoyAdvance。
再后来手机变强了,手游也起来了。但我越来越不想玩网游 ------ 又氪金,又要追别人的进度,慢慢就偏离了「享受游戏」这件事本身。画面越来越精致,游戏性却越来越像财务模型。
而我能玩游戏的时间,反而越来越碎:只有夜深人静,或者出差在飞机、火车上的那几个小时,才能真正心无旁骛地掏出手机。
GBA、NDS、PSP 的模拟器我都玩过。最后发现我最爱的还是 GBA,四个理由,全是实用主义的:
- 复古像素风 ------ 审美偏好,不解释。
- 不耗电 ------ 一趟长途飞机,GBA 游戏能撑到落地。
- 单屏 ------ 剩下的屏幕空间正好放虚拟按键。NDS 双屏在手机上永远是妥协。
- 同人改版的天堂 ------ GBA ROM hack 生态是所有掌机里最繁荣的。
第 3 点尤其关键:GBA 是唯一一个「手机形态天生适配」的掌机。这不是情怀,这是产品判断。
三、别人的模拟器差在哪
独乐乐不如众乐乐 ------ 一定有和我一样念旧的人。
所以我把双端主流的都用了一遍:iOS 上 Delta、Linkboy;Android 上 Pizzaboy、Linkboy、Lemuroid。Lemuroid 是开源的,市面上不少模拟器基于它。
结论是:每一个都好用,但每一个都有让我不爽的小瑕疵。
唯一双端都有的是 Linkboy(前身 MyBoy)。但它的配置项太专业了,复杂度仅次于 RetroArch。
这里就是空位所在 ------
别人在解决「能不能跑」和「能不能调到极致」。 没人在解决「拿起来就能玩」。
我想做的,是配置简单、双端一致、拿起来就爽的那一个。
四、Dogfooding 是唯一的产品方法论
我是 Flutter GDE,也是一个 40 人跨端团队的 Tech Lead。GoGBA 是我对自己多年移动端经验的一次单人验证。
方法论只有一条:我自己用到没有一处别扭,用户就会喜欢。
这听起来像偷懒,其实是最狠的标准。因为我是重度用户,我的忍耐阈值比普通用户低得多。别人觉得「还行」的地方,我会觉得不能忍。
举个例子。虚拟按键的透明度 ------ 大部分模拟器给你一个全局滑块。但我玩的时候发现:竖屏和横屏我想要的透明度根本不一样。竖屏按键在屏幕下方空白区,我希望它清楚;横屏按键压在画面上,我希望它淡。
于是 GoGBA 的按键透明度是按屏幕方向分别持久化的。这个需求没有任何用户提过,是我自己玩出来的。
再举一个。震动反馈。大多数模拟器一个开关管全部。但我玩下来发现:震动应该给离散动作(A/B 键),不该给高频连续输入(方向键) ------ 方向键持续震动只会让手麻。所以 D-Pad 震动是独立开关。
这类判断,只有真的天天玩的人才做得出来。
五、「AI 写的,有什么技术含量?」
外行的印象是:内核是现成的 mGBA,你不就是让 AI 做个前端,设计一下、测一测就上线了?
AI 时代确实让这件事容易了很多。但「容易很多」不等于「门槛低」。
我想用三类具体的东西来说明门槛在哪。
5.1 真正的门槛:判断 AI 有没有在乱修
我在自己 fork 的 mGBA 上修了 3 个 bug:
| 问题 | 影响 |
|---|---|
| MBC6 存档空指针崩溃 | 部分 MBC6 卡带游戏直接崩溃 |
| GB MBC RTC 状态丢失 | Pokémon Prism 等依赖实时时钟的游戏时间错乱 |
| 宝可梦 ROM 识别 off-by-one | 识别边界错误导致误判 |
这三个补丁是 AI 写的。我要说清楚这件事,因为不说就是重复我下面那一节要讲的错。
但这恰恰是我想讲的重点 ------
让 AI 改一份二十年历史的 C 代码库,最大的风险不是它改不出来,是它改得「看起来很对」。
它会给你一个编译通过、跑起来 bug 也消失了的 patch。而这个 patch 可能是:
- 在错误的层级加了个空指针判断,把崩溃变成静默的存档损坏 ------ 用户几十小时后才发现
- 幻觉出一个 mGBA 里根本不存在的结构体字段或函数,但恰好因为宏展开而编译通过
- 只针对我手上那一个 ROM 生效,换个 MBC6 卡带就废了
- 把边界条件从 off-by-one 改成反方向的 off-by-one,而我的测试用例正好没覆盖到新的那一边
所以我的实际工作不是「写补丁」,是审补丁:这个空指针到底该在哪一层拦?RTC 状态丢的是保存路径还是恢复路径?这个字段在 mGBA 的原始设计里是什么语义、谁负责它的生命周期?off-by-one 的正确边界,要回去读 GBA 卡带头的规范才能确认,不能听 AI 说。
AI 把「写代码」的成本降到接近零,于是「判断代码对不对」变成了唯一的门槛。而这件事没法外包给 AI ------ 你不能让产生幻觉的东西自己检查自己有没有幻觉。
这也是为什么我说门槛没降低,只是移位了:从「能不能写出来」移到了「能不能识别出那个看起来很对但其实是错的答案」。后者需要的知识量一点不少,而且它不可伪造 ------ 你要么知道 GBA 卡带头的布局,要么不知道。
补一句必要的澄清:这三个修复只在 GoGBA 自己的 fork 里,没有合回 mGBA 上游。 所以它们是 GoGBA 独家的行为差异,不是对社区的贡献 ------ 别人的 mGBA 里没有这些补丁,遇到同样的 MBC6 崩溃还是会崩。
5.2 双端像素级一致,是最贵的那种一致
很多人说 Flutter 性能比不上原生。这句话对,也不对。
GoGBA 的分层是:
scss
Flutter (Dart) ← 全部 UI、状态、配置、导航
↓ MethodChannel / EventChannel
Kotlin / Swift ← 渲染、音频、输入、存档 IO
↓ JNI / C FFI
libretro mGBA (C/C++) ← 模拟本体
画面渲染走原生 (Android OpenGL ES 3.0 / iOS Metal,帧缓冲直接贴到 Texture,零拷贝)。Flutter 一帧模拟逻辑都不跑。
但整个 UX ------ 24 种语言、设置层级、按键布局编辑器、付费墙、云存档 ------ 双端通过 Flutter 做到像素级一致。
技术栈没有好坏,只有用得对不对。 把性能敏感的部分放原生,把一致性敏感的部分放 Flutter,这个边界怎么切,才是真正的工作。
顺带说,原生层的坑一点没少。举两个真在生产环境咬过我的:
- EGL teardown race ------ Mali GPU 上退出游戏偶发 SIGABRT,destroyed mutex。根因是对
EGL_DEFAULT_DISPLAY调了eglTerminate,而它是进程级共享的。 - release-only 空栈崩溃 ------ 双击退出被触发了两次,把导航栈 pop 空,
matches.last崩。debug 下复现不出来。
这两个 bug 和 5.1 的性质不同,但同样棘手:它们在 AI 能看到的环境里根本不存在。 一个只在 Mali GPU 上偶发,一个只在 release 构建下复现 ------ 没有堆栈、没有稳定复现步骤,只有线上 Crashlytics 里一条 SIGABRT。你没法把它贴给 AI 然后等答案,因为「问题是什么」本身就是要先解出来的那一步。
5.3 加功能容易,「杂而不乱」难
上架后这半年,我提交了 1499 次,其中 276 个 feat、224 个 fix。
加了很多东西:宝可梦工具箱(图鉴 / 属性相克表 / 性格表 / 招式,离线 SQLite,24 语言)、成就离线缓存与待同步状态、按键布局编辑器(可隐藏单个按键)、手势快速存档(滑动解锁式交互)、自适应屏幕方向(菜单竖屏 / 游戏横屏)、云存档(iCloud + Google Drive)、金手指、AI 画面翻译、甚至一个每日小贴士。
每加一个,都要回答同一个问题:它会不会让「拿起来就能玩」变差?
所以我极度克制。GoGBA 是少数完全没有广告的模拟器 ------ 不是因为清高,是因为广告和「无打扰」这个核心体验直接冲突,没得谈。
真正难的从来不是加功能,是在功能变多的同时,让第一次打开的人依然三秒钟就知道该点哪里。
六、我犯的那个错
我曾经宣传「三天做出一款模拟器」。
这句话是错的。而且错得不是措辞,是概念 ------ 我混淆了「模拟器」和「模拟器前端」。
- 模拟器(emulator)是 mGBA。是别人二十年的心血,是精确到周期的硬件行为复刻。
- 模拟器前端(frontend)是 GoGBA。是 UI、输入、渲染管线、存档管理、平台集成。
我三天做出来的是后者的雏形。把它说成前者,是对 mGBA 所有贡献者的不尊重。
后果是 RetroAchievements 社区被我惹怒了,取消了 GoGBA 硬核模式的认证。
我想诚实地说清楚两件事:
第一,从技术要求上,GoGBA 是满足硬核合规的。 硬核模式禁止金手指、即时存读档、快进倒带、内存修改。GoGBA 把这些收敛到同一个判定(RA 已开启 ∧ 有已认证会话 ∧ 生效硬核),然后在每一个入口独立复查一遍 :金手指不下发;游戏内即时存档菜单项直接不渲染;手动槽位、自动存档/自动恢复、以及免费的手势快存槽,各自在自己的路径上再查一次 ------ 存和读双向都拦。免费不等于放行,那个不收费的手势槽照样拦。倒带和逐帧根本没做。客户端 User-Agent 按 RA 规范上报内核名与版本。包括「公开发行至少半年」这条时间线要求,现在也已经满足了。
第二,这不重要。 因为认证从来不只是技术检查表,它还包括社区信任。我用一句不准确的宣传,损害了后者。技术合规能靠写代码补上,信任只能靠时间。
什么时候被允许重新申请,我不知道。这个决定权不在我手上。
写下来是因为:如果有人也想做模拟器前端,请从第一天就把这两个词分清楚。这是行业礼节,不是文字游戏。
七、被改回五星的那些评论
每次看到五星评价,是我成就感最高的时候。所以我专门做了一个页面来放它们。
但让我更在意的是另一种:原本不是五星的评论,在我解决了那个用户的问题之后,被改回五星。
前者是「你做得好」,后者是「你在听」。
对一个人做的产品来说,第二个才是护城河。7400 个人留下来,不是因为我功能最多,而是因为提了问题会被解决。一个人的响应速度能快到什么程度,是独立开发唯一拿得出手的结构性优势 ------ 也是唯一不需要资源就能兑现的那个。
八、接下来
GoGBA 现在在 App Store 和 Google Play 都能搜到。
这篇一半是给自己的复盘,一半是想让更多和我一样念旧的人知道有这么个东西 ------ 酒香也怕巷子深。
半年这个节点没什么好总结的。7.4k 这个数字每天都在动,评论区每天都有新问题,RetroAchievements 那边什么时候愿意重新看一眼也不由我决定。能确定的只有下一版要修什么。
如果你也是那种只有深夜或者出差路上才有时间玩游戏、不想再追任何人进度的人,那 GoGBA 大概是为你做的。因为它就是为我做的。