Files
mtgodot-poc/audit/contracts/render.effect-particle-motion.md
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

120 lines
14 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.
# render.effect_particle_motion —— MDE/MSE、粒子、动作事件挂点和回收
状态:`PARTIAL`
本合同不仅检查 MDE/MSE 粒子播放器,还检查 40250 `ActorInstanceMotionEvent` 在每个动作帧分派的特效、音效、飞行物、特殊攻击、显示/隐藏、传送和目标特效。判断标准是资源字段、事件类型、帧时序、挂点矩阵、生命周期和所有 Actor 的消费范围一致;只验证“特效能播放”不能视为与 40250 等价。
## 40250 参考调用链
- `GameLib/RaceMotionData.cpp` / `RaceMotionDataEvent.h`
- 读取 `MotionEventDataCount`、事件类型、`StartingTime`,按 `1 / g_fGameFPS` 转为动作帧。
- `EFFECT` 保存 `EffectFileName``EffectPosition``AttachingEnable``FollowingEnable``IndependentFlag` 和骨骼名。
- `EFFECT_TO_TARGET` 保存目标偏移、跟随/钓鱼标志;`FLY` 使用独立的 `FlyFileName`/`FlyPosition``SPECIAL_ATTACKING` 保存 `DuringTime``EnableHitProcess`、攻击参数和碰撞球。
- 动作加载时还从同名 `.mss` 读取声音脚本,后续按动作帧调用 `SoundEventProcess`
- `GameLib/ActorInstanceMotionEvent.cpp`
- `MotionEventProcess()` 对当前动作的每一帧事件调用 `MotionEventProcess(dwcurFrame, index, data)`
- `EFFECT` 分独立世界特效、挂骨骼并跟随、挂骨骼但只快照、挂 Actor 根节点四条路径。
- `EFFECT_TO_TARGET` 分钓鱼落点、目标对象跟随和目标位置快照三条路径。
- `SPECIAL_ATTACKING` 建立带持续时间、命中去重和动态碰撞球的 splash 区;`FLY` 从骨骼/角色位置创建 `.fly` 实例并调用 `OnShoot`
- `CHARACTER_SHOW/HIDE` 改变渲染模式和透明度;`WARP` 做地图阻挡检查、朝向目标并移动到目标前 270cm。
- `SOUND` 调用 `PlaySound3D`,声音脚本由 `SoundEventProcess` 独立更新。
- `EffectLib/EffectManager.cpp``EffectInstance.cpp``ParticleInstance.cpp``PythonEffectModule.cpp`
- 负责 MSE/MDE 注册、解析、粒子时间曲线、混合、颜色、缩放、旋转、纹理动画、渲染空间、生命周期和实例回收。
## 当前端实现映射
- `formats/msa.cpp` / `formats/msa.h`
- 已读取动作时刻、通用效果/声音/骨骼/挂点字段,另有 MDE/MSE 和攻击数据的基础解析。
- 本轮已补齐 40250 动作事件的专用数据:`FlyFileName`/`FlyPosition`/飞行骨骼、震屏 `DuringTime`/`Power`/`AffectingRange`、特殊攻击 `DuringTime`/`EnableHitProcess`/攻击参数/碰撞球,以及 type 7/8/9 的稳定事件记录。
- `extension/src/metin2_anim.cpp`
- 已在 `.msa` 播放时间跨过事件时发出 `motion_event`/`motion_event_detailed`,并提供骨骼姿态查询。
- 本轮 `motion_event_detailed`/`get_events()` 已暴露上述专用字段和 `collision_spheres`,表现层可按 40250 分支消费;生产路由尚未全部接入。
- `project/fx/effect_player.gd``project/fx/mde.gd``project/fx/mse.gd``project/fx/effect_registry.gd`
- 已覆盖 MDE/MSE、粒子曲线、材质、颜色、混合、纹理动画、one-shot/loop 生命周期、注册/缓存和 `fx_spawned`/`fx_finished` 追踪。
- `project/fx/motion_effect_anchor.gd` / `target_effect_anchor.gd`
- 已实现角色根、骨骼姿态、follow/independent、目标中心和目标快照的基础矩阵转换;缺失骨骼时会清理特效。
- `project/game_scene.gd`
- 本地主角 type 1/10 走完整挂点/目标特效,type 2/3 使用固定震屏/屏闪,type 6 只从待发队列触发 `client.shoot`;本轮远端真实 Actor 的 type 1/10、7/8/9 和 direct sound 也经由 `NetWorld` 路由。
- 本地和远端真实 Actor 已接入 type 7/8 的 `__HideEvent`/`__ShowEvent` 等价消费;type 9 已按 40250 计算目标前 270cm、检查 `world.is_blocked`、调整朝向并在本地主角路径发送一次 `FUNC_WAIT`。仍没有特殊攻击事件的 splash 碰撞球,也没有把 `.fly` 文件按 40250 的起点/目标/owner/skill 链创建。
- `project/ui/player_view.gd``project/ui/mob_view.gd`
- 已按当前动作加载同名 `.mss` 并逐 60FPS 更新 3D 声音脚本;本轮还为远端真实视图提供统一的 animator signal 接入点。
- `project/ui/remote_player_view.gd``project/net_world.gd`
- 远端 PC 和怪物会创建真实模型并播放 `.msa`;本轮 `net_world` 已为有 native animator 的远端模型连接 `motion_event_detailed`,并由 `GameScene` 消费远端 type 1/10 特效、type 7/8 显示隐藏和直接动作声。飞行仍只走服务端 `GC_CREATE_FLY`,避免把远端动作帧错误地变成客户端发包。
## 分支等价矩阵
| 40250 判据/副作用 | 当前实现 | 结论 |
|---|---|---|
| MSE/MDE 注册、解析、粒子曲线、混合和 one-shot 回收 | `effect_player`/`mse`/`mde`/`effect_registry` + FX 测试 | 基本等价;真实渲染后端像素证据仍有限 |
| 动作事件按 `StartingTime → dwFrame` 在每个动作 Actor 上触发 | native 以时间区间发信号,本地主角和有真实 animator 的远端 Actor 均接入详细事件 | 部分等价;时间精度/重复跨帧和占位 Actor 升级前后的完整逐帧对照仍未完成 |
| EFFECT 的独立、骨骼跟随、骨骼快照、根节点四分支 | 本地和远端真实 Actor 的 type 1 都用 `spawn_motion` 和 anchor | 部分等价;基础矩阵存在,但远端 target/清理和非详细 fallback 仍需逐分支对照 |
| EFFECT_TO_TARGET 的钓鱼、目标跟随、目标快照 | 本地主角 type 10 有目标 anchor,钓鱼直接跳过 | 部分等价;缺少 40250 fishing effect 生命周期/落点字段和目标失效后的统一回收证据 |
| FLY 读取 `.fly`、骨骼起点、目标、owner/skill,并执行 `OnShoot` | 当前 type 6 已保留 `fly_file`/`fly_pos`/`fly_bone` 并能传到 native 事件字典;`GC_CREATE_FLY` 的索引飞行已按 40250 注册表加载 `.msf`,动作事件仍从待发队列触发 `client.shoot` | 部分等价;索引资源参数已接入,动作事件 `.fly` 起点/owner/skill、动态碰撞和 attach/tail 仍未闭合 |
| SPECIAL_ATTACKING 持续时间、动态球、命中去重和结束清理 | 当前 parser/native 字典已保留 `duration_time`、攻击参数和碰撞球 | 部分等价;splash 的生产碰撞/去重/生命周期分支仍缺失,资产中实际存在 818 个 type 4 事件 |
| SOUND 直接动作声和 `.mss` 帧声 | 本地和远端详细事件都能触发 direct soundPlayerView/MobView 更新 `.mss` | 部分等价;频率/距离和路径转换尚未做逐事件对照 |
| SCREEN_WAVING/FLASHING 使用事件自身参数 | 当前固定 `shake(0.06, 9.0)` 和固定 0.12s 屏闪 | 不等价;type 2 的 `DuringTime``Power``AffectingRange` 没有保留/使用 |
| CHARACTER_SHOW/HIDE、WARP | 本地和真实远端 Actor 的 type 7/8 已按事件帧切换 actor model 可见性;type 9 由 `_apply_motion_warp` 执行目标前 2.70m、阻挡检查和朝向,本地再发送 `FUNC_WAIT` | 部分等价;真实 `seomjeon.msa`、允许/阻挡/无目标路径有回归,远端目标生命周期和 live 服务端状态包仍待验证 |
| 事件资源和模型从网络实体删除、切图、死亡时清理 | FX anchor 对局部无效目标会回收,map reset 会清飞行/实体 | 部分等价;没有所有 Actor 事件实例、splash、fishing effect 和跨图中断的统一清理矩阵 |
## 已执行测试
- `project/fx_test.gd`PASSMSE 解析、EffectPlayer 和 registry。
- `project/effect_playback_test.gd`:0 failures;播放时间和生命周期基础路径。
- `project/target_effect_test.gd`0 failures;目标特效跟随/快照基础路径。
- `project/skill_fx_test.gd`PASS;技能 FX 基础映射。
- `project/effect_color_test.gd``effect_emission_test.gd``effect_faces_test.gd``effect_lie_test.gd``effect_random_rotation_test.gd``effect_render_regression_test.gd``effect_rotation_test.gd``effect_scale_test.gd``effect_space_test.gd``effect_surface_test.gd``effect_texture_animation_test.gd`:均无断言失败;surface/texture animation 在 headless 下明确跳过像素检查。
- `project/motion_effect_anchor_test.gd`:断言显示 `failures=0`,但进程以 exit 139 结束并有 6 个 ObjectDB 泄漏,不能作为稳定通过证据。
- 资源静态统计:当前资产中的 `MotionEventType` 为 type 1=1892、2=263、4=818、6=268、7=47、8=47、9=47、10=197,共 3579 个事件;没有 type 5 样本,但不能因此删除 type 5 分支。
## Deep audit round 2026-09-21
### Fresh regression results
- `fx_test.gd``effect_playback_test.gd``target_effect_test.gd``skill_fx_test.gd` 及颜色、发射、面、lie、随机旋转、渲染回归、旋转、缩放、空间、surface、texture animation 测试均无断言失败;surface/texture 的 headless 像素检查明确跳过。
- `motion_effect_anchor_test.gd` 的断言为 0 failures,但进程 exit 139,并泄漏 6 个 ObjectDB;因此不能升级为稳定回归通过。
### Static parity conclusion
- MDE/MSE 基础解析、粒子曲线、材质/颜色/纹理动画和本地主角基础挂点存在,但 `MotionEvent` 仍未完整保留 FLY、SPECIAL_ATTACKING、SCREEN_WAVING 参数以及 CHARACTER_SHOW/HIDE/WARP 专用字段。
- `game_scene.gd` 主要连接本地主角;RemotePlayerView/MobView 的动作没有接入 40250 式统一 motion-event 分派,因此远端特效、飞行、音效、隐藏、震屏和 warp 仍不一致。
- 当前 type 4/7/8/9 生产分支缺失,资产统计分别发现 818、47、47、47 个事件;type 6 也未按参考 `.fly` 起点、目标、owner/skill 形成等价飞行实例。
- 基础 FX 断言通过不能覆盖 splash 命中去重、事件跨帧/loop、目标删除、死亡、地图切换和实例回收;挂点测试的 exit 139/泄漏需先修复。
### Active source group review
- 对 Visual Studio `EffectLib` 活跃编译单元逐组核对后,参考链补充确认:`CEffectData::LoadScript` 同时加载 mesh、particle、light 和 `.mss` 声音数据;`CEffectManager::RegisterEffect/CreateEffectInstance/Update/Render/Destroy` 负责缓存、实例池、更新/渲染顺序和销毁;`CParticleSystemInstance`/`CParticleInstance``CEffectMeshInstance``CLightInstance` 分别维护粒子、网格帧/纹理帧和光源生命周期。
- 当前端虽然有 `EffectPlayer`/MDE/MSE/registry 的格式和局部播放测试,但没有证明声音脚本、mesh/light 三类实例、固定池耗尽、注册失败和 `DestroySystem` 的顺序与参考一致;本组静态核对完成,合同仍为 `PARTIAL`
## 已确认差异和未验证项
1. 已将 `MotionEvent` 扩展为按 40250 事件类型保留专用数据,并让 `motion_event_detailed` 传完整字典;本地主角和真实远端 Actor 已消费 type 1/7/8,仍需证明所有分支使用这些字段。
2. 已把动作事件连接扩展到 `NetWorld` 创建的真实远端 PC/NPC/Mob;仍需把震屏、局部 FX、飞行和隐藏行为按本地相机与远端 Actor 的范围/目标语义完整分开。
3. 必须建立 `.msa` 事件快照测试:每种事件的字段、帧、跨帧/loop、重复触发和缺字段回退;并建立与 40250 样本资源的数量和时序基线。
4. 必须增加 SPECIAL_ATTACKING splash 的碰撞、命中去重、持续时间、死亡/删除/切图清理测试,以及 FLY `.fly` 的起点、目标、owner/skill 和回收测试。
5. `motion_effect_anchor_test` 的 exit 139 和 ObjectDB 泄漏需要先修复测试稳定性,才能把基础挂点证据提升为可靠回归门禁。
## Implementation fix round 2026-09-22 — dedicated MotionEvent payload and local show/hide
- 对照 `RaceMotionDataEvent.h` / `RaceMotionData.cpp``formats::MotionEvent` 现在独立保存:
- type 6 的 `FlyFileName``FlyPosition`、挂接骨骼;
- type 2 的 `DuringTime``Power``AffectingRange`
- type 4 的 `DuringTime``EnableHitProcess`、攻击参数和 `SphereData*`
- type 7/8/9 的启动时间和稳定的详细事件字典。
- `Metin2AnimPlayer::get_events()` / `motion_event_detailed` 已传出 `fly_*`、震屏参数、特殊攻击参数和 `collision_spheres`
- `GameScene` 已按 40250 的 type 7/8 事件帧调用本地 Actor 的 `__HideEvent` / `__ShowEvent` 等价接口;`PlayerView` / `MobView` 暴露同一 Actor 级可见性 seam。
- `NetWorld` 已按 40250 的每 Actor 事件处理边界连接远端 native animator`GameScene` 消费远端 type 1/10 FX、type 7/8 显示隐藏和 direct sound;远端 type 6 不发客户端包。
- 回归:`formats_msa_test`、真实 type 2/4/6/7 资源的 `motion_event_payload_test``motion_event_visibility_test``net_world_motion_event_test` 通过。
- 本轮仍为 `PARTIAL``.fly` 资源和飞行实例、splash 碰撞/去重/清理、type 9 warp、震屏范围和统一跨图清理没有完成。
## Implementation fix round 2026-09-22 — WARP event branch
- 对照 `GameLib/ActorInstanceMotionEvent.cpp::ProcessMotionEventWarp``ActorInstanceEvent.cpp::__OnWarp``GameScene::_apply_motion_warp` 已实现相同的 270cm 单位换算、目标前落点、地图阻挡拒绝、朝向目标和本地主角 `FUNC_WAIT` 状态回调;远端 Actor 也消费 type 9,但不伪造本地网络包。
- `project/motion_event_warp_test.gd` 使用真实 `seomjeon.msa` 验证 type 9,并覆盖可移动、阻挡、无目标三条分支;测试通过。
- 当前合同仍为 `PARTIAL`type 4 splash、type 6 `.fly` 数据权威、震屏参数/范围和所有 Actor 的跨图/死亡清理仍未闭合。
## 下一轮验证入口
- 先以本轮 `formats/msa`/native payload 为输入,实现按 Actor 的事件连接/路由。
- 以 type 4、6、7、8、9 的真实资产各选样本,补逐帧事件、碰撞、飞行、显示隐藏和 warp 测试。
- 统一本地/远端/怪物/切图/死亡的 FX 实例清理,并把 `fx_spawned`/`fx_finished` 纳入可审计事件日志。