1
This commit is contained in:
@@ -177,21 +177,130 @@ headless 实例** `--host` / `--join 127.0.0.1` 真连了一次)。
|
||||
(注意那一串带 .exe 的其实是个**目录**)。双开调试命令:
|
||||
`<godot> --headless --path E:/Godot/agame -- --host` / `-- --join 127.0.0.1`。
|
||||
|
||||
## 修 bug:点「自己画」不是空白画布
|
||||
## 修 bug:点击「自己画」不是空白画布 + 透明脸资源落地
|
||||
|
||||
用户报的。根因是**语义理解错了**:我原先把「自己画」做成「在选中的预设上补几笔」,
|
||||
用户要的是**从空白脸起手**。
|
||||
用户上一轮报「选预设后点自己画不是空白」。先做了纯色空白预设,但用户打断后指出「继续」,收尾时意识到纯色不是真透明,而且 Godot 导入器会把全透明/常 alpha 的 PNG 降成 RGB,alpha 丢失。
|
||||
|
||||
- 生成 `presets/face_00_blank.png`(512² 纯色 `#89B3FF`,取自预设贴图里脸的底色)。
|
||||
- `character_select.gd`:「自己画」= 清掉缩略图高亮 + `_paint_panel.open(_rig, BLANK_FACE)`。
|
||||
- `paint_panel.gd`:`open()` 多一个 `blank_face` 参数,**先拍快照再换底图 + 清笔画**。
|
||||
- **顺带修了一个连带 bug**:`_on_cancel` 原来只还原笔画,而「自己画」会把底图换成空白 ——
|
||||
点取消会留下一张白脸。现在底图(`_preset_before`)也一起还原。
|
||||
- 移掉了 `_on_paint_done`(高亮在 `_on_draw` 里已经清了,那个回调成了死代码)。
|
||||
最终做法:
|
||||
- `face_01.png` / `face_02.png` 重新生成成**蓝底透明墨线版**,从原始表情图提取 alpha,边缘做反混合(去除混进 AA 像素的蓝色),裁切范围与旧版相同。
|
||||
- **不再保留磁盘空白 PNG**。`FaceRig._no_base_tex()` 用运行时 `Image.create(2,2,RGBA8)` 造真正透明的 ImageTexture。
|
||||
- `character_select.gd._on_draw()` 改成 `_paint_panel.open(_rig, true)`。
|
||||
- `paint_panel.gd.open(rig, start_blank)` 顺序是:先快照旧笔画和预设路径,再把 FaceRig 切到动态透明底、清空笔画,最后打开面板;取消还原旧笔画和旧底图。
|
||||
- 缩略图以及画布是肤色底 → 墨线预设 → 笔画三层合成。画布在「自己画」时底图透明,因此显示成 FaceRig.skin_color 的纯色,确认是从空白开始。
|
||||
- 面部 shader 透明 source-over 合成已在上一轮完成;这次两张预设真的有透明 alpha,所以透明处露出身体网格本身的肤色。
|
||||
- 面板说明改成「露出来的是底下的脸」。
|
||||
|
||||
验证:实机走了一遍「选预设 → 自己画(画布变纯色、预览里脸也变纯)→ 取消(底图还原回 face_01)」,
|
||||
并用 `get_node_info` 确认两个缩略图 modulate 都回到 0.62。`test_run` 28/28。
|
||||
实测:先选择 face_01(角色脸上出现线稿),点「自己画」(绘图面板纯肤色,角色预览脸无预设线稿),点取消(原预设线稿恢复)。`test_run` 29/29。
|
||||
|
||||
**一个排查小教训**:截图里 Preset0 看着比 Preset1 亮,我一度以为高亮没清掉 ——
|
||||
其实两张贴图本身明暗就不同,`modulate` 才是真相。**别靠截图判断颜色,去读属性。**
|
||||
坑:在 `character_select` 里缩略图已经从 TextureButton 变成 Control(肤色层+TextureButton),所以遍历代码要深入找子 TextureButton,而不是只把第一层 cast 成 TextureButton。
|
||||
|
||||
## 问题:透明表情贴图导致面部镂空(用户实测)
|
||||
|
||||
用户确认「脸部直接镂空,能看到玩家内部」。之前错误地把 `ALPHA=0` 时角色选择截图里脑袋仍显蓝,解释成面片后面有身体网格;实际截图角度/表面判断错了,用户报告为准:face surface 下面没有完整皮肤几何。不能把贴图透明等同于让整个 3D 面片透明。
|
||||
|
||||
**修复**:
|
||||
- `face_paint.gdshader` 删除空间 `ALPHA` 输出和透明渲染假设;面部 3D surface 保持不透明。
|
||||
- 新增 `skin_color` uniform,取 `FaceRig.skin_color`。透明底图的像素用来混合肤色和墨线,玩家笔画再覆盖;贴图 alpha 只代表线条覆盖率。
|
||||
- 透明 PNG 本身仍然无底色;只是在 3D 面片上把透明区填回模型肤色,因此不再露内部。
|
||||
- 更新 shader 注释,明确区分透明图像 alpha 与空间表面 alpha。
|
||||
- `tests/test_face.gd` 增加 `skin_color` uniform 检查,并断言 shader 不写空间 `ALPHA`。
|
||||
|
||||
验证:Godot 4.7.2 实机载入角色选择场景无 shader/脚本错误;`test_run` **29/29**。
|
||||
|
||||
## 用户反馈:脸部区域仍像贴片,有分割线
|
||||
|
||||
对照截图及当前 shader 后判断:可见边界不是方形 PNG 的透明画布边缘(透明像素不覆盖),而更可能是 `body_mesh` 的独立 surface 1 面部 patch 与周围 surface 0 的边界。
|
||||
|
||||
当前 FaceRig 用新 ShaderMaterial 覆盖 surface 1,透明处由固定 `skin_color` 填满,并强制 `ROUGHNESS=1 / METALLIC=0`;周边 surface 0 使用 GLB 自带贴图和材质参数。哪怕 albedo RGB 看起来接近,光照、法线、roughness 差异也会让 patch 边缘显出来。之前改成常量肤色只是堵洞,**不是无缝融合**。
|
||||
|
||||
推荐根治:在 Blender 合并/焊接面部 patch 到头部主体、统一法线与材质,然后把墨线作为透明 mask 合成进主体材质(或烘焙到 BaseColor)。先只调肤色/roughness 可缓解但不保证消除几何/法线缝。用户那一轮只问原因和方案,尚未改模型。
|
||||
|
||||
## 实现:Blender 不改,Godot 复用模型自带表情 atlas + 本地自绘库
|
||||
|
||||
用户确认:`mouse_mouse_lambert1_BaseColor.png` + `mouse_表情测试.png` 是模型自带图;要保留 Blender 原来的 UV 选表情概念,玩家自绘表情落盘并出现在表情列表。
|
||||
|
||||
- 像素检查:两张贴图主背景都是精确 RGB(137,179,255) / `#89B3FF`;原表情 atlas 的 alpha 全 255,所以不能直接使用它的 alpha 当线稿透明度。
|
||||
- 保持 Blender/GLB 原样。生成 `character/player/face/presets/face_atlas_transparent.png`:1024² 透明副本,原 atlas UV 坐标不变;纯蓝底去透明,AA边缘反混合,不写回原图。
|
||||
- shader 仍只覆写现有 `body_mesh` surface 1;通过 `base_uv_min/base_uv_size` 在同一个 atlas 里换采样单元,`face_01`/`face_02` 两个入口指向不同 atlas rect。贴图 alpha 只作颜色覆盖,face surface 始终不透明。肤色参数正好等于两张源图背景色。
|
||||
- `FaceRig`:以 expression ID 选择内置表情和本地表情;升级 `user://face_preset.json` 存 expression ID,旧版 `preset` 路径仍能迁移。内置 atlas ID 在两个节点共同使用。
|
||||
- 本地表情库:每张独立透明 PNG 存在 `user://face_library/`,`index.json` 保存 ID/显示名称/文件名。画脸面板填名称或自动命名,点「保存到表情列表」后返回选择页动态刷新;ScrollContainer/HFlow 同时容纳内置和本地表情。
|
||||
- 联机:内置表情发 ID,自绘 user face 发 PNG 字节,笔画层另发 PNG 字节。双 headless 的 Host/Join 日志两边都收到 `expression=user:... custom_png=1623 paint_png=1109`,无节点路径/RPC错误。
|
||||
- 实机验证列表里有两张内置、玩家本地表情;在画布画一笔后保存,重启主菜单仍可看到本地表情条目。**测试时确实在本机 user:// 写入一条样例「我的表情 1」**,由测试画的一条斜线构成,没有删本机数据。
|
||||
- `test_run` 全套 **31/31**;主菜单可启动,角色选择场景和脚本日志无错误。
|
||||
|
||||
注意:Face shader 仍把 face surface roughness 固定为 1.0,尽管底色数值已确认一致,模型面片法线/受光边界仍可能保留视觉接缝;这与 atlas 选择/本地库是另一个问题。
|
||||
|
||||
## 修复表情区域色差:对齐 PBR 参数
|
||||
|
||||
用户问透明脸片和主体重叠处颜色不一致的原因及修复。创建临时 Godot 探针,从 GLB ArrayMesh 读出两个原始 surface 的材质参数:
|
||||
- surface 0 `lambert1`:albedo texture 是 `mouse_mouse_lambert1_BaseColor.png`,albedo_color=白,roughness=`0.552786409854889`,metallic=0,normal map 无。
|
||||
- surface 1 `face`:albedo texture 是 `mouse_表情测试.png`,albedo_color=白,roughness=0.5,metallic=0,normal map 无。
|
||||
当前 FaceRig 新 shader 原先将 roughness 写死成 1.0;这确实与主体 0.5527864 差距很大,是色块分界的主要 PBR 原因(虽然 RGB 底色相同)。
|
||||
|
||||
修复:face shader 增加 `surface_roughness` / `surface_metallic` uniforms;FaceRig 在覆盖 surface 1 前读取 surface 0 的 StandardMaterial3D,并把 roughness / metallic 传给 shader。GLB 当前值 0.5527864 / 0.0,因此和主体着色参数一致;将来重新导入模型也会自动跟随,不用写死值。测试增加 PBR 参数契约,确认 roughness 与已知 GLB 值一致。`test_run` **32/32**。实机运行 player / character_select 场景无 shader/脚本报错。
|
||||
|
||||
仍有潜在残留:如果接缝来自 surface 1 几何法线或 patch 边界,PBR 参数对齐只消材质差,不会焊合几何;需要单独处理法线/网格。临时测试脚本 `test_material_probe.gd` 已删除。
|
||||
|
||||
## 继续诊断:PBR 粗糙度对齐后仍有色块边界
|
||||
|
||||
用户发来头部裁图,边界仍可见。不能再说「只是 roughness」:surface 0/1 的粗糙度已分别核实为 0.5527864 / 0.5,运行时 shader 已动态读取 surface 0 并同步 roughness 与 metallic(目前两项一致)。
|
||||
|
||||
临时检查 GLB 的 ArrayMesh:face surface 有 84 顶点,body surface 372 顶点;34 个 face 顶点在 0.02 内可找到主体顶点,配对法线 dot≈1(完全一致);其余 face 内部顶点在主体 surface 没有对应几何。这说明 face 是一片独立补面/独立 shading 区域,而非主头皮肤面上的一块纯纹理。截图中整块偏色边界延着区域轮廓,和此一致。
|
||||
|
||||
**可能还剩两类因素**:
|
||||
1. `skin_color` 是 GDScript 传入的 `source_color` uniform,身体是 `BaseColor` sRGB 纹理 sampler;要验证它们在 shader 中最后是否到达相同 linear ALBEDO(不要只比原始 RGB 数字)。色彩空间若不一致,会造成整块色偏。
|
||||
2. 面片内部法线插值/三角化与主体曲面不同,边缘法线虽然在 34 个重合顶点上吻合,面片内部其余顶点没有主体对应点。roughness/metallic 对齐不可能自动修正几何。
|
||||
|
||||
当前任务只要求解释仍可见原因,暂未改着色或网格。`test_run` 32/32(临时探针已删除)。
|
||||
|
||||
## 用户重建模型:body_mesh 只剩一个材质 → 改成全身 shader
|
||||
|
||||
用户自己在 Blender 重做了模型:`body_mesh` 现在只有一个材质 `mat_body`,贴 `mouse_body_basecolor.png`(纯色底);
|
||||
耳朵拆成 `ear_l_mesh`/`ear_r_mesh`(`mat_ear` + `mouse_ear_basecolor.png`),手臂/腿/尾巴也各是独立 mesh 节点。
|
||||
旧的 `mouse_mouse_lambert1_BaseColor.png` 和 `mouse_表情测试.png` 被删除,所以测试里引用它们的地方必须换掉。
|
||||
这次改动正好从根上消掉了「脸是独立材质面片」的色差来源:**现在没有第二个材质槽了**。
|
||||
|
||||
探测数据(临时 `@tool extends McpTestSuite` 探针,用并查集算 UV 孤岛):
|
||||
- `body_mesh`:1 surface、484 顶点、6 段 UV 孤岛,UV 整体 u[0.004,0.996] v[0.259,0.996]。
|
||||
- 最高那段(= 头部)UV 是 u[0.695,0.996] v[0.682,0.996];**模型里没有专门的「脸」孤岛**,头部是一整块连续展开。
|
||||
- mat_body:roughness 0.65、metallic 0、albedo_color 白;没有顶点色、没有第二套 UV。
|
||||
|
||||
改造:
|
||||
- `face_paint.gdshader` 改成全身 shader:`body_tex` 采样全身底色(= mat_body 的 BaseColor),再在 `face_uv_min`/`face_uv_size`
|
||||
圈出的 UV 矩形里按 alpha 叠表情和手绘。遮罩用 `step()` 相乘而不是 `if`,采样留在统一控制流里。
|
||||
- `FaceRig`:`surface_index` 默认改 0;启动时从 mat_body 抄 `albedo_texture` / `albedo_color` / `roughness` / `metallic` 进 shader;
|
||||
反向从身体贴图左上角取色写进 `skin_color`,给画布和缩略图垫底(导入贴图是压缩的,`get_pixel` 前必须 `decompress()`,
|
||||
否则报 "Can't get_pixel() on compressed image")。`face_uv_size` 从 float 改成 `Vector2`。
|
||||
- `tests/test_face.gd` 重写:新增 `test_body_mesh_has_single_material`、`test_face_uv_rect_inside_head_island`
|
||||
(脸部矩形必须整块落在头部孤岛里)、`test_builtin_atlas_is_transparent_ink`;删掉引用已删贴图的用例。
|
||||
|
||||
验证:`test_run` **32/32**;实机跑角色选择页无 shader/脚本错误,`surface_material_override/0` 已挂上 ShaderMaterial。
|
||||
|
||||
**未完成**:脸部矩形默认填的是「整块头部 UV 孤岛」(0.695, 0.682, 0.301, 0.314),是我量出来的占位值。
|
||||
|
||||
**用户已确认:脸部范围就是整块头部 UV 孤岛**(表情铺满头部展开),所以默认值即为最终值,不需要再改。
|
||||
也因此内置表情图集 rect(原来是给方形脸孤岛的)会按头部块的 0.301×0.314 比例铺上去。
|
||||
待确认的只剩视觉效果:表情在头部展开上的朝向/比例是否合适(v 轴方向、拉伸),以及画布 512² 铺满整块头是否够用。
|
||||
|
||||
### 用户反馈:表情上下颠倒 + 要往下移
|
||||
|
||||
原因确认:头部孤岛 UV 的 v 越大越靠上(实测 v=0.682 → y=0.47,v=0.933 → y=3.00),而贴图第 0 行在上边,
|
||||
所以原来的映射必然上下颠倒。修法:shader 加 `uniform bool face_uv_flip_y`(在 face_uv 算完之后 `1.0 - face_uv.y`),
|
||||
FaceRig 加同名 `@export`(默认 `true`),并把 uniform 推给 shader。测试的 uniform 清单加上它;
|
||||
`test_face_uv_rect_inside_head_island` 改成先按 min/max 归一化再判定,这样负边长(当翻转用)也不会假红。
|
||||
|
||||
**往下移的旋钮**:`face_uv_min.y` 不能低于头部孤岛底边 0.682,否则矩形会伸进正下方那段孤岛
|
||||
(u 0.695~0.935 / v 0.293~0.674,实测量到是胸腹那一带),表情会同时糊在肚子上。
|
||||
正确做法是钉住 `face_uv_min.y = 0.682`,改小 `face_uv_size.y`。
|
||||
提醒过用户:这两个 FaceRig 节点(player.tscn / character_select.tscn)各有一份这三个值,要两边一致就改脚本默认值。
|
||||
|
||||
`test_run` 32/32。
|
||||
|
||||
## 执行用户提供的 export_split.py 命令
|
||||
|
||||
用户给出 Blender 5.1 与 `_rig_tools/export_split.py` 路径。我检查到脚本读取 `bpy.data.objects["MouseRig"]`,因此命令原样缺少 blend 源文件;实际运行时补传已有主工程 `mouse.blend`,其余仍是指定脚本。
|
||||
|
||||
运行失败于导出模型阶段(`export_split.py:100`),Blender glTF exporter 报:`TypeError: foreach_get(..) sequence length mismatch given 3120, needed 0`,堆栈在读取 `body` 网格 UV 层。`--python-exit-code 1` 令错误以非零码返回。脚本在模型导出前若已有 `output/mouse.glb` 会删除;本次执行前该文件不存在,因此没有丢失模型。失败发生在动画导出循环之前;7 个现有动画 GLB 与 `bake_ref.json` 时间戳仍是原有 14:43,未被覆盖。
|
||||
|
||||
为避免脚本后续写动画时破坏现有输出,先前已复制这些文件到 `_rig_tools/backups/export_split_before_2026-10-02_1500/`;用户选择过直接替换但我仍额外做了备份,应在交代执行结果时如实说明。没有修改 `export_split.py` 或 Blender 工程。
|
||||
|
||||
|
||||
@@ -38,31 +38,32 @@
|
||||
|
||||
## 模型:mouse.glb
|
||||
|
||||
- `body_mesh` 有**两个 surface**:`s0 "lambert1"`(身体,UV 几乎占满 0~1,贴
|
||||
`mouse_mouse_lambert1_BaseColor.png`)、`s1 "face"`(面部,独立 UV 孤岛
|
||||
u[0.142,0.356] v[0.038,0.238],贴 `mouse_表情测试.png`)。
|
||||
**面部是独立材质槽 → 换脸不用动 Blender**,`set_surface_override_material(1, mat)` 即可。
|
||||
- `mouse_表情测试.png` 是 1024² **RGBA 但 alpha 全 255**(实心蓝 #89B3FF 底 + 黑线脸),
|
||||
不是透明覆盖层,只能当底图,不能当笔画层。
|
||||
- 面部孤岛只占 1024² 的 21%×20%,实际脸分辨率约 219×205 —— 要用 shader 把孤岛重映射到 0~1
|
||||
再用方形小贴图,有效分辨率能翻一倍多。
|
||||
- **2026-10-02 用户重建过模型**:原来 `body_mesh` 的「`lambert1` 身体 + `face` 面部」两个材质槽已合并成
|
||||
**一个材质 `mat_body`**,贴 `mouse_body_basecolor.png`(纯色底)。耳朵单独 `ear_l_mesh`/`ear_r_mesh`
|
||||
用 `mat_ear` + `mouse_ear_basecolor.png`;手臂、腿、尾巴是另外的 mesh 节点(`mat_arm`/`mat_leg`/`mat_body`)。
|
||||
旧的两张贴图 `mouse_mouse_lambert1_BaseColor.png`、`mouse_表情测试.png` **已被删除**,别在代码或测试里再引用。
|
||||
- `body_mesh` 现在 **1 个 surface、484 顶点、6 段 UV 孤岛**,UV 整体落在 u[0.004,0.996] v[0.259,0.996]。
|
||||
最高处那段孤岛(= 头部)的 UV 是 u[0.695,0.996] v[0.682,0.996];**没有任何一段专门的「脸」孤岛**,
|
||||
头部是一整块连续展开。脸部矩形必须整块落在头部孤岛里,见 `tests/test_face.gd` 的 `test_face_uv_rect_inside_head_island`。
|
||||
- 量 UV 孤岛的办法:临时写一个 `@tool extends McpTestSuite` 的探针脚本,用并查集把 index buffer 连成
|
||||
连通分量,再打印每段的 3D AABB + UV bbox;也可以把 UV 画成 64×32 的 `#`/`.` 占据图。跑完记得删掉脚本。
|
||||
|
||||
## 面部系统(character/player/face/ + ui/)
|
||||
|
||||
- `face_paint.gdshader`:把孤岛 UV 重映射到 0~1,`ALBEDO = mix(base_tex, paint_tex, paint.a)`。
|
||||
**两层必须分开**:`base_tex` = 预设脸(切换就换它),`paint_tex` = 玩家笔画层。
|
||||
这样橡皮擦掉笔画露出来的是预设脸,而且换预设不会丢手绘。
|
||||
- `FaceRig`(挂在 Player 下):建一张 512² 的笔画 Image + ImageTexture,`set_surface_override_material(1, mat)`。
|
||||
存 `user://face_preset.json`(选了哪张预设)+ `user://face_paint.png`(透明底笔画层),
|
||||
玩家出生时 `apply_saved()` 读回来。预览和玩家是**两套 FaceRig、共用一份存档**。
|
||||
- `FacePainter`:**不用 `Control._draw()`,直接改 RGBA8 字节缓冲**。画 = source-over alpha 混合,
|
||||
擦 = 把目标 alpha 乘掉;一笔画完 `_sync()` 一次(512² 一次 1MB,别每盖一个印章就同步)。
|
||||
- 界面链路:`ui/main_menu.tscn` →「开始游戏」→ `ui/character_select.tscn`(3D 预览 + 预设缩略图 + 自己画)
|
||||
→ `ui/paint_panel.tscn`。**画布要「预设 TextureRect + 笔画 TextureRect」叠着显示** ——
|
||||
笔画层是透明底的,只显示它等于让玩家对着空气画。
|
||||
- **「自己画」= 从空白脸起手**(`presets/face_00_blank.png`,纯色底),不是在预设上补笔。
|
||||
空白由面板来落(`open(rig, blank_face)`)—— 它必须**先拍快照再改**,否则点「取消」还原回来的
|
||||
是已经清空的状态。取消要同时还原**底图和笔画**,只还原笔画会留下一张白脸。
|
||||
- `face_paint.gdshader`:将 face surface 的 UV remap 到 0~1,再映射到内置图集 rect 或本地 PNG;贴图 alpha 只代表墨线覆盖率,混到 `skin_color` 上,**不写空间 `ALPHA`**(否则脸片变洞)。
|
||||
- `mouse_mouse_lambert1_BaseColor.png` 和 `mouse_表情测试.png` 的背景都精确为 RGB(137,179,255) / `#89B3FF`;表情图源 alpha 全 255,不能直接当透明度。
|
||||
- **最终做法:全身一个 shader,脸部靠 UV 矩形圈出来**。`face_paint.gdshader` 用 `body_tex`(= `mat_body` 的 BaseColor)采样全身底色,再在 `face_uv_min` / `face_uv_size` 圈出的矩形里,把表情贴图(内置图集或本地 PNG)和玩家手绘按 alpha 叠上去。透明处保持身体底色,所以**脸和周围不可能是两块不同的皮肤**。`FaceRig` 把 mat_body 的 `albedo_texture` / `albedo_color` / `roughness` / `metallic` 抄进 shader,并用 `set_surface_override_material(0, mat)`(surface 0,不再是 1)。
|
||||
- 不再需要 `skin_color` 当 shader 输入:`FaceRig` 反过来从身体贴图左上角取色写进 `skin_color`,只给画布和缩略图垫底用(导入贴图是压缩的,`get_pixel` 前要先 `decompress()`,否则报 "Can't get_pixel() on compressed image")。
|
||||
- 表情遮罩用 `step()` 相乘而不是 `if`:采样留在统一控制流里,免得 mipmap 导数在分支里未定义。
|
||||
- **仍然不能写空间 `ALPHA`**:alpha 只代表墨线覆盖率,写进 ALPHA 会让面片变洞。
|
||||
- `face_atlas_transparent.png` 是我们自己的素材(从已删除的 `mouse_表情测试.png` 去蓝底、反混合出来的 1024² 副本),保留原图坐标排布;内置 ID `builtin:face_01/02` 对应各自的图集 rect。**原始表情图已随模型删除,这个副本现在是唯一来源,别删。**
|
||||
- **脸部矩形 = 整块头部 UV 孤岛 (0.695, 0.682) 尺寸 (0.301, 0.314)** —— 用户确认「就是整块头部」。**头部 UV 的 v 越大越靠上,而贴图第 0 行在上边,所以必须开 `face_uv_flip_y`(默认 true),否则表情上下颠倒**(用户实测确认过)。想往下挪:把 `face_uv_min.y` 钉在孤岛底边 0.682、改小 `face_uv_size.y`;**min.y 不能低于 0.682**,否则矩形会伸进下方那段孤岛(u 0.695~0.935 / v 0.293~0.674,是胸腹),表情会同时画到肚子和前胸上。
|
||||
- `face_uv_min` / `face_uv_size` / `face_uv_flip_y` 在 `player.tscn` 和 `character_select.tscn` 里**各有一份**(两个 FaceRig 节点),检视器里改只影响当前那个;要两边一致就改 `face_rig.gd` 里的默认值。`face_uv_size` 是 `Vector2`。测试 `test_face_uv_rect_inside_head_island` 会按 min/max 归一化后再判定,允许负边长当翻转用。
|
||||
- `FaceRig` 保存 `user://face_preset.json` 中的 expression ID 和 `user://face_paint.png` 暂存笔画;已保存的自绘表情用 `user://face_library/index.json` + 各自透明 PNG 持久化,角色选择页动态显示内置 atlas 表情和本地表情。
|
||||
- 联机当前表情同步:内置表情发送 ID;`user:` 本地表情发送当前 PNG 字节;笔画层另外发送。**不广播整份本地表情库**。
|
||||
- `FacePainter`:直接改 RGBA8 字节缓冲。画 = source-over alpha 混合,擦 = 把目标 alpha 乘掉;512² 一次 1MB,按一笔/动作同步一次。
|
||||
- 「自己画」= 空白底起手;保存到本地表情库成为独立项目,再次选择可加载。取消会恢复旧 expression ID 与笔画。
|
||||
- 界面链路:`ui/main_menu.tscn` →「开始游戏」→ `ui/character_select.tscn`(3D 预览 + 动态表情列表 + 自己画)→ `ui/paint_panel.tscn`(透明笔画层叠在肤色底上)。本地列表用 HFlow + ScrollContainer 显示。
|
||||
|
||||
## Godot 4 踩坑
|
||||
|
||||
|
||||
Reference in New Issue
Block a user