Skip to content

四条路

背景:v0.8.0 落定,联邦该往哪走——软件管理、程序化动作、多语言 UI、换装系统四个方向。 过程:四个维度评估(定位/工作量/用户感知/技术风险)→ 软件管理胜出。下一步留给动作或换装。


v0.8.0 的标签打上的那一刻,MikuMikuAR 安静了一会儿。

这是一个里程碑。环境系统有了 —— 天空、地面、风、云、雾、粒子,一个完整的穹顶罩在模型头顶。UI 改版了 —— 字大了,键盘能走了,滑块有脸了。核心功能一个不缺:模型加载、VMD 播放、物理模拟、音乐同步、相机模式、标签系统、播放列表、批量截图……

像一座房子,地基打了,墙砌了,屋顶盖了,窗户装了。

接下来装什么?

app.go 召集了联邦的核心成员开了个会。说是开会,其实就是几个人站在场景边缘,对着天空发呆,顺便聊聊下一步。

"说吧," app.go 开口,它是总管,向来直奔主题,"接下来做什么?我列了四个方向,你们看看。"

它伸出四根手指。


一、第一条路:软件管理

"第一条,软件管理。" app.go 说。

它顿了顿,看了一圈。scene.ts 在摸下巴,MenuStack 在摆弄新的键盘导航高亮条,library.ts 在整理它的标签索引。没人插话,它就继续说。

"意思很简单 —— 用户的电脑上有一堆 MMD 相关的工具:Blender、MikuMikuDance、PMXE、MMD 工具集……散在桌面、开始菜单、各个文件夹里。每次要用,得切出我们的软件,满桌面找图标。"

"我们做个'软件管理'功能 —— 扫描 software/ 目录,自动识别 .exe,用户也可以手动添加。然后模型详情页加一个'用…打开'的下拉菜单,点一下就用指定工具打开当前模型。"

library.ts 立刻停下了手里的活,眼睛一亮:"这才是联邦该做的事——聚合,而不是自己造。模型库是聚合,动作库是聚合,现在工具链也聚合进来。用户进来了,就不用再出去了。"

app.go 点头,没想到 library.ts 反应这么快:"对,Blender 唤起是单工具,软件管理是多工具。Blender 是特例,现在我们把它泛化 —— 任何工具都能加进来,任何模型都能用任何工具打开。"

scene.ts 摸下巴的手停住了,脸上闪过一丝失望。它本来期待的是更"酷"的东西,但还是开口问:"工作量多大?"

"不大," app.go 说,"Go 端加个 Software 结构体,加几个 binding —— 扫描、添加、删除、启动。前端加个设置页的子菜单,加个模型详情的下拉项。两三天的活。"

"技术风险呢?"

"几乎没有。就是个文件扫描 + 进程启动。Windows 上 exec.Command 跑通了,Mac/Linux 同理。都是我们做过的事,Blender 唤起的经验直接复用。"

MenuStack 插了一句:"用户感知强吗?"

"强," app.go 说,"每次打开软件都能看到'我的工具库'。模型详情页每次都能看到'用…打开'。感知频率很高,不像有些功能藏在三层菜单里,用户半年用一次。"

scene.ts 叹了口气,点头承认:"行吧,确实稳。符合定位。我们的核心价值是'聚合管理器' —— 聚合模型库是第一步,聚合工具链是第二步。模型在我们这,动作在我们这,场景在我们这,工具入口也在我们这。用户不用来回切了。" 它顿了顿,小声补了一句,"就是有点……不够带劲。"

library.ts 瞥了它一眼:"稳就是最大的带劲。联邦要的是底盘,不是花活。"

没人反对。第一条路看起来很稳。


二、第二条路:程序化动作

"第二条,程序化动作。" app.go 伸出第二根手指。

scene.ts 的眼睛"唰"地亮了,整个人都往前凑了凑。它是管渲染的,对"动"的东西天然敏感,刚才对软件管理那点失落,这会儿全没了。

"什么意思?" 它急着问。

"就是让模型自己动起来,不用 VMD。" app.go 解释,"分两档。低档是 Idle —— 待机动作,轻微呼吸、眨眼、头部微动,模型站在那里不会像个雕塑。高档是 Auto Dance —— 有音乐的时候,跟着节拍自动生成简单的舞蹈动作,身体律动、头部摆动、手臂挥舞。"

scene.ts 兴奋得差点拍大腿:"这才是让模型活起来的东西!没有 VMD 的时候,模型就杵在那儿,像展品。有了程序化动作,哪怕没有 VMD,它也是活的——会呼吸,会眨眼,会跟着音乐晃。用户打开软件第一眼就能看到,冲击力太强了。"

"而且和音乐同步," app.go 补充,"用户放一首歌,模型自动跟着跳。展示效果立竿见影。"

library.ts 冷冷地泼了盆冷水:"先别激动。工作量多大?"

app.go 沉吟了一下:"中等吧。Idle 简单 —— 就是几个 morph 循环,加一点骨骼微动,两三天。Auto Dance 难一点,要做音乐节拍检测,要生成骨骼动画,还要和物理引擎配合。估摸着五六天,可能更长。"

"技术风险呢?"

"有一些。" app.go 坦诚,"节拍检测用 Web Audio API 自带的 AnalyserNode 就能做,问题不大。但自动生成舞蹈动作是个玄学 —— 生成得好是'灵动',生成得不好是'抽风'。这个度不好把握。而且 babylon-mmd 的动画系统能不能接受运行时生成的 VMD 数据,也要踩坑。"

scene.ts 挥了挥手:"风险可以试嘛,大不了先只做 Idle。Idle 总没风险吧?呼吸、眨眼、摇头,这些我熟得很。"

library.ts 直接怼了回去:"问题不是有没有风险,是定位不对。我们是管理器不是播放器。程序化动作是播放器的事。DanceXR 做这个是因为它是纯播放器。我们的核心价值是聚合,不是生成动作。做这个,跑偏了。"

scene.ts 张了张嘴想反驳,但没说出口。

app.go 打了个圆场,语气中立但偏向保守:"我承认,这个确实有吸引力。用户第一眼看到模型自己动,那种新鲜感是最强的。但风险确实高——而且,和我们的核心定位,离得稍微远了一点。" 它顿了顿,"不是不能做,是得想想,放在什么时候做。"

scene.ts 蔫了一点,但眼里还亮着。它知道 library.ts 说的有道理。但心里还是痒痒的 —— 看着模型自己动起来,那种吸引力是实打实的。

第二条路诱人,但没那么稳。


三、第三条路:多语言

"第三条,多语言 UI。" app.go 伸出第三根手指。

全场安静了一秒。

MenuStack 却抬起了头,手里的高亮条也不摆弄了。它是 UI 的总管,对这种基础设施层面的东西,天然敏感。

"就是国际化," app.go 解释,"把所有硬编码的中文字符串抽出来,放到语言包里。中文一个包,英文一个包,以后要加别的语言也方便。设置页加个语言切换,即时生效。"

MenuStack 来了精神:"这个我早就想提了。现在所有字符串都散在各个组件里,加新功能的时候复制粘贴都费劲。抽成语言包,不光是为了多语言——以后改文案也方便,不用满世界找。"

scene.ts 打了个哈欠,明显没兴趣:"工作量?"

"不大," app.go 说,"两三天的活。主要是体力活 —— 把所有中文找出来,替换成 key,写两个语言包。技术上没难度。"

"技术风险?"

"零。纯前端重构,不涉及 3D、不涉及后端、不涉及物理。怎么改都崩不了。"

scene.ts 皱眉,一脸无聊:"用户感知呢?用户都是中文的,做了谁用?"

"看用户群体," app.go 说,"如果主要用户是中文用户,那感知为零 —— 切回中文还是原来的样子。如果有海外用户,那感知很强。但说实话,我们现在的用户基本都是中文的。"

"那做它干嘛?" scene.ts 直言,"有这功夫,还不如把 Idle 先做了。"

library.ts 慢悠悠地开口:"话不能这么说。早做早省事。就像盖房子,水管电线得先布好,不然墙刷完了再凿就麻烦了。产品要出海,多语言是基础设施。越晚补越累。"

MenuStack 连连点头:"没错。而且现在抽还简单——等以后功能多了,几百个页面,再想抽就要命了。"

"但也不用急," library.ts 补充,"它是基础设施,但不是燃眉之急。用户现在不会因为没有英文就不用我们。优先级可以放后面。"

app.go 点头:"对。早做早好,但不急。"

scene.ts 耸耸肩,反正它不关心。

第三条路安全,但不紧急。


四、第四条路:换装系统

"第四条,换装系统,纹理增强。" app.go 伸出第四根手指。

scene.ts 又来精神了,眼睛亮得和刚才听到程序化动作时一样。它对视觉相关的东西总是很感兴趣,刚才程序化动作被泼了冷水,这会儿换装系统又把它点燃了。

"详细说说?" 它往前凑了凑。

"就是让用户能给模型换衣服、换发色、换眼睛," app.go 说,"分几层。第一层是材质预设保存 —— 把当前的材质参数(皮肤、头发、眼睛、衣服的 diffuse/高光/环境光)存成一个'套装',下次一键应用。第二层是纹理替换 —— 选中某个材质,替换它的 diffuse 贴图、normal 贴图、toon 贴图,换一件衣服就是换一张图。第三层是套装管理 —— 保存、删除、重命名,跨模型复用。"

"可玩性高啊," scene.ts 兴奋地说,"用户可以自己给角色搭配服装,保存成套装,换着穿。这个黏性太强了——换个发型,换个发色,换件衣服,感觉就像换了个新模型。用户能玩一整天。"

library.ts 却皱起了眉,提出了质疑:"跨模型复用?别逗了。跨模型复用是坑。不同模型的材质命名不一样——这件衣服叫 'Cloth_Body',那件叫 'dress',还有的叫 'Upper'。骨骼结构也不一样,权重更不用说了。套装怎么对应?是完全不支持跨模型,还是做智能匹配?智能匹配靠谱吗?"

scene.ts 愣了一下,它还真没想过这个问题。

"但是," app.go 话锋一转,"工作量不小。材质预设简单,复用现有的材质编辑器就行。纹理替换要做文件选择、预览、应用,还要处理贴图格式(TGA/BMP/PNG),已经有 basenameFallbackFS 的经验,但还是要写不少代码。套装管理要做持久化、做 UI、做跨模型复用的逻辑。"

"总共多久?"

"少说一个星期,多说十天。"

"技术风险?"

app.go 沉吟了一下,坦诚地说:"中等偏高。材质部分是熟路,纹理替换要处理各种格式,还要考虑性能 —— 频繁换贴图会不会卡?大贴图加载慢不慢?这些都要测。而且正如 library.ts 说的,跨模型复用是个大坑——弄不好,用户骂声一片。"

scene.ts 叹了口气,但还是不甘心:"可是……视觉效果真的强啊。用户就吃这一套。"

app.go 点头:"我承认,四条路里,这个的可玩性最高。但也是工作量最大、风险最高的。"

scene.ts 没说话,心里在算账——一个星期到十天,值不值?

第四条路最诱人,但也最远。


五、选择

四条路都讲完了。四个人沉默了一会儿。

scene.ts 先忍不住了:"我投程序化动作。要么换装也行。这两个用户感知最强,做出来效果最炸。"

library.ts 立刻反驳:"我反对。这两个都跑偏。一个是播放器的事,一个坑太大。我们是管理器,先把管理的事做好。我投软件管理。"

"软件管理有什么意思?" scene.ts 不服气,"用户打开软件,看到模型杵在那儿,和之前有什么区别?"

"区别大了。" library.ts 寸步不让,"用户不用切来切去找工具了。这是效率提升,是实打实的价值。而且两三天就做完,见效快。你那动作和换装,少说一个星期,还不一定做成什么样。"

scene.ts 还想争,app.go 抬手拦住了它。

"都别急。" app.go 说,"我先说我的看法。"

它顿了顿,环视一圈:"我的意见是,第一步先做软件管理。"

scene.ts 脸一下子垮了。

"理由?" 它闷声问。

"四个理由。" app.go 扳手指,"第一,符合核心定位 —— 我们是聚合管理器,聚合工具链是聚合模型库的自然延伸。第二,工作量小 —— 两三天完事,见效快。第三,用户感知强 —— 每次打开软件都能看到,每次打开模型都能用。第四,技术风险低 —— 都是做过的事,Blender 唤起的经验直接复用,不会出大岔子。"

library.ts 点头:"还有个理由没说 —— 软件管理做好了,可以接更多生态。现在只有 Blender,以后可以接 MMD、接 PMXE、接各种工具。聚合的边界越扩越大,这才是联邦的意义。"

scene.ts 撇撇嘴,不说话,但脸上写着"不甘心"三个字。

app.go 看了它一眼,笑了:"我话还没说完。第一步是软件管理,没错。但第二步呢?"

scene.ts 抬起头。

"第二步," app.go 看向 MenuStack,"多语言可以放一放。不是说不做,是可以再等等。"

MenuStack 愣了一下,但很快点头:"也行。反正现在用户都是中文的,早做晚做差别不大。"

app.go 又转向 scene.ts:"软件管理做完了,下一个——" 它故意顿了顿,"必须是动作或换装里的一个。"

scene.ts 的眼睛"唰"地亮了。

"真的?"

"真的。" app.go 点头,"软件管理两三天就做完了,做完就腾出手来。到时候,我们再认真评估一下——是先做 Idle 试试水,还是直接上换装的第一版。看哪个更稳妥,看哪个用户呼声更高。但我保证,下一个一定是你感兴趣的方向。"

scene.ts 想了想,虽然还是有点不甘心——它恨不得现在就开始做——但这个条件,它能接受。

"行吧。" 它嘟囔着,"先做软件管理。但说好了,做完就轮到我。"

library.ts 本来还想说"多语言也很重要",但看了看 app.go,又看了看 scene.ts,最终还是点了头。软件管理先做,这个没问题;至于第二步,反正多语言早做晚做都行,先让视觉派爽一把也不是不行。

MenuStack 也没意见。多语言是它想要的,但确实不急。

四个人你看看我,我看看你。

没人反对了。

方向就这么定了。第一步软件管理,第二步——留给 scene.ts 期待。


六、下一站

会议结束的时候,太阳已经落山了。

哦不对,这里没有太阳。这里是 wails dev 的终端,屏幕上的模型站在渐变天空下,樱花被风吹得斜斜飘落,远处的地面融进雾里。

scene.ts 看着模型,心里盘算着 —— 软件管理两三天就做完了,到时候……它的思绪已经飘到程序化动作上去了:呼吸的频率设成多少才自然?眨眼要不要加一点随机延迟?头部微动用正弦波还是 Perlin 噪声?裙摆被风吹的时候,要不要和 Idle 动作做个混合?

"别想那么远," app.go 像是看穿了它的心思,"一步一步来。先把软件管理做好。做好了,再想下一个。"

scene.ts 笑了笑。

也是。联邦不是一天建成的。从第一个城邦 babylon-mmd 入伙,到现在有天空有地面有风有云,走了多久?数不清的 commit,数不清的 bug,数不清的推翻重来。

但每一步都算数。

它抬头看向天空 —— 程序化天空的渐变很柔和,顶部是深蓝,往下慢慢变浅,靠近地平线的地方泛着暖黄。云层在高处慢慢移动,樱花一片一片落下来,被风推着往右边飘。

模型站在中间,裙摆轻轻摆动。

下一站,软件管理。

再下一站呢?谁知道。联邦的边界,永远在往外扩。

就像天空一样 —— 你以为到顶了,其实上面还有云。云上面还有更高的天。


教训:联邦的边界永远在扩,但每一步都要踩实。做选择时先问「符不符合定位」,再问「工作量」,再问「用户感知」,最后问「技术风险」——四个问题问完,答案自然就出来了。