上篇一图流:四个问题与触发点
一、先说结论
如果有一款用仓颉(一门编程语言)写成、已在鸿蒙上跑的应用,想搬到 iOS 和 Android,这篇讲我们先试的一条路、撞上的问题,以及后来为什么改走 CJMP(一套跨平台开发框架)。我们搬的是自己的棋盘游戏《驿路巡点》,具体的活由一组分工不同的 Grok Bot 来做。
9 月 26 日,我们先走「仓颉 1.1.3 交叉编译(在一台机器上编出另一个平台能运行的程序)+ 原生壳(iOS、Android 各套一层很薄的原生应用外壳)」,只接通了 1 关。9 月 27 日,团队里负责查资料、做对比的那个 Grok Bot 去查了一次 CJMP 的引擎源码,发现画棋盘要用的 Canvas(画布接口)在 iOS/Android 的构建里根本就没编进去。9 月 28 日起,我们转向 CJMP。
这不是"哪个框架更好"的横向评测,更像一个明确目标下的连续决策:把《驿路巡点》搬到 iOS 和 Android,能跑通多少、卡住的地方算谁的。本文只讲前两步------为什么先不走 CJMP,为什么后来又走了。这是我目前的做法,可能不是最优解。
二、游戏先说清楚,不然看不懂后面
《驿路巡点》是个仓颉写的棋盘游戏。规则一句话:从标了 1 的格子起笔,按上下左右拖过相邻格子,每个格子必须经过恰好一次,最大数字是整盘终点;拖回上一格可以撤销。驿站的顺序和墙的挡法见图 2。规则对齐 LinkedIn Zip。
图 2|《驿路巡点》的规则示意

项目规模:100 关(编号 0--99),6×6 的 30 关、7×7 的 70 关,分 10 章。技术上用仓颉 1.1.3,界面用仓颉 ArkUI(鸿蒙的界面框架)里的 Canvas(画布接口,kit.ArkUI 提供),触摸用 onTouch。代码里 CanvasRenderingContext2D 出现 4 处(棋盘、首页朱印路线、撤销图标、重绘图标各一个 Canvas),Path2D(画路径的接口)出现 11 处。
三、第一版:直接套原生壳,没用 CJMP
3.1 为什么第一版没用 CJMP
第一版没用 CJMP,原因很简单:当时我只说了要适配一下 iOS 和 Android,Grok Bot 们没往 CJMP 上想,直接给 iOS 和 Android 各写了一个原生壳,两个壳共用同一套游戏引擎代码。后来是我想试试 CJMP,才转了过去。
3.2 只接了一关:选哪一关,做到了什么
第一版只接了 1 关:编号 1 的「涿郡·范阳」。
选它的理由写在 iOS 目录的 README(项目说明文件)里:编号 0 的「涿郡·涿」是教程盘,但没有墙;范阳是涿郡里最早一盘既有驿站又有墙、而且没有奇偶边、只用基本规则的盘。
iOS 模拟器和 Android 模拟器(在电脑上模拟手机的软件)都能走到这一关的 6×6 棋盘。Android 壳那次改动的说明里写,Android 模拟器进入了涿郡·范阳,6×6 棋盘可见;iOS 壳那次改动的最后一条提交信息是「docs: record iOS simulator smoke passed」。
9 月 27 日同一天的三平台测试对这两个壳的描述很克制:目前只是用来做引擎冒烟测试(先跑一遍最基本的流程,看有没有明显出错)的壳,在范阳这一关上拖线、撤销、拖回、重绘、墙、通关判定都实测通过,其他基本都没有。
3.3 四个问题,和这一版的成绩
四个问题的现象、原因和做法都在下面的图里,这里只补图里没写的三点。第一个问题里,关卡数据文件 level.json 是 613 字节,正好走到字符串长度 32 及以上才会走的 memcpy_s 分支。第二个问题崩溃时的地址是 0x8,调用栈是 CJ_MCC_WriteRefField → PatrolGame.reset → yilu_load_level_json。第四个问题里,每个 .so 的 .init_array 只把运行时入队,第二次调用 MRT_CjRuntimeInit 会直接 fatal。
图 3|第一版遇到的四个问题

真正的问题是:这一版只跑通了 1 关,而且它是「壳」------9 月 27 日项目的测试规定当天加了一条:只能启动、只显示一关的壳,只能标「脚手架」(临时搭的架子,不算成品),不能写「通过」或「PASS」。
换句话说,第一版证明了壳能把引擎跑起来,还没证明整款游戏能在上面跑。
四、转向 CJMP
4.1 触发点,和我们用的版本
9 月 27 日核对 Canvas 在 iOS 和 Android 上能不能用,结果是:Canvas 在 iOS/Android 用的那份引擎构建里没有编进去。依据是引擎仓库里 cj_frontend 目录下的 BUILD.gn(构建配置文件),第 101 行原文(0.7.0 的发布分支和 CJMP 主分支上都在这一行):
# "interfaces/cj_ffi/cj_canvas_ffi.cpp", # [TODO] support pixelMap
图 4|鸿蒙与 iOS/Android 的两个模板

走的是 OpenSDK(CJMP 的开发包)0.2.2 公开版(发布于 2026-05-09),内置编译器 cjc 1.1.0(仓颉编译器;项目其余部分锁定 1.1.3)。CJMP 主分支最新有文档的版本是 v0.7.0(2026-09-09),我们落后 5 个版本。
4.2 时间线
日期和合并先后见图 5。图里没写的:做机关数据和求解器(自动判断一关有没有解的脚本)的那批改动是 10 月 1 日开始的;另有一次合并,把基座(应用的基础部分:关卡数据、字体、图片和页面导航)、棋盘和机关引擎一起并入主线(我们自己仓库的主分支)。求解器层面 100 关全通,详见下篇。
图 5|时间线(SGT)

改用 CJMP 是我想试试才提的。
五、CJMP 文档和源码的说法 vs 我们的实际
| 项 | CJMP 文档和源码写的 | 我们实际用的 / 碰到的 | | --- | --- | --- | | 它是什么 | 「CJMP 旨在构建支持 UI+逻辑 的高性能跨平台开发框架」 | 用它把鸿蒙版迁到 iOS 和 Android | | UI API 状态 | 仓颉 UI API 标 Beta,源码注释写「The Cangjie API is in Beta」 | 用的就是这套 Beta API | | Xcode | 0.2.2 发布说明要求 Xcode 16.0+,推荐 26.3 及以下,26.4 有已知兼容性问题 | 本地电脑装的是 Xcode 27,在支持范围之外。影响:在 iOS 27 模拟器上直接跑我们构建的 iOS 包,会启动即崩溃(崩溃信息指向缺少场景生命周期配置;94 关通关用的是 iOS 18.0 模拟器,没有遇到),10 月 3 日的补充验证改用了改过版本标记的测试专用包。这个崩溃的修复(iOS 外壳改用 UIScene 生命周期)已在 10 月 5 日合进主线;本文的 94 关测试是在这次修复合进主线之前做的,不含它。仓颉工具链链接时,默认 SDK 也要换成 26 版 | | 文档 | CJMP 主分支的文档要求先通过华为开发者网站上的权限申请 | OpenSDK 拿到手后没有 CJ-UI 的 API 文档 | | 平台 | 开发环境 Windows 10+、macOS 14+;没有 Linux 版 SDK | 所有 CJMP 构建都在 Mac 上做;Android 只产 arm64 |
CJMP 项目在 gitcode 的仓库:OpenSDK,Docs。
六、这篇没写的
后面两篇写转向之后遇到的问题:
-
用仓颉跨端做游戏(中):用 CJMP 搬游戏时碰到的三个缺口,和我们的绕法
-
用仓颉跨端做游戏(下):94 关能过、6 关卡住,问题最后出在我们自己这边
两条边界,先放在这篇:所有验证都在模拟器上,没有真机;相关的几批改动是 10 月 2 日合进主线的,本文的结论按当时主线上的代码写。
关卡数据怎么转换、怎么被求解器和测试校验,也就是数据管线,我们放在下篇讲。
这些取舍可能不是最优解,如果你有不同的看法或更好的路子,欢迎在评论区讨论。 Bot