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

14 KiB
Raw Blame History

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 保存 EffectFileNameEffectPositionAttachingEnableFollowingEnableIndependentFlag 和骨骼名。
    • EFFECT_TO_TARGET 保存目标偏移、跟随/钓鱼标志;FLY 使用独立的 FlyFileName/FlyPositionSPECIAL_ATTACKING 保存 DuringTimeEnableHitProcess、攻击参数和碰撞球。
    • 动作加载时还从同名 .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.cppEffectInstance.cppParticleInstance.cppPythonEffectModule.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.gdproject/fx/mde.gdproject/fx/mse.gdproject/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.gdproject/ui/mob_view.gd
    • 已按当前动作加载同名 .mss 并逐 60FPS 更新 3D 声音脚本;本轮还为远端真实视图提供统一的 animator signal 接入点。
  • project/ui/remote_player_view.gdproject/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 的 DuringTimePowerAffectingRange 没有保留/使用
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.gdPASSMSE 解析、EffectPlayer 和 registry。
  • project/effect_playback_test.gd:0 failures;播放时间和生命周期基础路径。
  • project/target_effect_test.gd0 failures;目标特效跟随/快照基础路径。
  • project/skill_fx_test.gdPASS;技能 FX 基础映射。
  • project/effect_color_test.gdeffect_emission_test.gdeffect_faces_test.gdeffect_lie_test.gdeffect_random_rotation_test.gdeffect_render_regression_test.gdeffect_rotation_test.gdeffect_scale_test.gdeffect_space_test.gdeffect_surface_test.gdeffect_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.gdeffect_playback_test.gdtarget_effect_test.gdskill_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/CParticleInstanceCEffectMeshInstanceCLightInstance 分别维护粒子、网格帧/纹理帧和光源生命周期。
  • 当前端虽然有 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.cppformats::MotionEvent 现在独立保存:
    • type 6 的 FlyFileNameFlyPosition、挂接骨骼;
    • type 2 的 DuringTimePowerAffectingRange
    • type 4 的 DuringTimeEnableHitProcess、攻击参数和 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 animatorGameScene 消费远端 type 1/10 FX、type 7/8 显示隐藏和 direct sound;远端 type 6 不发客户端包。
  • 回归:formats_msa_test、真实 type 2/4/6/7 资源的 motion_event_payload_testmotion_event_visibility_testnet_world_motion_event_test 通过。
  • 本轮仍为 PARTIAL.fly 资源和飞行实例、splash 碰撞/去重/清理、type 9 warp、震屏范围和统一跨图清理没有完成。

Implementation fix round 2026-09-22 — WARP event branch

  • 对照 GameLib/ActorInstanceMotionEvent.cpp::ProcessMotionEventWarpActorInstanceEvent.cpp::__OnWarpGameScene::_apply_motion_warp 已实现相同的 270cm 单位换算、目标前落点、地图阻挡拒绝、朝向目标和本地主角 FUNC_WAIT 状态回调;远端 Actor 也消费 type 9,但不伪造本地网络包。
  • project/motion_event_warp_test.gd 使用真实 seomjeon.msa 验证 type 9,并覆盖可移动、阻挡、无目标三条分支;测试通过。
  • 当前合同仍为 PARTIALtype 4 splash、type 6 .fly 数据权威、震屏参数/范围和所有 Actor 的跨图/死亡清理仍未闭合。

下一轮验证入口

  • 先以本轮 formats/msa/native payload 为输入,实现按 Actor 的事件连接/路由。
  • 以 type 4、6、7、8、9 的真实资产各选样本,补逐帧事件、碰撞、飞行、显示隐藏和 warp 测试。
  • 统一本地/远端/怪物/切图/死亡的 FX 实例清理,并把 fx_spawned/fx_finished 纳入可审计事件日志。