Live2D 看板娘项目日志
项目本地路径: D:\my-blog\technical
本地预览: http://127.0.0.1:8080(需先启动 Python HTTP Server 在 public/ 目录)
预览启动: cd D:\my-blog\technical\public && python -m http.server 8080
重新生成: cd D:\my-blog\technical && npx hexo generate
项目背景
- Hexo 博客 + NexT.Muse 主题
- 目标:左下角 Live2D 看板娘,三个角色轮换(小埋/和泉纱雾/香风智乃),点击触发语音(7s 冷却),各自独立语音不串扰
技术栈
- Live2D 引擎: live2d-widgets v1.0.1 (stevenjoezhang/live2d-widget)
- Runtime: Cubism 2.1 (live2d.min.js)
- 模型源: [email] (CDN)
- 语音: 小埋 34 句 wav(CDN 原声),纱雾 24 句 wav(B站视频 BV1fK4y1774f 提取,本地托管)
- 语音识别: OpenAI Whisper small 模型(日语)
- 注入点:
source/_data/body-end.swig(PJAX 环境)
- 核心配置:
source/js/live2d-config.json / source/js/model_list.json
- 模型目录:
source/js/model/umaru/ source/js/model/sagiri/ source/js/model/chino/
成功的部分
| 事项 |
状态 |
说明 |
| 博客本地预览 |
✅ |
Python HTTP server on 8080 |
| 菜单调整 |
✅ |
home / categories / archives / music / vibe-coding |
| 鼠标拖尾粒子 |
✅ |
Canvas 蓝紫色粒子,PJAX 兼容 |
| Live2D 渲染 |
✅ |
PixiJS + Cubism2 核心加载成功 |
| B站视频下载+提取 |
✅ |
yt-dlp → 94秒 m4a → ffmpeg → WAV |
| Whisper 语音识别 |
✅ |
small 模型,24 句日语,按标点分段 |
| 模型文件本地托管 |
✅ |
umaru 34句 / sagiri 24句 / chino 静态 |
| MSR 唱片音量 |
✅ |
降至 30% |
| 看板娘尺寸 |
✅ |
scale 0.42,工具栏 0.7 |
反复失败的部分
| 问题 |
尝试次数 |
根因 |
| CDN 路径 vs 本地路径 |
5+ 次来回切换 |
Widget 的 cdnPath 同时控制 model_list.json 位置和模型文件/语音路径解析,无法分离 |
| 本地 cdnPath 时语音 404 |
3 次 |
Widget 对 voice 文件的路径解析与 CDN 模式不同,本地模式下 voice/ 路径拼接错误 |
| CDN cdnPath 时模型过多 |
3 次 |
只能加载 yzs 完整模型列表(Pio 80套 + Tia 60套 + 7个角色),fetch 拦截可过滤但副作用未知 |
| fetch 拦截方案 |
当前 |
拦截 model_list.json 过滤三人 + 拦截 sagiri voice 重定向本地。但 widget 内部音频管理不隔离模型 |
| 小埋语音串到纱雾 |
未解决 |
Widget 切换模型时不清理上一个模型的 Audio 实例,导致语音残留 |
| 重复触发语音 |
未解决 |
Widget 的 tap motion 系统无内置冷却,且同 motion 重复触发依赖 widget 内部状态机 |
| hitTest 报错 |
3 次出现 |
index.js:194 — widget v1.0.1 的 Cubism2 鼠标跟踪 bug,部分模型的 hit_areas 参数 ID 不匹配 |
当前架构
1 2 3 4 5 6 7
| Widget (live2d-widgets v1.0.1) ├─ cdnPath: yzs CDN ├─ model_list.json → fetch 拦截 → 只返回 umaru/sagiri/chino ├─ umaru/index.json → CDN 直连(34 句原声) ├─ sagiri/index.json → fetch 拦截 → 本地版本(24 句 B 站语音) ├─ sagiri/voice/*.wav → fetch 拦截 → 本地文件 └─ 其他资源 → CDN 直连
|
无法解决的根本矛盾
- Widget 不支持模型间音频隔离 — 所有模型共享同一个 Audio 上下文,切换模型不清洗
- v1.0.1 的 Cubism2 路径解析 — voice 文件路径拼接逻辑在 CDN 和本地模式下不同,无法用单配置兼容
- 无内置冷却 — tap 触发依赖 widget 内部 motion 状态机,外部 JS 拦截会导致动画+语音一起失效
- hit_areas 参数 ID — 部分模型(umaru/chino)的
D_REF.PT_HEAD 等引用在 runtime 中为 null
已尝试过、确认无效的方案
cdnPath="/js/" + 本地完整模型文件 → 语音 404
cdnPath=yzs CDN → 模型列表无法过滤
initWidget({ models: [...] }) 直接传模型 → 参数被忽略
waifuPath 本地 JSON → 模型部分被 cdnPath/model_list.json 覆盖
new Audio() 构造函数拦截 → 影响全局含 MSR 音乐播放器
click 事件 capture 拦截 → 动画和语音一起被阻断
关键文件清单
| 文件 |
作用 |
D:\my-blog\technical\source\_data\body-end.swig |
主注入点(Live2D widget + 鼠标拖尾 + 音量控制 + fetch 拦截) |
D:\my-blog\technical\source\js\live2d-config.json |
看板娘 tips/对话配置 |
D:\my-blog\technical\source\js\model_list.json |
本地模型过滤列表(CDN 模式下未被使用) |
D:\my-blog\technical\source\js\model\umaru\ |
小埋模型文件(34 语音 + 34 动作) |
D:\my-blog\technical\source\js\model\sagiri\ |
纱雾模型文件(24 语音 + 动作) |
D:\my-blog\technical\source\js\model\chino\ |
智乃模型文件(无语音) |
D:\my-blog\technical\themes\next\_config.yml |
NexT 主题配置(菜单、侧边栏、scheme) |
D:\my-blog\technical\source\music\index.md |
黑胶唱片播放器页面 |
D:\my-blog\technical\source\_data\styles.styl |
自定义 CSS 注入点 |
D:\my-blog\technical\segment_audio.py |
音频分段脚本(Whisper + ffmpeg) |
D:\my-blog\technical\sagiri_voice.m4a |
B 站下载的原始音频 |
D:\my-blog\technical\sagiri_voice.wav |
转换后的 WAV(16kHz mono) |
D:\my-blog\technical\sagiri_segments\ |
24 个分段音频 + metadata.json |