limbo hsm
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# agame 项目长期笔记
|
||||
|
||||
Godot 4.7.2(Forward+,Jolt Physics,D3D12)。编辑器位于
|
||||
`D:\_yvmou\Godot\Godot_v4.7.2-stable_win64.exe\Godot_v4.7.2-stable_win64.exe`(另有 mono 版)。
|
||||
|
||||
## 代码约定
|
||||
|
||||
- **GDScript 警告被当作错误**(至少 `inference_on_variant`)。不要写成
|
||||
`var x := some_variant_returning_call()`;要显式写类型,例如
|
||||
`var s: Script = node.get_script() as Script`。改完脚本务必用
|
||||
`--check-only --script res://xxx.gd` 自查。
|
||||
- 缩进用 tab,注释用中文(项目既有风格)。
|
||||
|
||||
## 无头验证的正确姿势(踩过坑)
|
||||
|
||||
- `--check-only --script res://x.gd`:GDScript 语法校验,有效。
|
||||
- `godot --headless --editor --path <proj> --quit`:验证编辑器插件能否加载,有效。
|
||||
- **不要**用 `extends SceneTree` 的 `--script` 脚本去测 `_ready()` 相关行为:
|
||||
这种模式下节点不会真正进入场景树(`is_inside_tree()` 为 false),`_ready` 不执行,
|
||||
会得出完全错误的结论。正确做法是造一个临时场景,用位置参数当主场景跑:
|
||||
`godot --headless --path <proj> res://tools/_tmp_probe.tscn`。
|
||||
- 即使是 `--headless`(dummy 渲染后端),**shader 编译错误仍会被报告**
|
||||
(`SHADER ERROR: ...`),所以可以用无头运行来校验 shader 语法。
|
||||
|
||||
## 玩家 = LimboAI LimboHSM(2026-10-15 起)
|
||||
|
||||
- LimboAI v1.8.1 是 **GDExtension 版**(addons/limboai 只有 bin/,无 plugin.cfg),
|
||||
**不需要启用 editor plugin**;ClassDB 里没有 HSMPlayer —— `LimboHSM`/`LimboState` 本身就是 Node。
|
||||
- 玩家状态机在 `character/player/hsm/`:根 `player_hsm.gd`(Grounded⇄Airborne)+
|
||||
Grounded{Idle,Walk,Sprint,Crouch,Sneak} + Airborne{Jump,Fall},全是场景节点、脚本挂状态上。
|
||||
- 分工:**HSM 管「是什么状态」**(写 motor.target_speed/crouching/sprinting + 调
|
||||
animator.play_state),**motor 只管物理**(重力/加速度/跳跃缓冲 + try_jump),
|
||||
animator 只管「怎么播」(play_state + set_gait 刷倍率)。player.gd 管线第一位
|
||||
`hsm.update(delta)`(MANUAL 模式,仅权威端 initialize+set_active;远端靠 Sync 复制)。
|
||||
- 走/站立转换按**输入**判定(非旧版的速度阈值)——顶墙时播 walk 不播 idle,是预期行为。
|
||||
- 用法要点:子机 `_enter` 里可 `set_initial_state()` 动态挑入口子状态;环境判定
|
||||
(起跳/踏空/落地)放**叶子**状态的 _update,别放父机;落地判定要带 `vy<=0` 门槛。
|
||||
- 踩坑:新 class_name 后无头 `--check-only` 报「Could not find type」= 全局类缓存没刷新,
|
||||
让编辑器 scan 一次即解;MCP node_set_property 写 enum int 失败 → 手改 tscn + 场景 force_reload。
|
||||
|
||||
## toon 卡通渲染管线
|
||||
|
||||
- `shader/cel-shader-base.gdshader`(`USE_*` 宏开关各模块)+ `shader/includes/*.gdshaderinc`,
|
||||
上游是 godot4-cel-shader 项目。
|
||||
- `shader/toon.tres` 是材质模板;`shader/outline.gdshader` 作为 `next_pass` 描边。
|
||||
- `shader/toon_apply.gd`(@tool Node3D)给每个 MeshInstance3D 复制一份独立材质,
|
||||
因为**每个模型有各自的贴图**,所以必须独立(不能共享一份材质)。
|
||||
- 已在项目设置里用了 **global uniform**:`diffuse_curve`、`specular_smoothness`、
|
||||
`fresnel_smoothness`、`outline_width`、`outline_color` —— 这几项调参本来就是全局实时生效。
|
||||
- 贴图尺寸极不统一(289 张 PNG / 24 种尺寸,1024²、512²、4096²、32² 混杂),
|
||||
因此**不适合** Texture2DArray + per-instance uniform 方案(显存会爆且要额外打包流水线)。
|
||||
- 编辑器插件 `addons/toon_refresh/`:改 toon.tres 时自动重刷 toon_apply 材质。
|
||||
|
||||
## 坐标系陷阱 + 上游着色器的边界(重要)
|
||||
|
||||
Godot 4 的 `fragment()` 里 `NORMAL` / `TANGENT` / `BINORMAL` 都处于**视图空间**,
|
||||
而 `NORMAL_MAP` 要的是**切线空间**值、由引擎用 TANGENT/BINORMAL 还原。
|
||||
把切线空间值直接赋给 `NORMAL` 会让整个场景的法线变成一个跟着相机转的常量 ——
|
||||
表现为平视全黑、俯瞰才亮。
|
||||
|
||||
但**不要**因此去改上游文件:`cel-shader-base.gdshader` 与
|
||||
`includes/normal-map.gdshaderinc` 来自 <https://github.com/eldskald/godot4-cel-shader>,
|
||||
本项目的策略是**逐字跟随上游**。上游的法线贴图路径 `NORMAL = normal.vector;`
|
||||
确实有上述坐标空间错误,但上游所有变体都是 `#define USE_NORMAL_MAP 0`,
|
||||
全仓库从未启用过这条路径(README 也没提),所以是"能编译但结果错"的死代码。
|
||||
本项目保持 `USE_NORMAL_MAP 0`,与上游一致;`toon.tres` 里残留的
|
||||
`shader_parameter/normal_scale` 在宏关闭时会被忽略,无副作用。
|
||||
|
||||
**结论:要动上游 shader 之前,先用 `E:/Godot/godot4-cel-shader-main/` 里的干净副本 diff。**
|
||||
|
||||
正确做法(若将来真要开法线贴图):只写 `NORMAL_MAP = 原始切线空间值` +
|
||||
`NORMAL_MAP_DEPTH`,让引擎合成;不要自己写 `NORMAL`,也不要预先做 `*2-1` 重映射
|
||||
(引擎自己做,蓝通道由引擎重建)。
|
||||
Reference in New Issue
Block a user