- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
20 KiB
combat.flying_projectile —— 飞行物、目标、地形碰撞、命中和回收
状态:PARTIAL
本合同覆盖 40250 客户端的两条飞行物来源:
- 普攻/弓箭的
.msa FLY事件 →OnSetFlyTarget/OnShoot→CG_FLY_TARGETING、CG_SHOOT。 - 服务端
GC_CREATE_FLY、GC_FLY_TARGETING、GC_ADD_FLY_TARGETING→ 客户端CFlyingManager弹道、目标追踪、背景/Actor 碰撞和爆炸回调。
40250 参考调用链
客户端发射事件
GameLib/ActorInstanceFly.cppOnGetFlyTargetPosition- 从目标 Actor 的飞行目标包围球读取位置。
UserInterface/PythonPlayerEventHandler.cpp:232CNormalBowAttack_FlyEventHandler_AutoClear::OnSetFlyTarget- 读取主角位置/朝向并发送
SendFlyTargetingPacket(target VID, target position)。
- 读取主角位置/朝向并发送
UserInterface/PythonPlayerEventHandler.cpp:241OnShoot- 在动作资源的 FLY 事件帧发送
SendShootPacket(skill)。
- 在动作资源的 FLY 事件帧发送
UserInterface/PythonPlayerEventHandler.cpp:259OnExplodingAtAnotherTarget- 若启用对应处理,向另一 Actor 发送攻击包并调用目标
OnShootDamage;当前参考实现该函数主体被直接return,必须保留这个可达性事实,不能凭注释臆造伤害链。
- 若启用对应处理,向另一 Actor 发送攻击包并调用目标
飞行数据和弹道
GameLib/FlyingData.cppCFlyingData::LoadScriptFile.fly资源读取 initial velocity、cone/roll、angular velocity、gravity、range、acceleration、homing、bomb effect、hit-on-background、hit-on-another-monster、pierce count、bomb range 和 attach data。
GameLib/FlyingInstance.cppCFlyingInstance::Create- 绑定
CFlyTarget、canAttack、起点、旋转方向、局部初速/加速度和剩余射程。
- 绑定
GameLib/FlyingInstance.cpp:347CFlyingInstance::Update- homing → 速度/加速度推进 → 维护 attach effect → 超程 → 目标线段命中 → 穿透/爆炸 → 其它 Actor 动态碰撞 → 地形高度/静态背景碰撞。
GameLib/FlyTarget.cppCFlyTarget- 目标可以是对象或坐标;对象被删除时转为最后目标位置,避免悬空指针。
GameLib/FlyingObjectManager.cpp- Update 返回 false 后从实例表删除;地图/场景销毁时删除全部实例和挂接特效。
网络收包
UserInterface/PythonNetworkStreamPhaseGame.cpp:2559RecvAddFlyTargetingPacket- 找不到射手时只记录并返回;存在目标 VID 时绑定对象,否则通过地图高度构造坐标目标。
:2599RecvFlyTargetingPacket- 与上面相同,但替换当前目标而不是追加。
:2697RecvCreateFlyPacket- 起点或终点实例不存在时不创建;否则
CreateIndexedFly(type, start, end)。
- 起点或终点实例不存在时不创建;否则
:2637-2695SendShootPacket、SendAddFlyTargetingPacket、SendFlyTargetingPacket- 每个发送操作后都发送 sequence。
当前端实现映射
project/fly_object.gdFlyData、FlyInstance、FlyManager对应CFlyingData/CFlyingInstance/CFlyingManager。- 已实现
.msf的CFlyingData::LoadScriptFile字段解析和 cm→m 单位适配,并保留目标对象/坐标、homing、线段到点命中、超程、地形高度/静态 Physics layer 2 碰撞、穿透计数、爆点和地图清理。
project/net_world.gd_on_fly按playersettingmodule.py的索引表加载.msf并消费服务端飞行 cue;_on_fly_targeting暂存 shooter 的目标队列;clear_for_map_change清理飞行物和爆点。- 服务端索引经验球通过
FLY_EXP加载ga_piece_yellow_small2.msf;离线spawn_exp_fly仍是独立的多球测试/表现辅助入口。
project/net_play.gd_emit_swing在弓箭起手时调用_send_fly_target,并由bow_shot_fired进入发射链。send_fly_targeting处理扇形/圆形目标追加。
project/world_shot.gd、project/fx/skill_fx.gd- 提供世界投射物/技能表现层。
extension/src/net/*- 当前 classic parser/store/client 提供飞行事件和目标坐标的网络入口。
分支等价矩阵
| 参考判据/副作用 | 当前实现 | 结论 |
|---|---|---|
.fly/.msf 资源读取飞行参数和 attach data |
FlyData.load_msf 解析参考字段、相对/绝对资源路径和 cm→m 单位;FlyInstance 已创建 AttachFile/FlyTrace,NetWorld._indexed_fly_data 绑定 40250 注册资源;远端 Actor 的 MSA FLY 也进入同一实例入口 |
部分等价;精确远端 skill 上下文、相机尾迹几何和所有资源矩阵仍缺 |
| 起点/终点实例缺失则不创建飞行物 | _on_fly 用 Vector3.INF/节点存在门控后返回 |
基本等价;完整 packet handler/sequence 仍未 live 验证 |
| 对象目标随 Actor 移动,目标删除后转位置 | FlyTargetAnchor.fly_target_position 使用包围球中心;notify_target_despawn 在渐隐/替换前立即转为位置目标 |
基本等价;迟到目标包和 VID 重用的包级矩阵仍未完成 |
| homing、速度/加速度、射程和线段到点命中 | FlyInstance._adjust_homing/update |
基本等价;坐标轴和单位转换需真实资源验证 |
m_fCollisionSphereRadius = max(move distance*2, configured radius) |
FlyInstance.update 在同一顺序计算膨胀半径,并通过 actor_provider 查询当前可见 Actor |
基本等价;Actor 碰撞球半径的真实模型标定仍未完全证明 |
hitonanothermonster + 已命中对象集合 + OnExplodingAtAnotherTarget |
FlyInstance._find_another_actor、hitted_objects、at_another 回调和穿透分支已接入;射手被排除 |
部分等价;EffectManager/真实动态 culling 仍未完全复现 |
| hit-on-background 先地形高度后静态背景碰撞 | 当前动态 Actor 分支后仍是 sample_height 后 Physics layer 2 ray |
基本等价的 Godot 平台适配;真实 culling 半径/查询范围未证明相同 |
| pierce count:命中后 bomb,减计数,耗尽才 explode | FlyInstance.pierce/target_hitted 与动态 at_another 分支均执行相同的 bomb/继续/爆炸顺序 |
基本等价;多种真实资源与同帧背景竞争仍未完整验证 |
OnShootDamage 仅在 canAttack 对象目标命中时触发 |
当前 shoot_damage signal 有入口,但服务端 GC 弹道使用 can_attack=false |
基本等价;真实本地弓箭事件到服务端回包链未完整证明 |
超程 OnExplodingOutOfRange、背景/目标回调和删除 |
exploded signal + manager 删除;BombEffect 优先走 EffectRegistry,资源不可用才 fallback flash |
基本等价;EffectManager 真实生命周期和全部回调包序仍未完整验证 |
| Add/Set FlyTargeting 追加与替换 | 入站通过 shooter meta fly_target_queue、append;出站普通弓箭走独立 fly_targeting/CG_FLY_TARGETING,附加目标走 add_fly_targeting/CG_ADD_FLY_TARGETING |
基本等价;shooter 不可见/本地主角/VID 重用分支待验证 |
GC_CREATE_FLY indexed normal/firecracker/auto-fire 类型 |
INDEXED_FLY_TYPES 与 40250 注册表一致;_on_fly 已分别复刻普通、随机位置烟花和 +100cm AUTO_FIRE 起点,AttachFile 优先作为实际视觉 |
基本等价;资源缺失 fallback、真实模型半径和包序仍未完全证明 |
| 每个 outbound 飞行目标/射击包后发送 sequence | ClassicSession 字节级验证 CG_FLY_TARGETING、CG_ADD_FLY_TARGETING、CG_SHOOT 均追加正确 sequence;完整动作队列与真实 socket 包序仍未 live 验证 |
部分等价 |
已执行测试
project/fly_test.gd:PASS;覆盖CFlyingManager/CFlyingInstance基础弹道、命中、超程和__CanShot。project/fly_reset_test.gd:PASS;覆盖地图切换时飞行物和爆点清理。project/test_exp_fly_parity.gd:6/6 PASS;覆盖经验光球飞行和吸收。project/fly_target_bounds_test.gd:0 failures;覆盖模型目标包围球中心和 CPU/GPU 一致性,但有 ObjectDB leak warning。project/test_click_target_effect_parity.gd:0 failures;覆盖静态障碍碰撞和目标效果清理,但有 ObjectDB/RID 清理警告。project/combat_fx_test.gd:PASS;覆盖命中/连击/效果主链。project/fly_data_msf_test.gd:PASS;覆盖geompung.msf、经验球和arrow_01.msf的字段、相对资源路径、attach data、枚举及 cm→m 转换。project/indexed_fly_type_test.gd:PASS;覆盖 40250 NORMAL/FIRE_CRACKER/AUTO_FIRE 的索引类型、起点高度、对象/位置目标和随机距离范围。project/fly_dynamic_collision_test.gd:PASS;覆盖动态 Actor 双目标穿透和同一 Actor 的HittedObjectSet去重。project/fly_target_lifecycle_test.gd:PASS;覆盖包围球目标中心和 Actor 销毁时立即转位置目标。project/fly_attachment_effect_test.gd:PASS;覆盖绝对 AttachFile 解析、EffectRegistry 实例、FlyTrace 轨迹、BombEffect 和资源视觉不重复绘制。project/remote_motion_fly_test.gd:PASS;覆盖远端 ActorGC_FLY_TARGETING队列、MSA FLY 事件、owner VID/skill 和资源飞行实例创建。
Implementation fix round 2026-09-21T06:34Z
按 FlyingObjectManager::CreateIndexedFly 接入 INDEXED_FLY_TYPES:NORMAL 保持射手根位置和对象目标,FIRE_CRACKER 使用 +200cm 起点、-90° 基准方向与 20..50m/10..25m 随机位置目标,AUTO_FIRE 使用 +100cm 起点并保留对象目标。新增 indexed_fly_type_test.gd,修复前 FIRE_CRACKER/AUTO_FIRE 断言失败,修复后通过。
按 FlyingInstance::Update 接入动态 Actor 碰撞:每帧使用 max(move_distance * 2, collision_sphere_radius),排除 owner,按节点实例身份维护 hitted_objects,触发 at_another 并复刻 pierce/bomb/explode 分支。按 FlyTarget/ActorInstanceFly 接入 FlyTargetAnchor 包围球中心,并在 NetWorld 的 despawn、同 VID 替换前调用 notify_target_despawn,保证渐隐节点不再继续移动飞行目标。新增 fly_dynamic_collision_test.gd 和 fly_target_lifecycle_test.gd,均通过。
修复后 fly_test、fly_reset_test、fly_data_msf_test、motion_event_fly_test、test_exp_fly_parity、netbridge_test、combat_fx_test、netplay_test 和静态建筑碰撞回归通过。真实 EffectManager/FlyTrace 资源表现、真实模型动态半径、包序和不可见 Actor 生命周期仍使合同保持 PARTIAL。
Implementation fix round 2026-09-22T07:55Z
复核 CFlyingManager::DeleteAllInstances 的销毁边界时发现,当前 FlyInstance.handler 闭包捕获实例自身,而实例又持有该 Callable;地图清理只清数组会留下 RefCounted 环。现新增 _release_instance(),在地图清理和自然过期两个删除路径中同步解除 handler、target、world、data 引用,再移除实例;地图清理的 visual/flash 节点也按 Loading 的同步删除边界直接释放。
修复前 fly_reset_test.gd 虽 PASS 但退出报告 5 个 ObjectDB 泄漏;修复后退出无 ObjectDB 泄漏。test_exp_fly_parity.gd 行为断言仍为 6/6 PASS,但其旧式单函数 fixture 仍报告 3 个 ObjectDB/不在 SceneTree 的路径警告,属于测试 harness 生命周期待清理,不将其伪装成完全干净证据。动作事件 FLY 的 owner/skill/包序、真实模型半径、远端不可见 Actor 和 EffectManager 全量生命周期仍保持 PARTIAL。
Implementation fix round 2026-09-21T06:46Z
继续按 40250 FlyingData::LoadScriptFile、FlyTrace 和 FlyingObjectManager 的资源链修复飞行特效:TailLength 按参考保持秒单位,TailSize 只做 cm→m 转换;AttachData 现在会实例化 AttachFile,并维护 line/multi-line/sine/exp 四类尾迹历史和按时间衰减的轨迹几何。BombEffect 优先通过 EffectRegistry 资源实例化,只有资源不存在时才使用兼容性 flash,避免资源特效和通用球体重复绘制。
同时修复 EffectRegistry 对 .msf 内绝对资源路径的解析:存在的绝对路径不再被重复拼接到资源根目录。新增 fly_attachment_effect_test.gd 覆盖 AttachFile 实例、FlyTrace 历史、BombEffect、资源视觉去重和地图清理;fly_data_msf_test.gd 增加 TailLength 单位回归。资源特效链已进入正式运行路径,但精确相机朝向、所有远端 FLY/SHOOT owner-skill 组合、EffectManager 的每种资源生命周期和真实包序仍未完成,因此合同继续保持 PARTIAL。
Implementation fix round 2026-09-21T06:54Z
按 40250 CActorInstance::ProcessMotionEventFly 补齐远端 Actor 的动作事件飞行链:NetWorld 现在从 GC_FLY_TARGETING/GC_ADD_FLY_TARGETING 保存的当前目标队列取目标,在远端 MSA 的 type 6 事件帧创建同一个 .msf 飞行实例,使用 Actor 动作位置/挂骨骼位置作为起点,并传递 owner VID 与 skill index;对象目标、坐标目标和目标队列消费均保持分支语义。GC_CREATE_FLY 的服务端索引飞行仍与动作事件路径分离。
新增 remote_motion_fly_test.gd 覆盖远端目标队列、资源实例、owner/skill 和单次消费,修复后的测试无 ObjectDB 泄漏。远端动作事件没有原生 m_kCurMotNode.uSkill 的完整网络字段时仍只能使用事件字段或当前动作参数回退,精确 skill 上下文、包序、不可见 Actor 和全量资源矩阵继续保持 PARTIAL。
Implementation fix round 2026-09-21T06:59Z
按 40250 CNormalBowAttack_FlyEventHandler_AutoClear::OnSetFlyTarget 与 CPythonNetworkStream::SendFlyTargetingPacket 修正出站主目标链:新增 M2Client.fly_targeting、ClassicSession::send_fly_targeting 和 Godot 绑定,普通弓箭不再误发 CG_ADD_FLY_TARGETING;扇形/圆形技能的附加目标继续使用独立的 add_fly_targeting。ClassicSession 回归逐字节验证两种 header 和 sequence,战斗回归验证弓起手优先调用主目标 API。
该修复只闭合了主/附加目标包类型和局部 sequence 证据;不可见 shooter、VID 重用、真实 socket 中乱序/重连和完整 MSA→CG_SHOOT 时序仍保持 PARTIAL。
Implementation fix round 2026-09-21T07:03Z
继续核对 FlyingInstance::UpdateAttachInstance 后修正尾迹几何:40250 的
MULTI_LINE、SINE、EXP 分支使用 (-sin(roll) * offset, 0, -cos(roll) * offset),当前端原先把局部 Z 号写反,导致资源飞行物的尾迹/AttachFile
偏到相反一侧;现已改为同号。LINE 的 effect 朝向按参考只使用基础飞行旋转,其他三种
偏移类型才叠加 m_qAttachRotation。新增直接检查 MULTI_LINE 局部 Z 偏移的回归断言。
fly_attachment_effect_test、fly_data_msf_test、fly_test、fly_dynamic_collision_test、
fly_target_lifecycle_test、fly_reset_test 均通过。coneangle/rollangle 初始旋转、
相机相关 FlyTrace 顶点生成、完整资源矩阵和远端原生 skill 上下文仍未闭合,合同保持 PARTIAL。
Active GameLib flying-core review
GameLib/FlyingData.h定义.fly的初速、cone/roll、角速度、重力、加速度、homing、range、bomb effect、background/another-monster 命中、pierce、collision sphere 和 attach tail;FlyingInstance.h保存速度/旋转/剩余射程、目标、已命中集合和 attach effect 生命周期。FlyingObjectManager.h按 index/type 注册飞行资源,创建/更新/渲染/删除实例;FlyTrace.cpp则用动态池记录位置时间 deque,按 tail length 衰减并生成相机相关的飞行尾迹。当前fly_object.gd已按索引读取.msf,接入动态 Actor 碰撞/已命中集合、AttachFile/FlyTrace、BombEffect 资源清理和远端 MSA FLY 创建;精确相机朝向、模型半径标定、全部资源组合和远端原生 skill 上下文仍缺证据。- 本轮完成
FlyTarget/FlyingData/FlyingInstance/FlyingObjectManager/FlyTrace的活跃源码静态核对;测试通过只证明基础 Godot 弹道,不提升合同状态。
已确认差异和未验证项
- 普通 GC 飞行物已从 40250 索引注册表加载
.msf建立FlyData,本地主角和远端 Actor 的动作事件 type 6 都进入同一实例入口;远端原生m_kCurMotNode.uSkill上下文、动作/网络包序仍未闭合。 hitonanothermonster已接入动态 Actor 碰撞、按节点身份的HittedObjectSet去重、穿透后继续飞行和另一目标回调;真实客户端的 culling 范围、模型动态半径和背景/Actor 同帧竞争仍未证明一致。- 正式目标命中已经使用
FlyTargetAnchor包围球中心,目标销毁也会转为最后坐标;.fly的 bomb range/collision sphere 与真实模型资源组合仍没有逐类标定矩阵。 _on_fly_targeting只把目标队列挂在可见 shooter 节点;40250 的CPythonCharacterManager实例表与当前NetWorld可见节点表不是同一生命周期,远端暂不可见、主角本地节点、VID 重用和迟到GC_CREATE_FLY尚未证明一致。bombeffect已优先走EffectRegistry,AttachFile/FlyTrace 也已进入飞行实例生命周期;精确相机尾迹、颜色/材质、所有资源组合和 EffectManager 的逐资源回收仍未证明一致。- 已有测试覆盖
.msf字段、索引类型、动态 Actor 双目标穿透/去重、目标删除、AttachFile/FlyTrace/BombEffect、主/附加目标 header+sequence 和地图清理;仍缺 Add/Set 乱序、真实服务端包序、完整资源矩阵和背景/Actor 同帧竞争。
Audit round 2026-09-21T06:24Z
本轮沿 FlyingObjectManager::CreateIndexedFly、FlyingInstance::Update、FlyTarget::NotifyTargetClear 和 CActorInstance::OnGetFlyTargetPosition 做了源码级闭环核对,并验证了当前资源目录包含 40250 注册的 7 个 FIRE_CRACKER .msf、1 个 AUTO_FIRE .msf 以及普通索引 .msf。
playersettingmodule.py的 16 个索引资源路径已在当前INDEXED_FLY_FILES中出现,但类型元数据没有进入当前表;NetWorld._on_fly所有索引都统一创建普通对象目标,未复刻 FIRE_CRACKER 的+200cm起点/随机位置目标和 AUTO_FIRE 的+100cm起点。- 40250 每帧按
max(move_distance * 2, collision_sphere_radius)扫描动态 Actor,并用HittedObjectSet去重;当前已按同一顺序实现 provider 查询、owner 排除、去重和at_another分支,但 provider 的可见性边界与真实模型半径仍是适配层差异。 - 40250 目标 Actor 销毁时立即
ClearFlyTargeter(),飞行物切换为最后目标坐标;当前_on_despawn先走渐隐/延迟释放,FlyInstance仍持有旧 Node3D,目标删除、同 VID 重建和迟到GC_CREATE_FLY尚未达到同一时序。 - 40250 对象目标位置来自
CActorInstance::OnGetFlyTargetPosition的包围球中心;当前FlyInstance.target_position()读取 Node3D 根位置,fly_target_bounds_test的包围球 helper 尚未接入正式飞行命中链。 - 40250 的
bombeffect、attach/tail 和 FIRE_CRACKER/AUTO_FIRE 的资源表现由EffectManager/FlyTrace管理;当前只生成通用 SphereMesh/flash,资源路径虽能解析但尚未真正消费。
因此本轮没有提升合同状态;当前结论仍为 PARTIAL。资源消费、动态碰撞和目标生命周期已从“缺失”推进到“有运行证据的部分等价”,后续应优先补远端动作事件 FLY、包序/乱序、不可见 Actor 与完整资源生命周期矩阵。
下一轮验证入口
- 为动作事件 FLY 增加远端 Actor 的原生 skill 上下文和
CG_FLY_TARGETING → CG_SHOOT → sequence真实状态机/包序测试。 - 增加背景与 Actor 同帧竞争、三 Actor/多种 collision sphere 的真实可见性与半径矩阵。
- 增加
GC_FLY_TARGETING → GC_ADD_FLY_TARGETING → GC_CREATE_FLY乱序、不可见 shooter、目标删除/VID 重用和 map reset 测试。 - 对照每个
INDEX_FLY_TYPE的 AttachFile、BombEffect、尾迹材质、颜色和回收时序,补齐资源矩阵后再决定是否提升合同状态。