dsh-pet-live2d 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +56 -0
- package/README.md +425 -0
- package/cordis.patch.yml +8 -0
- package/docs/README.md +14 -0
- package/docs/downscale-fix.png +0 -0
- package/docs/preview.png +0 -0
- package/lib/client.js +3525 -0
- package/lib/index.js +1027 -0
- package/lib/live2d-vendor.js +997 -0
- package/package.json +61 -0
- package/pets/ds-whale-girl/LICENSE +37 -0
- package/pets/ds-whale-girl/README.md +67 -0
- package/pets/ds-whale-girl/c_0120.model3.json +249 -0
- package/pets/ds-whale-girl/catalog.json +310 -0
- package/pets/ds-whale-girl/expressions/angry.exp3.json +55 -0
- package/pets/ds-whale-girl/expressions/blank-eyes.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/cat-paws.exp3.json +20 -0
- package/pets/ds-whale-girl/expressions/claw-recolor.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/claw.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/cry.exp3.json +60 -0
- package/pets/ds-whale-girl/expressions/dark-tablecloth.exp3.json +11 -0
- package/pets/ds-whale-girl/expressions/dizzy.exp3.json +40 -0
- package/pets/ds-whale-girl/expressions/draw-brush.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/draw-eraser.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/draw-undo.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/drool.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/excited.exp3.json +65 -0
- package/pets/ds-whale-girl/expressions/exclaim.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/facial-red.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/glasses-oval.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/glasses-round.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/glasses-square.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/gloomy.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/hands-peace.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/headband.exp3.json +11 -0
- package/pets/ds-whale-girl/expressions/heart-pop.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/heartbeat.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/ketchup-squeeze.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/love-eyes.exp3.json +60 -0
- package/pets/ds-whale-girl/expressions/menu-press.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/naughty.exp3.json +20 -0
- package/pets/ds-whale-girl/expressions/omurice.exp3.json +16 -0
- package/pets/ds-whale-girl/expressions/parfait.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/phone-recolor.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/ponytail.exp3.json +11 -0
- package/pets/ds-whale-girl/expressions/question.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/sad.exp3.json +35 -0
- package/pets/ds-whale-girl/expressions/soul-out.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/sparkle.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/star-eyes.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/sticker-bow.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/sticker-cat.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/sticker-rabbit.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/sunglasses.exp3.json +20 -0
- package/pets/ds-whale-girl/expressions/sweat.exp3.json +10 -0
- package/pets/ds-whale-girl/expressions/tongue-out.exp3.json +26 -0
- package/pets/ds-whale-girl/expressions/whale-on-desk.exp3.json +11 -0
- package/pets/ds-whale-girl/expressions/whale.exp3.json +11 -0
- package/pets/ds-whale-girl/model/c_0120.cdi3.json +1802 -0
- package/pets/ds-whale-girl/model/c_0120.moc3 +0 -0
- package/pets/ds-whale-girl/model/c_0120.physics3.json +2396 -0
- package/pets/ds-whale-girl/motions/bubble-gum.motion3.json +369 -0
- package/pets/ds-whale-girl/motions/hammer.motion3.json +1120 -0
- package/pets/ds-whale-girl/motions/idle.motion3.json +4746 -0
- package/pets/ds-whale-girl/motions/ketchup.motion3.json +768 -0
- package/pets/ds-whale-girl/motions/open-case.motion3.json +116 -0
- package/pets/ds-whale-girl/motions/selfie-quick.motion3.json +47 -0
- package/pets/ds-whale-girl/motions/selfie.motion3.json +671 -0
- package/pets/ds-whale-girl/motions/spray-water.motion3.json +31 -0
- package/pets/ds-whale-girl/pet.json +599 -0
- package/pets/ds-whale-girl/previews/idle.png +0 -0
- package/pets/ds-whale-girl/textures/texture_00.png +0 -0
- package/pets/ds-whale-girl/textures/texture_01.png +0 -0
- package/pets/ds-whale-girl/voice.json +46 -0
- package/pets/ds-whale-girl//345/256/211/350/243/205/350/257/264/346/230/216.md +30 -0
- package/src/vendor-entry.ts +28 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 A8Chann
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Third-party components bundled into lib/live2d-vendor.js:
|
|
26
|
+
|
|
27
|
+
pixi.js MIT
|
|
28
|
+
untitled-pixi-live2d-engine MIT
|
|
29
|
+
|
|
30
|
+
The DS鲸鱼娘 model bundled in ./pets/ds-whale-girl (and its source pack in
|
|
31
|
+
../model-packs) is NOT covered by the MIT license above, and must not be used
|
|
32
|
+
as if it were. It is licensed CC BY-NC-SA 4.0:
|
|
33
|
+
|
|
34
|
+
attribution required - NON-COMMERCIAL only - share-alike
|
|
35
|
+
https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh
|
|
36
|
+
|
|
37
|
+
Rights holders, in the chain the character passed through:
|
|
38
|
+
|
|
39
|
+
上善无形 original character, the OC 溟月
|
|
40
|
+
https://www.pixiv.net/users/62155430
|
|
41
|
+
ZipZipPipe the DeepSeek maid redesign
|
|
42
|
+
https://www.pixiv.net/users/18604994
|
|
43
|
+
氵六青 this Live2D model (rigging, motions, expressions)
|
|
44
|
+
https://space.bilibili.com/11272072
|
|
45
|
+
|
|
46
|
+
氵六青 authorised redistribution and open-sourcing of this model here. That
|
|
47
|
+
permission does NOT lift the base terms: the character itself was published by
|
|
48
|
+
上善无形 and ZipZipPipe under CC BY-NC-SA 4.0, so the non-commercial and
|
|
49
|
+
share-alike conditions still apply. Commercial use needs permission from EACH
|
|
50
|
+
rights holder, not just one.
|
|
51
|
+
|
|
52
|
+
Live2D Cubism Core (live2dcubismcore.min.js) is proprietary and is NOT
|
|
53
|
+
included in this repository. It must be obtained from Live2D Inc. and placed
|
|
54
|
+
in the pet runtime directory by the user. See README.
|
|
55
|
+
|
|
56
|
+
Full attribution: ../NOTICE.md
|
package/README.md
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
# dsh-live2d-pet — Live2D 桌宠插件
|
|
2
|
+
|
|
3
|
+
给 DSH Web GUI 挂一只 **Live2D 桌宠**:会呼吸、会眨眼、眼睛和脑袋跟着鼠标转,能拖着换位置,点一下有反应,右键面板里能播模型自带的**全部动作与表情**。
|
|
4
|
+
|
|
5
|
+
自带 **DS鲸鱼娘**(氵六青 的无偿分享模型):8 组动作 + 44 个表情/道具。
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## 功能
|
|
10
|
+
|
|
11
|
+
| 功能 | 说明 |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Live2D 渲染 | PixiJS v8 + untitled-pixi-live2d-engine(Cubism 3/4/5),WebGL 透明画布,$O(1)$ 开销的常驻浮层 |
|
|
14
|
+
| 鼠标跟随 | 指针在窗口内移动时,模型的眼球/头部实时朝向指针;移开后视线自然回到中心 |
|
|
15
|
+
| 拖动与缩放 | 按住**角色身上**拖走;位置与尺寸存 localStorage,刷新后原样恢复 |
|
|
16
|
+
| 点击反应 | 点**头部** → 重锤出击 + 脸红 + 台词;点身上其它地方只出气泡,不挥锤 |
|
|
17
|
+
| 事件穿透 | 只有角色剪影吃鼠标事件,方形画布的透明区域**穿透**到底下页面,不挡 DSH 的 UI |
|
|
18
|
+
| 零常驻 UI | 画面上只有角色本身;UI 全在右键面板里,`Esc` 或点面板外关闭 |
|
|
19
|
+
| 右键面板 | **宠物身上点右键**呼出全部控制:动作 / 表情 / 换宠物 / 大小 / 归位。平时画面上**没有任何常驻 UI**,鼠标划过也不显示 |
|
|
20
|
+
| 动作面板 | 读取模型自己声明的 motion group,8 个动作一键播放 |
|
|
21
|
+
| 装扮菜单 | **表情与装扮合并成一个菜单**:44 个表情归入 17 个互斥槽位,每槽单选,**跨槽位可同时生效**。选择**常驻**;其中六件**装扮**(眼镜/发饰/魔爪/巴菲/桌布/手机换色)是**穿在身上**的:会话相位不动它们,「归位」也不清它们,而且**跨启动记住**(localStorage) |
|
|
22
|
+
| 拖动 | 按住角色身上拖走;位置与尺寸存 localStorage。**可以一直往下拖,压出屏幕底边**(最多沉下去一个身位),模型的画布有透明边距,不放开下界的话角色看着永远悬空 |
|
|
23
|
+
| 多宠物 | `%DSH_HOME%\pets\` 下所有 `renderer: live2d` 的宠物都会被扫描,面板里可切换 |
|
|
24
|
+
| 零配置宠物 | 宠物 = 一个目录 + `pet.json`;插件不硬编码任何模型 |
|
|
25
|
+
|
|
26
|
+
## 安装
|
|
27
|
+
|
|
28
|
+
```pwsh
|
|
29
|
+
dsh plugin --profile web add "link:D:\HTML\DSH_Pet_Live2d\dsh-live2d-pet"
|
|
30
|
+
# 或从目录安装
|
|
31
|
+
dsh plugin --profile web add "link:<本目录绝对路径>"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
装完 **重启 `dsh web`**(新 bundle 不参与热重载)。
|
|
35
|
+
|
|
36
|
+
## Cubism Core 运行时(一般不用管)
|
|
37
|
+
|
|
38
|
+
Live2D 的专有许可**不允许再分发** Core 运行时,所以本插件**不内置**它 ——
|
|
39
|
+
但也不需要你去找文件:**宿主半区会在本地缺失时,向 Live2D 自己的 CDN 取一份**
|
|
40
|
+
(`https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js`,
|
|
41
|
+
官方 SDK 文档就是让使用者在页面里引这一行;这个地址带 `Access-Control-Allow-Origin: *`),
|
|
42
|
+
取到后**缓存到本地**,之后离线也能用:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
%DSH_HOME%\pets\.runtime\live2dcubismcore.min.js
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
想完全离线就自己从 [Live2D 官方 Cubism SDK for Web](https://www.live2d.com/download/cubism-sdk/download-web/)
|
|
49
|
+
下载后放到上面那个路径,插件会优先用它。两边都拿不到时(无网络 + 无本地文件),
|
|
50
|
+
宠物位置会显示一张安装指引卡,接口返回 502 并带上该地址和落盘路径(不会崩,也不影响其它插件)。
|
|
51
|
+
|
|
52
|
+
## v1.4 UI 重构
|
|
53
|
+
|
|
54
|
+
**画面上不再有任何常驻 UI,鼠标划过也不出现。** 所有功能收进**右键面板**。
|
|
55
|
+
|
|
56
|
+
| 之前 | 现在 |
|
|
57
|
+
|---|---|
|
|
58
|
+
| 鼠标划过宠物 → 浮出一条工具栏(面板 / 归位 / - / +) | 划过**什么都不显示**;宠物身上**点右键**呼出完整面板 |
|
|
59
|
+
| 工具栏浮在角色身上,挡住画面 | 面板在宠物旁边,不盖住角色 |
|
|
60
|
+
| 大小只能靠工具栏的 -/+ | 面板底部:- / 滑杆 / + / 当前 px / 归位 |
|
|
61
|
+
| 关面板要点同一个按钮 | `Esc`、右上角 `×`、或点面板外任意处都能关 |
|
|
62
|
+
|
|
63
|
+
为什么去掉 hover:一是浮层压在角色身上很碍眼;二是**透明穿透的代理层没法好好表达 hover**——根节点是 `pointer-events: none`,`[data-hover]` 只能靠代理的 `pointerenter/leave` 手动维护,指针从宠物移到按钮上那段空隙还会让工具栏闪掉。右键没有这些问题:它是主动动作,而且要拦掉浏览器自带菜单(`preventDefault`),语义唯一。
|
|
64
|
+
|
|
65
|
+
面板只对**角色剪影**响应——在方形画布的透明角落点右键会照常穿透到页面,不会误开面板。
|
|
66
|
+
|
|
67
|
+
### 槽位分类(装扮 + 表情)
|
|
68
|
+
|
|
69
|
+
44 个表情全部归入 **17 个互斥槽位**(换过宠物/改过配置的话,以面板和 `pet.json` 为准)。
|
|
70
|
+
分类不是按文件名猜的,依据是**模型作者在 cdi3 里自己写的分组和中文名**——例如 `ParamGroup29` 被作者命名为「C款动作开关(集中放置方便检查」,
|
|
71
|
+
里面正好是 `point`(点菜板) / `danbaofan`(蛋包饭) / `phone`(手机手) / `phone7`(双手比耶) / `maoshou`(猫手),一组互斥的手部状态。
|
|
72
|
+
表情那 7 个槽位则参考了参数冲突:`爱心眼/开心兴奋/悲伤/大哭/生气/晕晕` 都会写眉毛参数,放在一起必然打架。
|
|
73
|
+
|
|
74
|
+
| 槽位 | 不选 | 可选项 |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| 眼镜 | 无 | 圆眼镜 / 方眼镜 / 椭圆眼镜 / 墨镜 |
|
|
77
|
+
| 贴纸 | 无 | 猫猫 / 兔兔 / 蝴蝶结 |
|
|
78
|
+
| 发饰 | 戴着 | 摘掉发箍 / 单边马尾 |
|
|
79
|
+
| 桌布 | 白色 | 黑色 |
|
|
80
|
+
| 魔爪 | 无 | 粉魔爪 / 白魔爪 |
|
|
81
|
+
| 鲸鱼 | 无 | 头顶鲸 / 放桌上 |
|
|
82
|
+
| 桌面摆设 | 无 | 巴菲 |
|
|
83
|
+
| 右手 | 无 | 掏出手机 / 喵喵手 / 双手比耶 / 挤番茄酱 / 写本本 |
|
|
84
|
+
| 左手 | 无 | 撤回 / 画笔 / 橡皮 / 蛋包饭 |
|
|
85
|
+
| 眼部 | 默认 | 星星眼 / 爱心眼 / 呆呆眼 / 晕晕 / 阴暗 |
|
|
86
|
+
| 情绪 | 平静 | 开心兴奋 / 悲伤 / 大哭 / 生气 / 调皮 / 闭眼口水 / 吐魂 |
|
|
87
|
+
| 嘴部 | 闭嘴 | 吐舌 / 吹泡泡糖 |
|
|
88
|
+
| 符号 | 无 | 问号 / 感叹号 / 流汗 |
|
|
89
|
+
| 氛围 | 无 | 情绪花花 / 心跳 / 冒爱心 |
|
|
90
|
+
| 脸红 | 否 | 脸红 |
|
|
91
|
+
| 其他 | 无 | 手机换色 |
|
|
92
|
+
| 点菜 | 无 | 点菜按下 |
|
|
93
|
+
|
|
94
|
+
槽位定义在 `pet.json` 的 `live2d.expressionSlots`,换宠物只改这里;宿主会用
|
|
95
|
+
`normaliseSlots()` 校验(缺 id、缺表达式的槽位直接丢掉,免得渲染出死按钮)。
|
|
96
|
+
|
|
97
|
+
**一个选项可以带多个表达式**。这个模型的 `魔爪换色` 只改颜色、本身不显示任何东西
|
|
98
|
+
(没有爪子可换色),所以「白魔爪」必须同时点亮 `桌面粉魔爪` 和 `魔爪换色`:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{ "label": "白魔爪", "expressions": ["桌面粉魔爪", "魔爪换色"] }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**选择常驻**。早期版本里手动点的表情会在 `EXPRESSION_HOLD_MS` 后自动清掉,那是为了防止
|
|
105
|
+
宠物卡在某个状态;但对装扮来说这是错的——用户从面板挑的搭配过几秒自己消失,看起来就是 bug。
|
|
106
|
+
现在自动清理只留给**反应**和**会话相位**,面板选择一直保留到「归位」或改选。
|
|
107
|
+
|
|
108
|
+
### 装扮:穿在身上的东西
|
|
109
|
+
|
|
110
|
+
眼镜 / 发饰 / 魔爪 / 巴菲(桌面摆设)/ 桌布 / 手机换色 这六个槽位是**装扮**(`OUTFIT_SLOTS`),
|
|
111
|
+
跟"这一轮临时挑的表情"不是一回事:
|
|
112
|
+
|
|
113
|
+
| 场景 | 临时表情 | 装扮 |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| 会话相位开始/结束 | 由相位接管,相位结束就撤掉 | **不动** |
|
|
116
|
+
| 点「归位」 | 清空 | **保留** |
|
|
117
|
+
| 重启 / 刷新页面 | 回到默认 | **从 localStorage 穿回来** |
|
|
118
|
+
| 自动清理(`EXPRESSION_HOLD_MS`) | 到点清掉 | 从不参与 |
|
|
119
|
+
|
|
120
|
+
实现上一句话就够:`commitPinsRef` 里装扮的 pin **最后合并**,所以相位的"槽位拥有权"
|
|
121
|
+
压不过它;`resetAll` 只清临时效果;选择一变就写一次 `localStorage`
|
|
122
|
+
(key `dsh-live2d-pet:outfit`),启动后在 ready 时读回来,并**校验 label 在当前 pet.json
|
|
123
|
+
里还存在**——换模型或改配置之后存档可能对不上,对不上就当没存过,不会凭空造一个选项出来。
|
|
124
|
+
|
|
125
|
+
### 多槽位叠加(装扮)
|
|
126
|
+
|
|
127
|
+
引擎的表情管理器一次只持有**一个**表情(`expressionManager.currentExpression`),所以「眼镜 + 猫猫贴纸 + 深色桌布 同时戴」不能交给它。
|
|
128
|
+
|
|
129
|
+
**做法**:插件自己按帧写参数。每个表达式在自己的 .exp3.json 里声明了它要写的通道和混合方式(这个模型全是 `Add`),客户端把选中表达式的通道取并集交给控制器,控制器每帧叠加一次。
|
|
130
|
+
|
|
131
|
+
**关键是挂钩点**:写在 `loadParameters()` 之后是错的——那一帧的 `saveParameters()` 会把它一起快照进基线,于是下一帧恢复出来时已经含了它,再叠一次,**永远关不掉**(实测开关停在 1,取消选择也不回落)。正确的点是 `saveParameters()` **之后**:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
每帧:loadParameters()(抹掉上一帧的表情层)→ 动作写参数 → saveParameters()(快照)
|
|
135
|
+
→ 我们写表情层 → 引擎算形变 → 绘制
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
引擎自己的表情流程就在 `saveParameters()` 之后,所以这就是同一层,只是没有「只能有一个」的限制。
|
|
139
|
+
|
|
140
|
+
实测(`cdp-merge`,全部从**真正的装扮面板**点出来):
|
|
141
|
+
|
|
142
|
+
| 操作 | 结果(帧内参数) |
|
|
143
|
+
|---|---|
|
|
144
|
+
| 什么都不选 | `[0,0,0,0]` |
|
|
145
|
+
| 点圆眼镜 | `[1,0,0,0]` |
|
|
146
|
+
| 再点猫猫 | `[1,1,0,0]` —— 眼镜留着 |
|
|
147
|
+
| 眼镜槽选「无」 | `[0,1,0,0]` —— 猫猫留着 |
|
|
148
|
+
| 再加深色桌布 | `[1,1,1,0]` |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### 附:这一块踩过的测量陷阱
|
|
153
|
+
|
|
154
|
+
同一个问题我给出过**两次互相矛盾**的结论,都是被错误的测量方法带偏的,记下来免得重蹈:
|
|
155
|
+
|
|
156
|
+
| 方法 | 得到 | 我当时的结论 | 真相 |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| 冻结 rAF + 截图逐像素比对 | 每格 0.00% | 「表情全都不生效」 | 冻结不可靠,后面每格都在读同一张陈旧帧 |
|
|
159
|
+
| 比对 canvas 哈希 | `changed: true` | 「表情是好的,只是要等淡入」 | 宠物一直在呼吸眨眼,**任意两帧都不会逐字节相同**,信号恒为真 |
|
|
160
|
+
|
|
161
|
+
可靠的做法是读**引擎在帧内写进模型的参数值**:确定性、不受动画相位影响。`cdp-exp` 和 `cdp-merge` 现在都这么做。
|
|
162
|
+
|
|
163
|
+
顺带修的:`cdp-exp` 原本是个**打印型** driver——结尾无条件 `process.exit(0)`,从不判定,再加上它唯一的信号(canvas 哈希)恒为真,等于从来没有断言过任何事。现在它真的会失败。
|
|
164
|
+
## v1.3 修正(本次)
|
|
165
|
+
|
|
166
|
+
| # | 问题 | 原因 | 处理 |
|
|
167
|
+
|---|---|---|---|
|
|
168
|
+
| 1 | 重锤出击在摸鱼动画里也会播 | 摸鱼从「所有非待机动作」里随机抽,重锤出击与鲸鱼喷水都在池子里 | 加 `FIDGET_DENY` 拒绝表;宠物可用 `motionOptions[group].fidget` 覆盖 |
|
|
169
|
+
| 2 | 鲸鱼喷水同上 | 同上 | 同上 |
|
|
170
|
+
| 3 | 动作 / 表情播完不回初始待机 | `hold: true` 是**永久**定格;手动点的表情也永久钉住 | `ACTION_HOLD_MAX_MS`(9s) / `EXPRESSION_HOLD_MS`(12s) 两道时限 + `resetToRest()` 收口 |
|
|
171
|
+
| 4 | 没接上 DSH 会话流 | 订阅了 `tool/call`——那是**会话日志事件**,不是 cordis 事件,**永远不触发**;而且相位只播一次,长任务看着像没反应 | 改订 `tools/pre-execute` / `tools/post-execute`(瀑布事件,必须 `next()`);`tool`/`done`/`failed` 改为 **sustain**,动作播完自动重播直到相位改变 |
|
|
172
|
+
| 5 | 整个方形画布都能拖动、挡住底下 UI | 元素即使全透明,只要 `pointer-events: auto` 就吃满整个盒子 | 根节点 `pointer-events: none` + 一层用 `clip-path: path()` 裁成剪影的**不可见代理**;透明处穿透(`mask-image` 不影响命中测试,只有 `clip-path` 会) |
|
|
173
|
+
| 6 | 点身上任何地方都挥锤 | 没有头部区域的概念 | 从**模型自己的五官 drawable** 量出头部包围盒(模型空间,随缩放/拖动自动跟随),只有点头部才触发重锤出击 |
|
|
174
|
+
|
|
175
|
+
### 为什么这些 bug 之前测不出来
|
|
176
|
+
|
|
177
|
+
回归套件的宿主是**假的**:`attachActivityEvents({ on: () => {} }, hub)` 传了一个空的 `on`,相位全靠 `/__nudge` 直接推 hub。于是**插件自己的事件订阅一行都没被验证过**——这正是 `tool/call` 那个 bug 能带着 10 个绿灯活下来的原因。
|
|
178
|
+
|
|
179
|
+
现在 harness 提供一个真的小事件总线 + `/__emit`,`cdp-host-events.mjs` 用**真实事件名**驱动并断言。新增 5 个 driver(共 16 个):
|
|
180
|
+
|
|
181
|
+
| driver | 覆盖 |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `cdp-host-events.mjs` | `tools/pre-execute` 有订阅者、会 `next()`、能推进相位;长相位持续播放;`tools/post-execute` / `agent/turn-stopping` 收尾 |
|
|
184
|
+
| `cdp-head.mjs` | 点头部播重锤出击 + 脸红;点身上不播;摸鱼池排除重锤/喷水 |
|
|
185
|
+
| `cdp-idle-return.mjs` | 表情 12s 自清;定格 9s 释放;`resetToRest()`;相位表情随相位清除 |
|
|
186
|
+
| `cdp-passthrough.mjs` | 四个角都穿透到页面;角落点击不触发反应、不拖动;角色上点击正常送达 |
|
|
187
|
+
| `cdp-bubble.mjs` | 动作定格真的能关掉:吹泡泡糖 / 掏出手机 → 无,**连做三轮**(污染只在第二轮之后现形);断言用 `drawn()`,并同时钉住"帧外基线仍停在 1"这条缝 |
|
|
188
|
+
## v1.2 交互与渲染优化
|
|
189
|
+
|
|
190
|
+
| 问题 | 原因 | 处理 |
|
|
191
|
+
|---|---|---|
|
|
192
|
+
| 放大后画面模糊 | 渲染缓冲固定 1x,画面被 CSS 拉伸 | `resolution = max(2, devicePixelRatio)`;实测 backing store 恒为 CSS 尺寸的 2–3 倍 |
|
|
193
|
+
| **缩小后线条发虚**(v1.2.1) | 两道叠加:① 2048² 图集被直接缩到 160–760px,而 `lod:"single-auto"` 只在 effectiveScale < 0.5 时才做 LOD——300px 时约 0.59,**这个分支根本没触发**,等于每个屏幕像素只从图集里抽 1 个纹素,细笔画被整根抽掉;② 画布 backing store 在 1x 屏上只有 160–760²,细线本身就落在采样点之间 | ① `lod:"full"` 建完整 mip 链(注意:`lod:false` 是"全分辨率但**不建** mip",比 `"single-auto"` 更糟)+ `maxAnisotropy: 8`;② 渲染倍率下限提到 **2x** 做超采样。面部细笔画像素占比实测(300px):原始 8.16% → 仅建 mip 3.89% → mip+2x 超采样 5.27%,断线/锯齿消失,细线恢复连续 |
|
|
194
|
+
| 画布空白处也能点到 | 整个方形 canvas 都在吃点击 | 从**实际渲染出的像素**提取 64×64 透明度网格,只有落在角色轮廓上才算点击 |
|
|
195
|
+
| 画布挡住底下的 UI(v1.3) | 元素即使透明,只要 pointer-events: auto 就会吃满整个盒子 | 根节点改成 pointer-events: none,另加一层**不可见代理**,用 clip-path: path(...) 把可点区裁成角色剪影(mask-image **不影响**命中测试,只有 clip-path 会影响)。透明处直接穿透到页面;代理轮廓按 hitsMask 的 ±1 格容差**膨胀一格**,两者严格重合 |
|
|
196
|
+
| 鼠标移开后视线不回正 | 视线停在"最后一个指针位置" | 超出注视范围即回到**模型默认中心位**(`data-gaze="center"`) |
|
|
197
|
+
| 不接会话状态 | 只响应点击 | 宿主订阅 `agent/status` / `agent/turn-stopping` / `agent/error` / `approval/request`,经同源 SSE 推送;客户端按相位切换动作与表情 |
|
|
198
|
+
| 打开设置面板宠物被放大 | **真 bug**:`layout()` 用了 `model.width`,而 Pixi 的 `Container.width` 返回的是**当前缩放后**的尺寸,于是每次重排都把缩放自乘一次 | 启动时缓存**未缩放**原始尺寸,之后一律由它计算;面板开合不再影响画面 |
|
|
199
|
+
| 待机太死板 | 没有随机行为 | 静置 12–26 秒后随机播一个非待机动作("摸鱼"),播完自动回待机;任何交互都会重置计时。**重锤出击与鲸鱼喷水不在摸鱼池里**——它们是「点头」和「出错」的专属反应,被随机播出来就像宠物在回应一件根本没发生的事(`FIDGET_DENY`,宠物可用 `motionOptions[group].fidget` 覆盖) |
|
|
200
|
+
|
|
201
|
+
### 缩小时的锐度(v1.2.1)
|
|
202
|
+
|
|
203
|
+

|
|
204
|
+
|
|
205
|
+
上图为 3 倍最近邻放大的面部区域,顺序是 **旧 160px | 新 160px | 旧 300px | 新 300px**。旧的渲染里发丝是断续的虚线状、轮廓边上有明显的方块感;新的渲染线条连续、边界干净。
|
|
206
|
+
|
|
207
|
+
三处改动:
|
|
208
|
+
|
|
209
|
+
| 项 | 旧 | 新 | 为什么 |
|
|
210
|
+
|---|---|---|---|
|
|
211
|
+
| 纹理采样 | `lod: "single-auto"` | `lod: "full"` | `"single-auto"` 只有 effectiveScale < 0.5 才生效;300px 宠物约 0.59,**这条分支从未触发**,等于只做双线性点采样。`"full"` 才会让资源加载器生成完整 mip 链(注意 `lod:false` 是"全分辨率但**不建** mip",比 `"single-auto"` 更差) |
|
|
212
|
+
| 各向异性过滤 | 无 | 各向异性 8x | 引擎不会把 `textureOptions.maxAnisotropy` 传给采样器,必须在加载后写到每张纹理的 style 上;它负责斜向线条(刘海、缎带边缘)在斜视时不糊成一片 |
|
|
213
|
+
| 渲染倍率 | `min(3, devicePixelRatio)`,1x 屏就是 1x | `min(3, max(2, devicePixelRatio))` | 这是最有效的一招:1x 屏上 300px 画布只有 300² 采样点,无论纹理怎么筛,输出就只有这么多样本。下限提到 2x 等于**超采样**(每个显示像素 4 个渲染样本),再由浏览器缩回 CSS 尺寸 |
|
|
214
|
+
|
|
215
|
+
实测(300px,面部细笔画像素占该区域的比例):**旧 8.16% → 仅建 mip 3.89% → mip + 2x 超采样 5.27%**。纯 mip 化会把细线"抹平"(数字反而比旧的低),所以两者必须一起上;超采样把细节拉回来,mip 链保证缩小时不出现摩尔纹和闪烁。
|
|
216
|
+
|
|
217
|
+
### 表情(v1.2.2)
|
|
218
|
+
|
|
219
|
+
44 个表情此前**全部加载失败**:`model3.json` 里指向的是中文文件名(`expressions/脸红.exp3.json`),而磁盘上按 manifest 路径校验的要求已经改成了 ASCII slug(`facial-red.exp3.json`)——生成器改过,但改完没有重新跑,装到 `%DSH_HOME%` 的那份是旧的。`build-pet.mjs` 现在会**保留手写文件**(`pet.json` / `catalog.json` / `README.md` / `voice.json`),重跑不会再把这些删掉,可以安全地反复执行。
|
|
220
|
+
|
|
221
|
+
还有一处命名不一致:`哭.exp3.json` 在 `model3.json` 里声明的 `Name` 是 **"大哭"**。表情查找按 `Name` 而非文件名匹配,所以 `failed` 相位原本写的 `"哭"` 永远查不到、静默什么都不做。现已修正为 `大哭`。
|
|
222
|
+
|
|
223
|
+
### 会话状态映射(v1.3 修正)
|
|
224
|
+
|
|
225
|
+
宿主把 DSH 的真实事件折叠成一个粗粒度相位并推送:
|
|
226
|
+
|
|
227
|
+
| 事件 | 相位 | 默认动作 | 默认表情 | 持续播放 |
|
|
228
|
+
|---|---|---|---|---|
|
|
229
|
+
| `agent/status` → running | thinking | 待机 | 呆呆眼 | 否(待机本身就在循环) |
|
|
230
|
+
| `approval/request` | waiting | 待机 | 问号 | 否 |
|
|
231
|
+
| **`tools/pre-execute`** | tool | 挤番茄酱 | 流汗 | **是** |
|
|
232
|
+
| `agent/turn-stopping` | done | 吹泡泡糖 | 情绪花花 | **是** |
|
|
233
|
+
| `agent/error` | failed | 鲸鱼喷水 | 大哭 | **是** |
|
|
234
|
+
|
|
235
|
+
> **为什么会话流之前「没接上」**:`tool/call` **不是** cordis 生命周期事件,而是写进会话记录(transcript)的**日志事件**——`ctx.on('tool/call')` 永远不会触发,所以工具活动对宠物完全不可见。真正的挂钩点是 `tools/*` 瀑布事件(`tools/pre-execute` / `tools/post-execute` / `tools/execute`),它们带着 `ToolExecution` 本身。瀑布事件**必须**调用 `next()`,否则会把整条链断掉,所以订阅器把它包在 try/catch 里、异常时也继续 `next()`。
|
|
236
|
+
|
|
237
|
+
**工具相位不抖动**:一轮对话里往往连着跑很多个工具。如果每次工具返回就立刻退回 `thinking`,相位会一秒翻好几次、动画跟着不停重启。所以退回是**防抖**的——只有在工具真的不再来了(`TOOL_IDLE_MS` = 1.2s 内没有新工具)之后才退回。
|
|
238
|
+
|
|
239
|
+
**持续播放**:相位是**状态**而不是一次性事件。`tool` / `done` / `failed` 会由控制器的 sustain 循环在动作播完后自动重播,直到相位改变——否则一个跑了 30 秒的工具调用只会看到 5 秒动画然后回到待机,看起来就像「没反应」。`thinking` / `waiting` 落在待机循环上,本身就在动,不重复触发(否则只会显得抽搐)。
|
|
240
|
+
|
|
241
|
+
可被宠物自己在 `pet.json` 的 `live2d.motions` / `live2d.expressions` 里覆盖(键就是这些相位名)。相位到达时若宠物正在演用户触发的反应,会**延后到回待机再补播**,不会丢掉状态;SSE 断开时会**解除 sustain**,不会永久卡在某个相位。
|
|
242
|
+
|
|
243
|
+
### 回到待机(v1.3)
|
|
244
|
+
|
|
245
|
+
要求是「所有动作、表情在播完一段时间后都要**完全**切回初始待机」。三件事共同保证:
|
|
246
|
+
|
|
247
|
+
| 情形 | 上限 | 说明 |
|
|
248
|
+
|---|---|---|
|
|
249
|
+
| 普通动作 | 动作自身 `Duration` | 播完即回待机循环 |
|
|
250
|
+
| `hold: true` 的定格 | `ACTION_HOLD_MAX_MS` = 9s | 定格**不是永久的**:先定格一会儿让人看清,然后交还身体并还原参数 |
|
|
251
|
+
| 手动点的表情 | `EXPRESSION_HOLD_MS` = 12s | 到点自动清除,面板的高亮也跟着消失 |
|
|
252
|
+
| 会话相位表情 | 跟相位同寿 | 相位离开时清除 |
|
|
253
|
+
|
|
254
|
+
`resetToRest()` 是唯一的收口:清 sustain、还参数、回待机循环。「归位」按钮走的就是它。
|
|
255
|
+
|
|
256
|
+
### 观测契约
|
|
257
|
+
|
|
258
|
+
宠物根节点上有三个属性,便于排查与自动化测试:
|
|
259
|
+
|
|
260
|
+
- `data-motion` — 当前动作组(待机为 `idle`)
|
|
261
|
+
- `data-gaze` — `center`(回默认位)/ `pointer`(跟随鼠标)
|
|
262
|
+
- `data-phase` — 最近一次会话相位
|
|
263
|
+
|
|
264
|
+
另外 `window.__dshLive2dPet` 暴露了动作控制器(`maskInfo()` 可查看点击轮廓、`playOnce()` / `playIdle()` 可手动驱动),方便在控制台排查。
|
|
265
|
+
|
|
266
|
+
### 关于 HitAreas
|
|
267
|
+
|
|
268
|
+
本模型**没有**声明 Cubism HitAreas,所以点击判定不依赖引擎的 `hitTest`,而是从渲染结果的 alpha 通道提取轮廓——因此任何模型都能用,无需作者额外导出命中区。
|
|
269
|
+
|
|
270
|
+
## 动作状态机
|
|
271
|
+
|
|
272
|
+
Live2D 的 `MotionManager` 有三处反直觉行为,直接裸调 `model.motion()` 会出现「点一下就一直循环播放」这类问题。插件用一个状态机统一接管动作生命周期:
|
|
273
|
+
|
|
274
|
+
| 引擎行为 | 后果 | 处理 |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| 同 group+index 正在播放时拒绝再次启动 | 连点没反应 / 只能播一次 | 每次启动前先 `stopAllMotions()` |
|
|
277
|
+
| `NORMAL` 优先级不能打断 `NORMAL` | 第一次动作后宠物「死」了,后续全被静默拒绝 | 互动与面板动作用 `FORCE`,待机用 `IDLE` |
|
|
278
|
+
| `motion()` 是**异步**的:先 `stopAllMotions()` 再加载入队,这中间 `MotionManager.update` 会看到 `playing && isFinished()` 并**误发 `motionFinish`** | 新动作刚开始就被判定「播完了」,瞬间弹回待机 | 忽略启动后 250ms 内到达的 `motionFinish` |
|
|
279
|
+
| 动作自带 `"Loop": true` 时永不结束,也就永不触发 `motionFinish` | 动作无限循环,回不到待机 | 宿主从 `motion3.json` 读出 `Duration`/`Loop`,按声明的时长定时收尾 |
|
|
280
|
+
|
|
281
|
+
因此状态机只做三件事:**常驻待机循环** → **播一次动作** → **自动回待机**,且每一步都可被打断。
|
|
282
|
+
|
|
283
|
+
宿主半区会把每个动作的 `duration`(毫秒)、`loop` 以及**该动作写了哪些参数**(来自模型自己的 `motion3.json`)随 catalog 下发,所以换任何模型都能自适应,不需要改插件代码。
|
|
284
|
+
|
|
285
|
+
### 动作语义(v1.2.2)
|
|
286
|
+
|
|
287
|
+
引擎还有一条更隐蔽的行为,是「吹泡泡吹完嘴不还原」的根因:
|
|
288
|
+
|
|
289
|
+
> **动作结束后,它写过的参数没有任何人负责还原。** 引擎在动作播放期间往模型参数里写值,停下就只是「不写了」——参数留在最后一帧的值上。平时看不出问题,是因为待机循环恰好也在驱动这些参数;而本模型的动作专属参数(`chuipaopao*`、`phone*`、`pengshui`…)**待机完全不碰**,于是动作一停,最后的嘴形就永久留在脸上。
|
|
290
|
+
|
|
291
|
+
插件现在的做法:动作启动前把它会写的参数**快照**下来,回待机时**还原**。参数名单由宿主从 `motion3.json` 的 `Curves` 里读出并下发。
|
|
292
|
+
|
|
293
|
+
这条「还原」还藏过两个更深的坑,都在这条缝上:
|
|
294
|
+
|
|
295
|
+
1. **引擎的一帧是 `saveParameters()` → `update()` → `loadParameters()`,`loadParameters()` 是最后一个。**
|
|
296
|
+
我们的图层(表情 / 嘴 / 眨眼 / 扫动画 / 动作还原)写在 `saveParameters()` 之后,`update()` 把当时的值烘进模型画出去,
|
|
297
|
+
然后帧尾的 `loadParameters()` 把引擎自己的基线整片盖回来。所以:
|
|
298
|
+
- 还原**不能**「把快照写回参数」——那是帧外写,会被帧尾的 `loadParameters()` 抹掉,看起来就是「切回无也切不回去」。
|
|
299
|
+
它现在是一层**每帧覆盖**。
|
|
300
|
+
- **帧外读参数永远拿到"图层之前"的值**:动作停了它还是 1、表情明明生效却是 0。
|
|
301
|
+
快照若从那里取,第二次吹泡泡糖就会忠实还原成「鼓着的嘴」。快照现在读**还原缝上的值**(还装着就取还装的值,否则取引擎的值)。
|
|
302
|
+
2. **要断言"画面里是什么",只能读 `window.__dshLive2dPet.drawn(id)`**(控制器在钩子里存下的、这一帧真正画出去的值),
|
|
303
|
+
或者自己包一层 `core.update`。测试里读帧外的 `_model.parameters.values` 是量错了地方——这个错让同一个 bug 骗过两次。
|
|
304
|
+
|
|
305
|
+
另外全部动作现在都以 `loop: false` 启动。引擎的合并方式是 `setLoop(调用方的 loop ?? 动作自带的 Meta.Loop)`,而本包**所有** `motion3.json` 都写着 `"Loop": true`,所以不显式传 `false` 的话动作永远不结束,也就永远摆不出「定格」姿势。
|
|
306
|
+
|
|
307
|
+
剩下三件事是模型作者才知道的意图,写在 `pet.json` 的 `live2d.motionOptions` 里:
|
|
308
|
+
|
|
309
|
+
| 声明 | 含义 | 解决的问题 |
|
|
310
|
+
|---|---|---|
|
|
311
|
+
| `{"hold": true}` | 动作播完**定格**在最后一帧;最多 `ACTION_HOLD_MAX_MS`(9s)后交还身体 | 掏出手机后手机能拿在手里看一会儿,但不会永远举着 |
|
|
312
|
+
| `{"fidget": false}` | 不参与随机摸鱼 | 把手交互类动作排除出摸鱼池 |
|
|
313
|
+
| `{"prepend": "OpenCase"}` | 先播前置动作,再播真正的动作 | 自拍的 `phone` 第一帧就是 1(作者假定手机已在手),不先掏手机就是在对着空气自拍 |
|
|
314
|
+
| `{"preset": {"jingyu": 1}}` | 动作本身没写、但这个动作需要被一起点亮的参数 | 鲸鱼喷水这个动作只写了 `pengshui`(碰水),真正负责「喷」的鲸鱼是另一个参数 `jingyu`,不点它看起来就是毫无反应 |
|
|
315
|
+
|
|
316
|
+
定格姿势**不算「忙」**(`isPlaying()` 返回 false):否则点一次掏出手机就会永久压住待机摸鱼和会话相位,宠物就此卡死。它只是「看起来不一样的待机」,任何新动作都能接管。
|
|
317
|
+
|
|
318
|
+
宠物根节点带 `data-motion` 属性(待机时为 `idle`),方便直接观察当前状态。
|
|
319
|
+
|
|
320
|
+
## 宠物契约
|
|
321
|
+
|
|
322
|
+
一个宠物目录长这样:
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
%DSH_HOME%\pets\<id>\
|
|
326
|
+
pet.json # 清单(renderer: live2d)
|
|
327
|
+
c_0120.model3.json
|
|
328
|
+
model\ # .moc3 / physics3 / cdi3
|
|
329
|
+
textures\ # 贴图
|
|
330
|
+
motions\ # .motion3.json
|
|
331
|
+
expressions\ # .exp3.json
|
|
332
|
+
catalog.json # 可选:动作/表情的中文名与分类
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
`pet.json`:
|
|
336
|
+
|
|
337
|
+
```jsonc
|
|
338
|
+
{
|
|
339
|
+
"petManifestVersion": 2,
|
|
340
|
+
"id": "ds-whale-girl",
|
|
341
|
+
"displayName": "DS鲸鱼娘",
|
|
342
|
+
"renderer": "live2d",
|
|
343
|
+
"license": "...", // 资产授权声明
|
|
344
|
+
"live2d": {
|
|
345
|
+
"model": "c_0120.model3.json", // 相对于本目录
|
|
346
|
+
"scale": 1, // 在自适应缩放上乘算
|
|
347
|
+
"translate": { "x": 0, "y": 0 }, // 像素偏移
|
|
348
|
+
"motions": { "idle": "Idle" }, // 可选;插件主要用模型自带列表
|
|
349
|
+
"expressions": { "idle": "脸红" },
|
|
350
|
+
// 可选;模型自己表达不了的「作者意图」,见《动作语义》
|
|
351
|
+
"motionOptions": {
|
|
352
|
+
"OpenCase": { "hold": true },
|
|
353
|
+
"Selfie": { "prepend": "OpenCase" },
|
|
354
|
+
"SprayWater": { "preset": { "jingyu": 1 } }
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
`motionOptions` 的三个键都可以组合;不写就是默认行为(播一次然后回待机)。`prepend` 的前置动作同样受该动作自己的 `motionOptions` 约束。
|
|
361
|
+
|
|
362
|
+
`catalog.json`(可选,只影响显示名):
|
|
363
|
+
|
|
364
|
+
```jsonc
|
|
365
|
+
{
|
|
366
|
+
"motions": [{ "key": "Hammer", "label": "重锤出击", "category": "action" }],
|
|
367
|
+
"expressions": [{ "key": "脸红", "label": "脸红", "category": "emotion" }]
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**动作和表情列表以 `c_0120.model3.json` 里声明的为准**,插件启动时从模型读出,所以换模型 / 改模型文件立刻生效,不用改插件代码。
|
|
372
|
+
|
|
373
|
+
## 宿主 HTTP 接口
|
|
374
|
+
|
|
375
|
+
| 方法 | 路径 | 说明 |
|
|
376
|
+
|---|---|---|
|
|
377
|
+
| GET | `/api/live2d-pet/catalog` | 已安装宠物 + 各自的动作/表情清单 + 运行时 URL |
|
|
378
|
+
| GET | `/api/live2d-pet/asset/<id>/<path>` | 只服务 `model3.json` **引用闭包**内的文件(白名单 Set 比对 + realpath 包含,`..` 永远匹配不上) |
|
|
379
|
+
| GET | `/api/live2d-pet/runtime/live2dcubismcore.min.js` | 用户自备的 Cubism Core |
|
|
380
|
+
| GET | `/api/live2d-pet/runtime/live2d-vendor.js` | 插件内置的 MIT vendor 分包(pixi.js + 引擎),按需懒加载 |
|
|
381
|
+
|
|
382
|
+
API 与资产路由默认只答本机回环请求。
|
|
383
|
+
|
|
384
|
+
## 架构
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
dsh-live2d-pet/
|
|
388
|
+
package.json dsh.client.platform = web -> 双半区包
|
|
389
|
+
cordis.patch.yml bundle patch:插一行 live2d-pet
|
|
390
|
+
lib/
|
|
391
|
+
index.js 宿主半区:宠物发现 / 引用闭包资产路由 / 运行时分发
|
|
392
|
+
client.js 浏览器半区:手写 __ModuleLoader__ 工厂,无构建步骤
|
|
393
|
+
live2d-vendor.js pixi.js + untitled-pixi-live2d-engine 的 IIFE(esbuild 产物)
|
|
394
|
+
src/vendor-entry.ts vendor 分包入口(npm run build:vendor 重新生成)
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Vendor 分包**懒加载**:只有真正挂载 Live2D 宠物时才注入 `live2d-vendor.js`,页面首屏不为它买单。
|
|
398
|
+
|
|
399
|
+
## 二次开发
|
|
400
|
+
|
|
401
|
+
```pwsh
|
|
402
|
+
npm install # pixi.js / untitled-pixi-live2d-engine / esbuild
|
|
403
|
+
npm run build:vendor # 重新生成 lib/live2d-vendor.js
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
改完 `lib/client.js` 后**重启 `dsh web`**(bundle 不做热重载)。
|
|
407
|
+
|
|
408
|
+
回归测试在仓库的 `tools/browser-test/`:无头 Edge + CDP,在真实 WebGL 里跑完整契约。
|
|
409
|
+
```bash
|
|
410
|
+
cd ../../tools/browser-test && npm install && npm run suite
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## 许可
|
|
414
|
+
|
|
415
|
+
- 插件代码:MIT
|
|
416
|
+
- vendor 分包:pixi.js(MIT)+ untitled-pixi-live2d-engine(MIT),可随包分发
|
|
417
|
+
- Cubism Core:Live2D 专有,**用户自备,本插件不内置**
|
|
418
|
+
- DS鲸鱼娘模型:**CC BY-NC-SA 4.0**(署名 · **非商业** · 相同方式共享),见
|
|
419
|
+
[`pets/ds-whale-girl/LICENSE`](pets/ds-whale-girl/LICENSE)。
|
|
420
|
+
|
|
421
|
+
版权链:**上善无形**(鲸鱼娘角色原作,原创 OC「溟月」)→ **ZipZipPipe**(DeepSeek 女仆二创)
|
|
422
|
+
→ **氵六青**(本模型)。氵六青已授权本项目转载开源,但该授权**不解除基础版权**,
|
|
423
|
+
所以 **NC / SA 依然有效**;商业使用需分别取得三人授权。
|
|
424
|
+
|
|
425
|
+
完整说明见 [`../NOTICE.md`](../NOTICE.md)。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# dsh-live2d-pet bundle patch: inserts its plugin row into the web plugin
|
|
2
|
+
# roster. One row mounts both halves — the host side (Live2D pet discovery,
|
|
3
|
+
# the model reference-closure asset route, the runtime route) and the browser
|
|
4
|
+
# side (the floating Live2D pet surface, served from this package's
|
|
5
|
+
# lib/client.js).
|
|
6
|
+
- insert:
|
|
7
|
+
- id: live2d-pet
|
|
8
|
+
name: dsh-pet-live2d
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# 截图
|
|
2
|
+
|
|
3
|
+
这个目录用于放插件截图。目前**空着**是刻意的:
|
|
4
|
+
|
|
5
|
+
插件自带的示例模型是 DS鲸鱼娘,属于第三方美术资产,以 **CC BY-NC-SA 4.0** 授权
|
|
6
|
+
(署名 · 非商业 · 相同方式共享),版权链为 上善无形 → ZipZipPipe → 氵六青。
|
|
7
|
+
模型与截图都已随仓库分发;**这些图片同样受 CC BY-NC-SA 4.0 约束,不是 MIT**。
|
|
8
|
+
|
|
9
|
+
这里的图片:
|
|
10
|
+
|
|
11
|
+
- `preview.png` — 桌宠与面板的整体截图(300px 宽,建议跑 `tools/browser-test/cdp-final.mjs` 生成)
|
|
12
|
+
- `downscale-fix.png` — 缩小时线条清晰度的前后对比
|
|
13
|
+
|
|
14
|
+
当前这些图片保存在仓库之外的 `local-assets/screenshots/`,已在 `.gitignore` 中排除。
|
|
Binary file
|
package/docs/preview.png
ADDED
|
Binary file
|