Files
agame/.workbuddy-ai/memory/2026-10-02.md
T
2026-10-02 09:32:11 +08:00

69 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-10-02
## 审查:Godot 4.7「SubViewport 面部手绘 + 骨骼驱动」方案
用户贴来一份第三方方案(SubViewport 当可绘制面部贴图 + SkeletonModifier3D 把骨骼 Z 转成 shader 参数),问可行性。只做评审,未改任何代码。
结论:**原理可行**(SubViewport-as-texture + shader 混合是标准做法),但原文代码有 4 处硬伤:
1. shader `render_mode ... skip_vertex_transform` 却没写 vertex() 手动变换 → 模型位置错。删掉该 render_mode。
2. `PaintSurface._input` 在**后台 SubViewport 里收不到鼠标事件**(Viewport 之间不派发输入)→ 必须放进 `SubViewportContainer`,或手动 `push_input`。
3. `SkeletonModifier3D` **没有 `skeleton` 属性**,只有 `get_skeleton()`(已查 4.5 文档确认)。
4. `save_paint` 没 `await RenderingServer.frame_post_draw` → 存到空帧。
标定类问题:`get_bone_pose_position()` 是相对父骨的姿态,Z 轴受父骨朝向影响,GLTF 导入后与 Blender 不一致,z_min/z_max 必须实测标定;`face_index` 只做了亮度乘,没真正切换表情图层。
与本项目相关的判断:角色是 `character-male-e.glb`,需先确认有独立面部材质槽 + 展平 UV;多人下 SubViewport/材质必须每实例一份,远端玩家的画要按 PNG 字节流同步。
## 重写 player_animator.gd(7 状态)
按新枚举 `{IDLE, WALK, CROUCH, RUN, FALL, SNEAK, JUMP}` 重写 `character/player/player_animator.gd`。
改动:加 `class_name PlayerAnimator`(`player.gd` 用它当类型)、新增 `Stance{STAND,CROUCH,SNEAK}`、
`set_motion(velocity, on_floor, sprinting=false, stance=STAND)`、片段基准速度扩到 walk/run/crouch/sneak。
实测结论(用 @tool 副本跑 McpTestSuite,14 条断言全过):
- 短片段名查不到,必须用 `"库名/片段名"`(`walk/walk`);`_ready()` 里扫库解析一次并写入 `_clips`。
- 状态映射、自动绑兄弟 AnimationPlayer、`play("walk/walk")`、loop_mode 补丁、倍率 5/5≈1.0 均正确。
- `class_name` 之后 `self is AnimationPlayer` 是编译期错误 → 脚本不能挂在 AnimationPlayer 节点上。
**未改、留给用户决定的场景问题**(`player.tscn`):
1. 脚本目前挂在 `Body/AnimationPlayer` 上,而 `player.gd`、`player_sync.tres`、`tests/test_net_replication.gd` 都找 `Body/PlayerAnimator` —— 需要新增独立 PlayerAnimator 节点。
2. 该节点的 6 个 float 导出在 .tscn 里被序列化成 `null`(实测运行时是 `Nil`),会覆盖脚本默认值。
3. `tests/test_net_replication.gd` 里的 `REQUIRED_CLIPS` 仍是旧的 `sprint`,需要同步成新状态集。
## 收尾:按用户决定接线 + 清场景脏数据
用户决定「脚本就挂在 `Body/AnimationPlayer` 上」,据此改完并验证(`test_run` 20/20,320 条断言;实机跑 player.tscn 确认 idle→fall 切换正常)。
- `player_animator.gd`:文档改成「脚本挂在 AnimationPlayer 上、`animation_player` 留空自绑自身」。
- `player.gd`:`$Body/PlayerAnimator` → `$Body/AnimationPlayer`,场景结构注释同步;`_ride_zipline` 里 `tan` 改名 `tan_dir`(消 built-in 遮蔽警告)。
- `player_sync.tres`:同步字段路径改 `Body/AnimationPlayer:anim_state` / `:anim_speed`。
- `player.tscn`:删掉被序列化成 `null` 的 6 个 float 导出(`blend_time` 等)—— 实测运行时确实是 `Nil`,会覆盖脚本默认值。
- `tests/test_net_replication.gd`:路径、`REQUIRED_CLIPS` 全部同步;新增 `_find_clip()` 按库解析(短名断言会假红)。
- `tests/test_zipline.gd`:`test_interact_action_exists` 先调 `InputMap.load_from_project_settings()` —— 编辑器进程的 InputMap 不带项目动作(实测 `interact`/`jump` 均为 false,只有 `ui_accept`),这条一直假红。
- `world/zipline/zipline.gd`:`tan` → `tan_dir`。
**Godot MCP 注意**:`node_get_properties` 读不到脚本的 @export 属性(`unknown_fields`),但 `node_set_property` 能写进去;数值标量经 MCP 会变成字符串(`node_set_property` 返回 `"0.15"`),`batch_execute` 里的 `set_property` 直接报 INTERNAL_ERROR。改 `.tscn` / `.tres` 这类被编辑器占用的文件,直接用文本工具写反而更省事(`.tscn` 可写、`.tres` 报 EBUSY,用 `filesystem_manage(op="write_text")`),写完 `scene_open(force_reload=true)` 让编辑器重新读盘。
## 接线:Ctrl 蹲下 / Ctrl+移动 潜行
用户定的规则:`crouch` 绑 Ctrl;**蹲着不动 = CROUCH,蹲着走 = SNEAK**。
- `project.godot`:用 `input_map_manage(op="ensure_binding", {action:"crouch", event_type:"key", keycode:"Ctrl"})`
加的动作,落在 keycode 4194326,和已有的 `sprint`(Shift/4194325) 同一套写法。
- `player_animator.gd`:**删掉 `Stance` 枚举**,`set_motion` 第 4 个参数改成 `crouching: bool`。
「蹲 / 潜行」的区分放回表现层(`moving := 水平速度 >= idle_threshold`),控制器只传「按没按 Ctrl」。
优先级改成 空中 > 蹲 > 移动。
- `player.gd`:调用点传 `Input.is_action_pressed("crouch")`;操作说明补上 Ctrl。
- `tests/test_net_replication.gd`:加 `test_crouch_action_exists`(先 `InputMap.load_from_project_settings()`)。
验证:`test_run` 21/21;另用 @tool 副本跑了 4 组状态映射断言(Ctrl+不动→CROUCH、Ctrl+移动→SNEAK、
Ctrl+Shift→仍是 SNEAK、空中→JUMP/FALL 优先于蹲),片段也确认切到了 `crouch/crouch` / `sneak/sneak`。
**踩坑**:`test_run` 的脚本缓存按路径,删掉重建同名文件会跑旧版本 —— 第一次探针就因此假失败
(旧版 `stance: int` 收到 `true` 被当成 `Stance.CROUCH`)。换文件名才拿到真实结果。
**未做**:蹲下目前不改移动速度、也不改碰撞胶囊高度 —— 潜行时用走速(5 m/s)播 sneak 片段,
`speed_scale` 会被 clamp 到上限 1.6。要不要减速/压低碰撞盒等用户定。