limbo hsm
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# 2026-10-15
|
||||
|
||||
## 玩家状态管理迁移到 LimboAI LimboHSM
|
||||
|
||||
- 需求:玩家场景的状态管理完全用 LimboHSM 实现(addons/limboai v1.8.1,GDExtension 版,
|
||||
**无需启用 editor plugin**,ClassDB 里没有 HSMPlayer —— LimboHSM/LimboState 本身就是 Node)。
|
||||
- 方案讨论:BT vs HSM 选型 → 玩家是人操作的(决策已由人完成),用 HSM 建模身体状态;
|
||||
BT 留给将来的 NPC/autopilot。
|
||||
- 新增 `character/player/hsm/`(11 个脚本):
|
||||
- `player_hsm.gd`(根,转换表 Grounded⇄Airborne:`airborne`/`landed` 事件)
|
||||
- `grounded.gd`(Grounded 子机:12 条转换 + `_enter` 按当下输入挑初始子状态)
|
||||
- `airborne.gd`(Jump→Fall on `falling`;`_enter` 按 vy>0 挑 Jump 否则 Fall)
|
||||
- `ground_state.gd`(地面叶子基类:统一判 try_jump/踏空 → `airborne`,再走 `_think()`)
|
||||
- idle/walk/sprint/crouch/sneak.gd(写 motor.target_speed/crouching/sprinting + 动画)
|
||||
- jump.gd/fall.gd(落地判定 `is_on_floor and vy<=0`,带 vy 门槛防起跳帧弹回)
|
||||
- 改动:
|
||||
- `player.gd`:加 `class_name Player`;挂 HSM;`_ready` 末尾 `hsm.initialize(self)` +
|
||||
`hsm.set_active(true)`(仅权威端);管线第一位 `hsm.update(delta)`;animator 改调 `set_gait`。
|
||||
- `player_motor.gd`:删掉从输入推断 sprint/crouch/target 的逻辑,暴露
|
||||
`target_speed`(状态写)、`has_move_input()`、`try_jump()`(起跳在状态判定处就地执行,
|
||||
同帧转 Jump);crouching/sprinting 变成状态写入的事实镜像(Look/DebugPanel 继续用)。
|
||||
- `player_animator.gd`:`set_motion` 拆成 `play_state(state)`(状态 _enter 调)+
|
||||
`set_gait(velocity)`(每帧刷倍率);删 `_resolve_state` 和 `idle_threshold`。
|
||||
anim_state/anim_speed 同步属性保持不变(远端靠 Sync 复制驱动)。
|
||||
- `player.tscn`:HSM 节点子树(LimboHSM root + Grounded{Idle,Walk,Sprint,Crouch,Sneak} +
|
||||
Airborne{Jump,Fall}),根机器 `update_mode = 2`(MANUAL,编辑器里写入 int 失败,
|
||||
手改 tscn 后 force_reload 场景生效)。
|
||||
- 行为变化(有意为之):行走/站立切换改按**输入**判定(旧版按速度阈值 0.2 m/s),
|
||||
顶墙时播 walk 而非 idle;其余(蹲伏优先于疾跑、跳跃缓冲、落地按输入挑状态)与旧版一致。
|
||||
- 验证:全部脚本 `--check-only` 通过;无头探针场景模拟输入跑完整状态链
|
||||
(Idle→Walk→Sprint→Sneak→Crouch→Idle→Walk→Jump→落地Walk→Idle),数值与理论吻合。
|
||||
|
||||
## 二次重写:小人国定标(同日)
|
||||
|
||||
- 用户明确:不保旧手感、可重写;**小人国玩法,角色身高约 0.2 m**;旧代码可删。
|
||||
- 定标(全部固化进 player_motor.gd 默认值,删掉了 tscn 里的 walk/sprint 覆盖):
|
||||
walk 2.419(≈12 身长/s)、sprint 3.624(≈18 身长/s)、crouch 1.21(走速一半)、
|
||||
accel 22(0.11s 到全速)、air_accel 5.9(地面 1/4)、jump 1.5 / gravity_scale 0.5
|
||||
(跳高 0.23 m ≈ 1.15 身高、滞空 0.61 s)不变。DebugPanel body_height 0.15 → 0.2。
|
||||
- **修掉一个真 bug**:场景里 walk 被调到 2.419 后,动画器基准速度还是 0.4286,
|
||||
walk/run 动画一直被 clamp 在 1.6× 上限。重构:删掉 animator 的 4 个 *_ref_speed 导出,
|
||||
`play_state(state, ref_speed)` 由 HSM 状态传入基准速度(状态知道目标速度是多少),
|
||||
`set_gait` 用存下的 `_ref_speed` 算倍率 —— 两处对表的坑没了。
|
||||
- 探针复测:三个位移动画全速倍率均 1.00,转换链不变。
|
||||
|
||||
### 踩坑(复用注意)
|
||||
|
||||
1. **无头 `--check-only` 对新 class_name 报「Could not find type」**:
|
||||
全局类缓存 `.godot/global_script_class_cache.cfg` 没刷新。让编辑器跑一次
|
||||
`EditorFileSystem.scan()`(godot_ai MCP 的 filesystem scan)即解;批量循环检查时
|
||||
同一批里前后结果不一致也是这个原因。
|
||||
2. **godot-ai MCP `node_set_property` 写 enum int 属性失败**
|
||||
(WRONG_TYPE: Cannot write String to a int property,value 传 JSON number 也一样)。
|
||||
变通:MCP scene_save 后直接手改 .tscn 补属性行,再 `scene_open force_reload=true`。
|
||||
3. **MCP 场景节点路径**要含场景根:`/Player/HSM`,不是 `/HSM`。
|
||||
4. **LimboHSM 嵌套用法**:子状态机的 `_enter`(脚本 override)先于其 initial_state 激活执行,
|
||||
所以可以在 `_enter` 里 `set_initial_state()` 动态挑入口子状态(落地按输入挑、空中按 vy 挑)。
|
||||
5. **转换触发的环境判定要放叶子状态**,不要放父机 `_update`:父机先于子状态执行,
|
||||
在父机里 dispatch 转换后,本轮级联还可能调到已退出的旧子状态(叶子自己判、dispatch 后
|
||||
立即 return,没有这个窗口)。
|
||||
6. 无头模拟按键:`Input.action_press` 只改动作状态、**不发输入事件**(Motor 的跳跃缓冲在
|
||||
`_unhandled_input` 收事件,探不到);要用 `Input.parse_input_event(InputEventAction)`。
|
||||
Reference in New Issue
Block a user