(2026-10-06更新)
初步移植好游戏后,我粗略看了开头场景感觉没问题就以为万事大吉。结果今天认真上手玩了之后发现:人物立绘哪儿去了?怎么只有背景图!
火急火燎地打开PC版一看,确认是移植版出现了问题:
(PC版场景人物都是动态立绘,身体会摇动、会眨眼睛、说话会动嘴巴)
(移植版背景图正常,但什么立绘都看不到;这里出现的色块是debug引入的,原版没有)
因为Codebuddy免费额度用光,这回换上QoderCN的免费Qwen-3.8-Flash。Qoder官方本来说9月30号就截止Qwen-3.8-Flash免费调用活动,结果到了10月又延期,且没明说截止日期。本着不用白不用的心态,迅速下好然后破限使用 ![]()
Qwen-3.8-Flash排查了好几轮,确认不是引擎脚本的问题,而是当前使用手机的Mali GPU不支持硬件解码立绘。具体来说:
立绘
.psb里内嵌的是 BC7 / DXT5 压缩贴图,而这台机器的 GPU(天玑 9400+ / Mali-G925-Immortalis)的 GLES 驱动只支持 ASTC 和 ETC1,没有s3tc/bptc/rgtc。
官方安卓libartemis.so又不做任何软解,背景/CG 是普通 PNG,引擎自己解码后上传 RGBA 照常显示,这一条误导AI,导致浪费了大量排查时间。
排除掉的错误方向
game.os/trueos组合、ex.progressos、
vsync 首帧强制推进、禁用emote.cacheinit()的 windows 预加载分支、
createEmoteLayer progress=true、以及怀疑 so 不是 emote 版(md5 比对确认是)。
为什么不换引擎
有个开源的 artemis-compat(GPL-3)实测对象正是本作,
且把 BC7/DXT5 做了 CPU 软解,能显示立绘图。但它的头发/胸部惯性摇动不模拟
(setHairScale/setBustScale 只存值 + 打日志),而本作重度依赖这套物理。
换过去就是个不会摇的半成品,对不起移植折腾这么久的工夫,不可接受 ![]()
方案评估
用工具把真实图集解出来统计:
立绘图集里alpha==0 的像素占 62%,所以 PSB 的
RL游程压缩收益极大——
RGBA8 + RL实测 1.05 B/px(体积几乎不变还保 8 位精度),
RGBA4444 + RL只有 0.24 B/px。但两个前提都不成立:本作是spec="win"图集布局,
compress字段只存在于krkr布局的 icon 上,官方资产里图集从来没用过它;
而且 pf8 包级零压缩(4141 个条目的存储间隙全为 0),打包时也压不回去。
裸转RGBA4444则是 2448 MB → 4.78 GB,alpha 还只剩 16 级。
最终方案:不碰资源,在 GL 边界拦截。
APK 带一个 64 KB 源码的
libarkhook.so,在Application.onCreate抢跑,手写dl_iterate_phdr解析并覆写libartemis.so的 GOT 槽(这些 GL 入口都是 UND 动态符号,走自己的 PLT),把 BC7/DXT5 在 CPU 上解成 RGBA8 再上传。包体积一点不涨、psb/pfs 一字未改。同时伪造glGetString(GL_EXTENSIONS)的返回值,把s3tc/dxt1/bptc三个名字追加进去,引擎开始调用
性能优化
直接看方案对比:
| 4096×4096 那张 | 渲染线程实际阻塞 | |
|---|---|---|
| 直接软解 | 294 ms | 294 ms(丢 18 帧) |
| 多核并行 | 61.5 ms | 95 ms(解码 61.5 + 上传 33.6) |
后台解码 + glClear 前 pump |
61.5 ms | 0.72 ms |
- 并行:BC7 的 4×4 block 彼此独立,按 block 行用原子计数器抢任务就行,
7 个 worker + 渲染线程自己 = 4.8 倍。输出缓冲从malloc换成
mmap + MADV_HUGEPAGE,因为 67 MB 首次触碰要上万次 4 KB page fault。 - 异步:动手前先量了「两次
glClear之间最多创建几张压缩纹理」,
实测最多 2 张、15 秒内 2400 次 clear,所以拿glClear当上传 pump 点
最多只延迟一帧。这一步让渲染线程从 95 ms 掉到 0.72 ms。
异步有三个不踩不知道的坑:
-
- 源数据必须自己拷一份(调用返回后引擎那块内存随时可能被复用,后台再读就花屏);
-
- 提交时要先传一个 1×1 全透明像素占位,
否则纹理不完整、采样会得到不透明黑,角色位置变黑块;
- 提交时要先传一个 1×1 全透明像素占位,
-
- pump 时必须临时切回提交时那个纹理对象,因为那时引擎早就绑了别的纹理。
这么一番折腾之后,总算是能愉快地玩耍了:
原文分割线
用 dsv4.1f 向安卓端移植了PC 版《アマカノ3》(约 8 GB),原生安卓手机运行,非 Winlator 兼容层,拿引擎官方的 Android 运行库自己改包数据。
(注:这个游戏官方存在安卓版,我仔细搜了一下也看到了分享。但考虑到我是第一次尝试安卓移植,积累经验比单纯拥有安卓版游戏更重要,所以最后还是进行了自主移植尝试)
工具:DeepSeek-V4.1-Flash+Codebuddy(免费额度)。脚本、逆向、调试由AI来做,我负责决策、真机验证。
文中没有带任何游戏文件,因为我的小水管上传只有2MB/s,完整文件有8GB多,上传太慢了
成果
- 手机:vivo V2507A(天玑 9400+ / Android 16 / arm64-v8a / 12GB 内存)
- 状态:标题、剧情、立绘、对话框全部正常,底部按钮栏 + 顶部工具条完整。(顶部工具条是PC版就有的原生大按钮,在触摸屏上直接就能用,功能完全,非常舒适,不用费劲去按下面的小按钮。)
- 顺便把官方 1.0.2 升级补丁打了上去(存档页从 15 槽变 25 槽、多了「返回上一选项」按钮)
APP图标使用官方海报截取女主头像,deepseek进行适配,效果很好,最终使用B方案:
主要流程
先侦察手机规格
用 Termux(免 root)把硬件信息全采了一遍:CPU、GPU、ABI、内存、存储、屏幕、可用工具链。
关键结论:
ro.product.cpu.abilist只有arm64-v8a—— 纯 64 位设备,跑不了任何 32 位原生库。- 有 OpenGL ES 3.x / Vulkan,12GB 内存,存储够。
参数看不懂没关系
先摸清机器规格,让AI定方案就行了
判定引擎
打开游戏目录,看到 Amakano3.pfs 的扩展名后缀就知道引擎了:Artemis Engine(iMel 系的引擎)。
目前主要的 Galgame 移植现状:
| 引擎 | 安卓方案 | 体验 |
|---|---|---|
| KiriKiri / KAG | Kirikiroid2(原生) | 好 |
| NScripter | ONScripter Plus(原生) | 好 |
| Artemis | 官方就有 Android 运行库 | 最好 |
| Siglus(Key 系)、YU-RIS 等 | 只能 Winlator(兼容层) | 折腾、耗电、翻车多 |
Artemis 有官方 Android 运行库,所以路线定为:拿官方运行库工程 → 换上自己的包名和图标 → 把 PC 版数据改造成安卓系统认识的格式。
折磨到死的调试
换上工程、塞进数据、装到手机上——标题画面出来了,挺激动。然后进入主菜单就崩了:
- 字体被截断、各选项挤成一团
- 剧情页底部那一整排按钮栏(Skip/Auto/Save/Load/Log/Config…)直接消失
- 没有虚拟按键,只能单击
Deepseek第一反应是"图片没按屏幕缩放",于是花了大力气去改图片尺寸、改分辨率——全是白费。因为方向从一开始就错了。把包里的脚本全解出来读,终于找到真正的根因:
-- system/adv/adv.lua:216
function reload_ui()
if game.os ~= "windows" then -- ← 安卓时这一段是空的!
elseif flg.ui then ...
else init_adv_btn() flip() -- 只有 windows 才建立 ADV 底部按钮栏
end
end
安卓下这段直接跳过,按钮栏从来没被创建过。再查资源:PC 包里只有 pc/ja 一套 UI,而 Android 版的表里指向 pa/、_hd/、_and/ 这些目录——PC 包里根本不存在。也就是说,官方 Android 版的数据包和 PC 版是两套,手上只有 PC 版,只能手动改——不修 android 表,而是让游戏以为自己跑在 Windows 上。
另外还顺手关掉了两个 Windows 专属弹窗(存档位数校验、旧系统版本检查),它们在安卓上会误报。
game.os 一变,加载的表也变了,Windows 表里写着 blur_path="pc/"、blur_exp=".hlsl"——HLSL 是 DirectX 的着色器,安卓的 GLES 根本编译不了。改回 mb/ + .glsl 。顺带发现原版 shader 里有一行非法写法:
vec4 fore = texture2D(...);
fore = 0.0; // ← vec4 赋 float,非法
官方 1.0.1 才修(改成逐分量 fore.r/g/b = 0.0),而且只修了 blur_k,漏了 blur_kx/blur_ky,这两个得自己补。
PC 上"模拟真机"
安卓实机调试一次要传 1.7GB,太慢。于是在 PC 上构造了和手机等价的环境:
game.os = "windows" ← 强制
game.trueos = "android" ← 模拟真机
这样 PC 上能跑、能截图、能快速迭代,而且 lua 层的逻辑组合和真机完全一致。
升级官方 1.0.2补丁
折腾好本体后,没想到升级补丁还折腾了大半天。官方给了升级补丁 Amakano3.pfs.010 / .030。查官方文档才知道:
同目录下的
root.pfs.NNN(000~999)是补丁,优先于root.pfs。
所以改名丢进数据目录就能生效——机制本身很干净。然后Deepseek想当然地自己重建了一个补丁包(把两个官方补丁合并成一个,顺便改掉里面几处路径)。
结果:黑屏 → 崩溃。折腾了很久。
最后逐字节对比才发现——.pfs 的索引在"条目表"之后还有一段尾部结构:
[u32 count+1] + (count+1)×[u32 值][u32 0] + [u32 条目区末尾]
重建索引时Deepseek整个把它漏掉了,引擎一读就 SIGSEGV。排查了半天才搞出来。
图标
工程模板自带的是模板作者的示例图,不是本作素材。我找了官方海报,让DeepSeek裁了女主角头部,做成标准 adaptive icon:
foreground:透明底,头像居中占画布 90%background:取裁切区四角平均色(暖米色)- legacy 的方形 / 圆形图标也都生成,5 档 dpi 全覆盖
总结:
AI干得好的:
- 逆向文件格式、写解析/加密脚本
- 在几千个文件里定位"哪一行决定了行为"
- 快速生成大量候选方案、反复试错
- 跨语言/跨工具链的琐碎工作(Lua / Python / PowerShell / adb / Gradle)
AI干得不好的:
方向必须要人把关,不然会在错路上一路狂奔;有过好多次,活没干完,Deepseek直接嚷嚷说"看起来好了",然后停工,这种时候必须拿个鞭子抽他他才干活 ![]()
遗留
- OP 影片是
.wmv,安卓不解码,不播。暂时没改
因为codebuddy今天deepseek的免费额度用完了
写在最后
AI 把这类"脏活累活密集"的移植工作门槛拉低了很多,但仍然需要人来判断拿主意。希望各位大胆尝试,能把自己喜欢的 Galgame 搬上手机。














