diff --git a/CLAUDE.md b/CLAUDE.md index 962c424..27345c9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -90,6 +90,15 @@ StageController.cs ← manages stage GameObjects, StageObjectMap **Brightness is a separate channel — do not fold it into the colour.** Write the key's colour to the colour property and its `power` to `_ColorPower`; let the shader combine them. Only fall back to `colour * power` where `_ColorPower` is absent (`BgMirrorBall`). +**Writing shader properties: one shared block, always Get → modify → Set.** All of `Director`'s +handlers write through the single `PropBlock` (`Director._propBlock`) and must call +`renderer.GetPropertyBlock(block)` first — `SetPropertyBlock()` **replaces** the block rather than +merging, so a handler that starts from an empty block wipes whatever another track wrote to the +same renderer earlier in the same frame. That is exactly how GlobalLight was silently dead for +59/59 songs (BgColor1's character branch runs right after it and cleared all 13 rim properties). +Never `new MaterialPropertyBlock()` per frame, and never write `_Color` without first checking the +target shader's real property table — three separate tracks shipped no-op colour writes that way. + Dump any shader's real property table with: ```python @@ -141,22 +150,42 @@ MonoBehaviour 实例脚本为空。**但这些脚本是可以「接管」的** 签名清单(`Assets/Scripts/` 下任意位置均可,只要在 umamusume 程序集内): -| namespace | 类 | live10149 实例数 | -|---|---|---| -| `Gallop.Live` | `AnimationObjectController` | 282 | -| `Gallop.Live` | `BillboardController` ✅已建 | 164 | -| `Gallop.Live` | `UnityLensFlareController` | 205 | -| `Gallop.Live` | `WashLightController` | 27 | -| `Gallop.Live` | `LightProjection` | 4 | -| `Gallop.RenderPipeline` | `CustomLensFlare` | 200 | -| `Gallop.RenderPipeline` | `MirrorBallProjector` | 4 | -| `Gallop.RenderPipeline` | `CustomProjector` | 4 | -| `Gallop` | `MirrorReflection` | 1 | -| `Gallop.Live.ShaderParam` | `ShaderParamController` | 1 | +| namespace | 类 | live10149 实例数 | 状态 | +|---|---|---|---| +| `Gallop.Live` | `AnimationObjectController` | 282 | ❌(动画播放已由 `StageAnimationPlayer` 顶上,见下)| +| `Gallop.Live` | `UnityLensFlareController` | 205 | ❌ | +| `Gallop.Live` | `BillboardController` | 164 | ✅ 已建(字段已读到,朝向行为待定)| +| `Gallop.Live` | `WashLightController` | 27 | ❌ | +| `Gallop.Live` | `LightProjection` | 4 | ❌ | +| `Gallop.RenderPipeline` | `CustomLensFlare` | 200 | ❌ | +| `Gallop.RenderPipeline` | `MirrorBallProjector` | 4 | ❌ | +| `Gallop.RenderPipeline` | `CustomProjector` | 4 | ❌ | +| `Gallop` | `MirrorReflection` | 1 | ✅ 已建并实现(平面镜反射,见下)| +| `Gallop.Live.ShaderParam` | `ShaderParamController` | 1 | ❌ | 字段名必须和 TypeTree 逐字一致(大小写敏感),dump 一下即可。 这条同样适用于任何其它 bundle 里的 MonoBehaviour。 +**重建签名只拿得到数据,拿不到方法体。** 两个已建的类正好是两种情形: + +- `MirrorReflection`(`Assets/Scripts/umamusume/Gallop/MirrorReflection.cs`)—— 接收端能 + 自己查出来,所以能补完行为:宿主 `mirror_a` 的材质用 `Cygames/MirrorAndShadow/ReceiveMirror`, + 该 shader 只有 `_MainTex`/`_ReflectionTex`/`_Color`/`_ReflectionRate`,而组件字段 + `_mirrorReflectionColor`/`_mirrorReflectionRate` 与后两者逐字对应。实现方式是第二摄像机 + + 反射矩阵 + 斜投影渲进 RT 再写回 `_ReflectionTex`(URP 下不能用 `Camera.Render()`, + 必须挂 `beginCameraRendering` 拿 `ScriptableRenderContext` 后 `RenderSingleCamera`)。 + 这同时解决了长期的「地板发白」——`_ReflectionTex` 未绑定时 Unity 代入白贴图, + 而 `_ReflectionRate` 默认 1.0,等于满强度白反射。 +- `BillboardController` —— 字段读到了,但 `_rotationType` 是枚举,语义随 IL2CPP 元数据一起 + 拿不到,所以只有数据、没有行为。这是「重建签名」的能力边界。 + +**舞台自带 Animation 的播放**(`StageAnimationPlayer`,顶替缺失的 `AnimationObjectController`): +283 个 `Animation` 组件全部 `playAutomatically = true` 但默认 clip 为空,Unity 因此什么都不播。 +只接管**唯一解**的情形(1 个 clip,live10149 上 4 个对象);3 个 / 5 个 clip 的状态机选择规则 +没有依据,只打日志不猜。播放不走 `Animation.Play()` —— 那用的是 Unity 自己的时钟,暂停和拖动 +进度条都会脱节;改为每帧按 `LiveTimelineControl.currentLiveTime` 设 `AnimationState.time` 再 +`Sample()`,与演出时间同源。 + ### Blocked **IL2CPP 元数据拿不到。** 游戏是 IL2CPP 构建,但 `global-metadata.dat` 既不在 @@ -235,9 +264,9 @@ Open items and why each is blocked are tracked in `LIVE_TRACKS.md` → 「已知 | Particle (41) | `LiveTimelineParticleData` | sets ParticleSystem.emission.rateOverTime | | ParticleGroup (42) | `LiveTimelineParticleGroupData` | sets FlickerLightRate | | ChromaticAberration (73) | `LiveTimelineChromaticAberrationData` | URP `ChromaticAberration.intensity` ← `power`;per-channel offset 无法映射 | -| HdrBloom (38) | `LiveTimelineHdrBloomData` | **dead code** — 0/58 songs carry data (`hdrBloomKeys` empty in every scan and in the song-1177 dump). Safe to delete. | +| HdrBloom (38) | `LiveTimelineHdrBloomData` | **no handler, deliberately** — 0/58 songs carry data (`hdrBloomKeys` empty in every scan and in the song-1177 dump), and the field→URP-`Bloom` mapping was never verifiable. Data layer (data class, worksheet field, event) is kept; the Director handler and the `Bloom` volume override were removed 2026-08-07. | | ColorCorrection (61) | `LiveTimelineColorCorrectionData` | URP `ColorAdjustments.saturation` + `ColorCurves` RGB;depth/blend curve 无 URP 对应 | -| BlinkLight | `LiveTimelineBlinkLightData` | SetActive + child Light color/intensity (56/58 songs). **Biggest lighting payload by far** — 全语料 19420 keys / 57 songs, and over half are named `*_wash_*` (`wash_truss_a` 147, `wash_truss_b` 147, `wash_ground_a` 51, …). `color1Array` / `LightBlendMode` / `isReverseHueArray` still unused. | +| BlinkLight | `LiveTimelineBlinkLightData` | SetActive + writes `_BlinkLightColor`/`_ColorPower` on the group's renderers (56/58 songs). **Biggest lighting payload by far** — 全语料 19420 keys / 57 songs, and over half are named `*_wash_*` (`wash_truss_a` 147, `wash_truss_b` 147, `wash_ground_a` 51, …). Palette slot = the `lightNNN_`/`alphaNNN_` prefix of the renderer or its nearest ancestor; renderers with no prefix are left to their real owner (BgColor1) when the group has slotted ones. `color1Array` / `LightBlendMode` / `isReverseHueArray` still unused. | | WashLight | `LiveTimelineWashLightData` | SetActive only (5/58 songs). Low value: real "wash light" output comes from the BlinkLight `*_wash_*` groups above, not this track — song 1177 has just 1 group / 11 keys here. | | Laser | `LiveTimelineLaserData` | SetActive + position/rotation/scale — blink/raycast not implemented (6/58 songs) | diff --git a/LIVE_AUDIT_2026-08-05.md b/LIVE_AUDIT_2026-08-05.md index 1666c8f..b6e3be6 100644 --- a/LIVE_AUDIT_2026-08-05.md +++ b/LIVE_AUDIT_2026-08-05.md @@ -19,7 +19,7 @@ | B1–B4 | 每帧 `r.materials` / 全场景遍历 / 每帧 new MPB | ✅ 随 A1–A3 一并修掉(Wash/Laser 的 `r.materials` 仍在,属 ⚠️ 轨道,未动) | | C1 | GlobalFog 把高度雾当距离雾 | ❌ 未动,需先做语料统计定方案 | | C2 | 初始化时同名子物件只有第一个被关,且没人再打开 | ✅ 已修(**前两稿的诊断都是错的**,见下文) | -| C4 | 舞台地板 / 高光面发白 | 🔍 已定位到 `DefaultEnvMapNoAmbient` 的 `_EnvMap`/`_EnvRate`;只剩「运行时贴图有没有解析出来」一个未知项,已加 `StageEnvMapDiag` 打印 | +| C4 | 舞台地板 / 高光面发白 | ✅ 已解决 —— 但**排查对象从头到尾都错了**:白的是叠在同位置的 `mirror_a`,不是 `DefaultEnvMapNoAmbient` 那三个物件。实现 `Gallop.MirrorReflection` 后正常。诊断用的 `StageEnvMapDiag`/`StageEnvRateToggle`(F9)验证假设不成立后已于 2026-08-07 删除 | | C3 | ParticleGroup 覆盖 Particle | ❌ 未动,优先级低 | **均未经实机验证** —— 环境里没有 Unity,也没有 C# 编译器,只做了括号配平和符号引用检查。 @@ -454,6 +454,10 @@ URP Lit 的属性(`_WorkflowMode`、`_Smoothness`、`_ClearCoatMask`、`_Blend > 发白消失 → 就是 env 反射项;发白照旧 → env 路径无关。 > 已实现为 `StageEnvRateToggle`(按 **F9** 在「原始值 ↔ 0」间来回切,走 MPB 不动材质, > 随 `StageEnvMapDiag.Dump()` 自动挂上)。 +> +> **2026-08-07:这两个诊断类已删除。** 判决实验做完了、结论是「env 路径无关」, +> 而真正的成因(`mirror_a`)也已查明并修复,它们没有留下来的理由 —— +> 留着只会让每次载入舞台都多跑一遍无用的全场景遍历,还挂一个 F9 热键。 **候选 B(可能相关,未证实):缺失的平面镜面反射系统** @@ -525,6 +529,24 @@ F9 实测。日志确认开关确实生效(9 次切换 × 3 个渲染器,`[E 前 4 项之后建议重跑 `LiveTimelineWorksheetDiag.Dump()` 对照歌曲 1177 与 1006, 确认组名解析命中率,再更新 `LIVE_DEV_MAP.md` 里这几行的 ✅/⚠️ 标记。 +**进度(2026-08-07)**:1(A1)、2(A2,部分)、3(A3)、4(C2)、5(A4)均已落地, +A5 一并做掉;剩 6(B 类残余:WashLight/Laser 的 `r.materials`)与 7(C1 / C3,都需要先做语料统计)。 + +--- + +## 审计之后(2026-08-06 ~ 08-07) + +审计只覆盖了「已标 ✅ 的轨道对不对」。这两天做的是另一类问题 —— **舞台侧原版脚本缺失**, +以及随之而来的一次代码整理。 + +| 事项 | 结果 | +|---|---| +| 舞台 Animation 全都不播 | 283 个 `Animation` 组件 `playAutomatically = true` 但默认 clip 为空,Unity 什么都不播;原版靠 `AnimationObjectController` 显式 `Play("clip名")`。已由 `StageAnimationPlayer` 顶上,**只接管单 clip 的唯一解情形**(4 个对象),3/5 个 clip 的状态机选择规则没依据,只打日志 | +| 按签名重建原版脚本 | 打通了。类名 + namespace + 程序集名对上就能让 Unity 真正反序列化字段 —— `BillboardController` 令 `[StageScripts]` 丢失数 892→728(正好 −164)。**但只拿得到数据,拿不到方法体**:`_rotationType` 的枚举语义随 IL2CPP 元数据一起没有 | +| 地板发白(C4) | 查明是 `mirror_a`,实现 `Gallop.MirrorReflection` 后解决。详见 C4 条目 —— 这条的过程比结论值钱 | +| BlinkLight 调色板槽位 | 槽位号 = `lightNNN_`/`alphaNNN_` 名字前缀,7 组实测无一例外。顺带修掉镜面球球体被 BlinkLight 涂成粉红的问题(`_MulColor0` 是 BgColor1 的通道,BlinkLight 排在它之后,抢了) | +| 代码整理 | 六个各自为政的 `MaterialPropertyBlock` 字段合成一个共享块(用法本来就是 Get→改→Set,不带跨帧状态);`ResolveStageTargets` 的四个诊断实例字段改成显式返回的 `StageResolveReport`;effect 载入、vocal 音源解析、VMD 的 FOV 标记三处重复各去掉一份;`StageEnvMapDiag`/`StageEnvRateToggle` 删除;HdrBloom 的 handler 与 `Bloom` volume override 撤掉(全语料 0 keys,映射也没核实过)| + --- ## 附:本次用到的验证命令 diff --git a/LIVE_DEV_MAP.md b/LIVE_DEV_MAP.md index 5993dd7..4986df1 100644 --- a/LIVE_DEV_MAP.md +++ b/LIVE_DEV_MAP.md @@ -45,7 +45,7 @@ Toon shader 已移植,卡住的主要是全屏后处理和几个逆向未完 | Transform (31) | ✅ | position / rotation / scale。2026-08-05 修:原先只查 `StageObjectUnitMap`,组名是 GameObject 名(son1028 的 `light001_glow_001`)时整条轨道静默失效,12 首 / 681 keys 从未生效 | | WashLight (43) | ⚠️ | `SetActive`(颜色/投影未做) | | Laser (44) | ⚠️ | `SetActive` + transform(blink 未做) | -| BlinkLight (45) | ⚠️ | `SetActive` + 写 Renderer 的 `_BlinkLightColor`/`_ColorPower`(color1Array/BlendMode 未做) | +| BlinkLight (45) | ⚠️ | `SetActive` + 写 Renderer 的 `_BlinkLightColor`/`_ColorPower`。2026-08-05 补:**调色板槽位映射已逆出** —— 槽位号 = 渲染器自身或最近祖先的 `lightNNN_`/`alphaNNN_` 名字前缀,live10149 每组「前缀种数」与「调色板去重颜色数」精确相等;组内无前缀的渲染器(镜面球球体等)留给它真正的所有者 BgColor1,不再被涂成粉红。剩 color1Array/BlendMode 未做 | | Spotlight3d (68) | ✅ | `SetActive` + 写 Renderer 颜色属性(**不是** Unity `Light`)。2026-08-05 修:原先写 `_Color`,而 `Gallop/3D/Live/Stage/*` 下**没有任何 shader** 声明该属性(133 个全枚举过),颜色写入一直是空操作;灯柱是 `StageBeamLight` 系列 → `_MulColor0` + `_ColorPower`,改用 BlinkLight 的 shader 探测 | | Effect (60) | ✅? | `Instantiate` prefab | @@ -86,8 +86,22 @@ Toon shader 已移植,卡住的主要是全屏后处理和几个逆向未完 |------|------| | ChromaticAberration (73) | ⚠️ intensity 完成,通道偏移无 URP 对应 | | ColorCorrection (61) | ⚠️ saturation 完成,depth/blend curve 无 URP 对应 | -| HdrBloom (38) | ❌ 0/58 首无数据,无需实现 | -| PostFilm (39) / RadialBlur (15) / TiltShift (63) / SunShafts (37) | ❌ 见后处理 TODO | +| HdrBloom (38) | ❌ 0/58 首无数据。2026-08-07 起**连 handler 和 Bloom override 一并撤掉**,只留数据层 | +| PostFilm (39) | ⚠️ 数据层已通(三条轨道 → `PostFilmRendererFeature.Layers`),Feature 已注册进 Renderer 资产但 `_enableRendering = false` —— 定义 vignette 几何的 shader 不在任何 bundle 里,开着只会画错 | +| RadialBlur (15) / TiltShift (63) / SunShafts (37) | ❌ 见后处理 TODO | + +### 舞台自带脚本(原版 MonoBehaviour,按签名重建) + +`Assets/Scripts/umamusume.asmdef` 的程序集名与 bundle 里写的 `umamusume` 一致,所以按 +**类名 + namespace + 程序集名** 建类就能让 Unity 把原版的序列化字段真正填进来。 +清单和实例数见 `CLAUDE.md`「用原签名重建脚本」。 + +| 脚本 | 状态 | +|------|------| +| `Gallop.MirrorReflection` | ✅ 已建并实现。第二摄像机 + 反射矩阵 + 斜投影 → RT → `_ReflectionTex`。**顺带解决了长期的「舞台地板发白」** —— `mirror_a` 的 `_ReflectionTex` 从没被赋值,Unity 代入白贴图 × 默认 `_ReflectionRate = 1.0` | +| `Gallop.Live.BillboardController` | ⚠️ 已建,字段可读(`[StageScripts]` 丢失数 892→728),但 `_rotationType` 枚举语义随 IL2CPP 元数据一起拿不到,行为未实现 | +| `Gallop.Live.AnimationObjectController` | ⚠️ 未建;其最直接的后果(283 个 `Animation` 一个都不播)已由 `StageAnimationPlayer` 顶上:只接管单 clip 的唯一解情形,按 `currentLiveTime` 采样而不是 `Animation.Play()`,暂停/拖动进度条都跟随 | +| 其余 7 个 | ❌ 未建,见 `CLAUDE.md` 清单 | ### 内部系统 | 轨道 | 状态 | @@ -105,8 +119,8 @@ Toon shader 已移植,卡住的主要是全屏后处理和几个逆向未完 |------|-------| | PostFilm (39) / RadialBlur (15) / TiltShift (63) / SunShafts (37) | URP 无对应,需自写 ScriptableRendererFeature + shader → 见 `live-shader-todo.md` | | LightProjection (74) | `Projector` 组件在 URP 不工作,需 DecalProjector 或自定义(36/58 首)| -| Environment (58) | Planar Reflection 系统完全没有(第二摄像机→RenderTexture→地板 shader)(48/58 首)| -| LensFlare (57) | 舞台用 `CustomLensFlare` 脚本,UmaBuild 无源码;可先做 SetActive(45/58 首)| +| Environment (58) | 轨道本身仍未接。但 Planar Reflection **已经不是从零** —— `Gallop.MirrorReflection` 已实现整条第二摄像机→RT→镜面 shader 的路径,轨道要做的是把 `mirrorReflectionRate` 这类关键帧接到它上面(48/58 首)| +| LensFlare (57) | 舞台用 `CustomLensFlare`(`Gallop.RenderPipeline`,200 实例)。~~UmaBuild 无源码~~ —— **这句是错的**:按签名建类就能拿到它的序列化字段(见上一节),拿不到的只有方法体。可先做 SetActive(45/58 首)| ### 没 dump 找明白(不知道打哪里) @@ -153,7 +167,10 @@ Toon shader 已移植,卡住的主要是全屏后处理和几个逆向未完 | 文件 | 说明 | |------|------| -| `Assets/Scripts/umamusume/Gallop/Live/Director.cs` | 事件订阅 + 所有 handler | +| `Assets/Scripts/umamusume/Gallop/Live/Director.cs` | 事件订阅 + 所有 handler。写 shader 属性统一走共享的 `PropBlock`,且必须 Get→改→Set | +| `Assets/Scripts/umamusume/Gallop/MirrorReflection.cs` | 平面镜反射(原版脚本按签名重建)| +| `Assets/Scripts/umamusume/Gallop/Live/StageAnimationPlayer.cs` | 舞台 Animation 播放 + 缺失脚本普查(`[StageAnim]` / `[StageScripts]` 日志)| +| `Assets/Scripts/umamusume/Gallop/Live/PostEffect/PostFilmRendererFeature.cs` | PostFilm 的 URP Feature,默认 `_enableRendering = false` | | `Assets/Scripts/umamusume/Gallop/Live/Cutt/LiveTimelineControl.cs` | 每帧插值 + 事件触发 | | `Assets/Scripts/umamusume/Gallop/Live/Cutt/LiveTimelineWorkSheet.cs` | 所有轨道字段声明 | | `Assets/Scripts/umamusume/Gallop/Live/Cutt/LiveTimelineDataList/` | 各轨道数据类 | diff --git a/LIVE_TRACKS.md b/LIVE_TRACKS.md index 261cd17..bdc0409 100644 --- a/LIVE_TRACKS.md +++ b/LIVE_TRACKS.md @@ -199,7 +199,7 @@ originSessionId: 319ae839-0292-4b1e-80de-2f3c91b1f40e | 问题 | 依据 | |---|---| -| BlinkLight 调色板槽位映射 | `color0Array`/`powerArray` 恒为 10 项而 renderer 可达 570,映射规则未逆出,暂统一取第 0 槽 | +| ~~BlinkLight 调色板槽位映射~~ ✅ 2026-08-05 解决 | 槽位号 = 渲染器自身或最近祖先的 `lightNNN_`/`alphaNNN_` 名字前缀。live10149 全组实测「前缀种数 N」与「槽 0..N-1 的去重颜色数」精确相等,槽 N..9 一律白色填充,7 组无一例外。此前统一取第 0 槽,于是镜面球整个发粉红、truss wash 丢掉黄绿蓝紫四色 | | BlinkLight `pattern` 语义 | 实测有 0 和 2 两种;相位公式是推测,`pattern != 0` 时按 `cycle * i / N` 错开 | | BgColor1 剩余 4 组 | `BgBL`(256 keys) / `FollowSpotColor`(16) / `Shadow`(2) / `mob_00`(2) 不是物件名也不是材质名 | | BgColor1 的 `power` → `_ColorPower`? | `_ColorPower` 属性确实存在,但映射关系无证据,已撤回 | @@ -215,7 +215,7 @@ originSessionId: 319ae839-0292-4b1e-80de-2f3c91b1f40e | 现象 | 状态 | |---|---| -| 舞台地板 `plane_000` / `stage_object_001` / `specular_002` 长期偏亮发白 | **早于本次所有改动就存在**,与 BgColor1/`_ColorPower` 无关,未排查 | +| 舞台地板长期偏亮发白 | ✅ 2026-08-06 解决。**白的不是文档里记的那三个物件** —— 是与 `plane_000` 同位同尺度、叠在一起的 `mirror_a`(同在 `pfb_env_live10149_main000` 下,局部变换全为单位)。它用 `Cygames/MirrorAndShadow/ReceiveMirror`,`_ReflectionRate` 默认 1.0 而 `_ReflectionTex` 从没赋值,Unity 对未绑定采样器代入白贴图 = 满强度白反射。实现 `Gallop.MirrorReflection` 接上反射 RT 后恢复正常。**教训见 `CLAUDE.md`「现象描述不是 ground truth」**:整轮排查都围着文档里那三个名字转,那个「`_EnvRate` 归零无变化」的实验切的是另一批物件 | | 小灯泡渲染成黑块 | 用 `StageTransmittedLightMask` / `StageMirrorBallShine`,这两个 shader **没有任何颜色属性**,BlinkLight 点不亮。疑为 URP 移植问题,对应未实现的 95/96 TransmittedLight 轨道 | | LED 大屏黑屏 | MonitorControl (10) 未实现,见上 | @@ -251,7 +251,7 @@ FacialToon(47, **6341 keys/59首**) > MonitorControl(10, 3911/55首,dispID 语 ### 第四优先:要从零搭渲染系统 ★★★ -LightProjection(74, 3793/37首,URP 下 Projector 不工作) > Environment(58, 1375/49首,Planar Reflection 整套;注意还有独立的 `MirrorReflectionDataList`) > LensFlare(57, 993/45首,可先做 SetActive 层) +LightProjection(74, 3793/37首,URP 下 Projector 不工作) > Environment(58, 1375/49首,注意还有独立的 `MirrorReflectionDataList`;**Planar Reflection 本身已不用从零搭** —— `Gallop.MirrorReflection` 已实现,缺的是把轨道关键帧接上去) > LensFlare(57, 993/45首,可先做 SetActive 层;`CustomLensFlare` 的字段可按签名重建拿到) ### 不该做