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

11 KiB
Raw Blame History

movement.click_collision

Scope

审计鼠标点地、点实体、掉落物优先级、预约移动、地形 ATTRIBUTE_BLOCK、Actor 碰撞、滑动/停止和输入门控。

Reference call chain

  • CPythonPlayer::NEW_SetMouseState
    • 按绑定分派 MOVE、SMART、CAMERA、AUTO、ATTACK、SKILL。
  • __OnPressSmart / __OnClickSmart
    • 优先级为 item -> actor -> ground -> screen;按住和点击分别进入移动/攻击/交互分支。
  • __OnPressGround / __OnClickGround
    • 清预约和自动攻击,取消钓鱼,检查 MOVABLE_GROUND_DISTANCE,调用 NEW_MoveToDestPixelPositionDirection;被 gate 拒绝时使用 __ReserveClickGround 延迟重试。
  • CInstanceBase::NEW_Goto / NEW_Stop
    • 检查 syncing、moving skill、lock;设置 Src/Dst/rotation/m_isGoing,到达或停止时结束 walking。
  • CInstanceBase::CheckAdvancing
    • Actor collision 使用真实 collision sphere;逐个 TestActorCollisionAdjustDynamicCollisionMovement,第二个碰撞或调整后仍相交时 BlockMovement
    • 对移动段和下一位置检查 ATTRIBUTE_BLOCK

Current call chain

  • PlayerController._on_click
    • ground_items.pick_at 优先,其次对 pickables 做射线/AABB 点选,最后做地面射线求交。
  • PlayerController._goto / _process
    • 检查 active/frozen/locked/moving_skill 和近距离门;被 gate 拒绝时保存目标并在 0.1 秒后重试。
    • 点地使用 Src/Dst;WASD/移动端输入取消点地目标。
  • PlayerController._get_blocking_actor / _actor_blocked
    • 使用固定 ACTOR_BODY_RADIUS_M、距离门和趋近条件;静态 Actor 阻挡,其他 Actor 尝试切线滑移。
  • PlayerController._blocked
    • 通过 Metin2World.is_blocked 查询地形/静态阻挡;阻挡时尝试 x/z 分轴推进。
  • GameScene._on_ground_clicked
    • 生成 40250 EFFECT_PICK / click.mse 地面点击特效。

Field-level findings

  • 40250 的 NEW_SetMouseState 区分 MBS_PRESSMBS_CLICKSmart 按住会持续执行 __OnPressSmart,可持续预约地面/Actor/物品;松开才执行 __OnClickSmart。当前 PlayerController._unhandled_input 只处理左键按下,未实现同一条鼠标 Smart press/hold 状态机,因此按住鼠标连续移动、按住点 Actor/物品和 screen-direction 分支不能证明与参考一致。
  • 40250 的 Smart 优先级是 item -> actor -> ground -> screen,并且 actor/item 在按住与点击时走不同的预约/攻击/拾取逻辑。当前单击优先级已映射,但 actor 分支直接 target_selecteditem 通过 signal 进入 NetPlay;没有统一记录 press/click/auto 三种组合的发送包序列。
  • 40250 NEW_CanMoveToDestPixelPosition 只拒绝当前坐标相同;当前 _goto 以固定 1m 门提前成功返回,门值来自近似常量,不是从 CPythonPlayer::SetMovableGroundDistance 的运行时状态读取。
  • 40250 Actor 碰撞使用当前运动模式选择 body/defending sphere,并在每个真实碰撞球对之间做距离门、趋近判定;当前 _get_blocking_actor 固定 0.55m 半径,且 _actor_blocked 只把部分 meta kind 当作静态阻挡,远端骨骼/坐骑/多个 collision sphere 的分支未复刻。
  • 40250 的物体碰撞在 __TestObjectCollision 命中后走 AvoidObject/AdjustCollisionMovement,若仍阻挡则 BlockMovement 清除运动;当前对 terrain/Actor 采用一次 frame 的 direct、切线和 x/z 分轴推进,可能留下 _is_going 或继续发移动状态,需单独验证被阻挡终止时序。
  • 当前 _ray_ground 以每 1m 射线采样、最多 80m,再二分地表;40250 GetPickingPointWithRay 先对象 picking,再 terrain/object height,且射线范围由相机 ray 配置,采样边界和桥/台阶优先级不同。

Branch matrix

场景 40250 当前端 结论
点选优先级 item -> actor -> ground;点击和按住路径不同 item -> actor -> ground;左键 click 已接入 MAPPED,按住/自动模式仍需完整 fixture
近距离点地 小于 MOVABLE_GROUND_DISTANCE 直接不动 固定 1m MOVABLE_GROUND_DISTANCE_M PARTIAL:默认值/运行时设置未完全证明
lock/sync/moving skill 拒绝平移;moving skill 可转向;失败点地预约 0.1s 三门和预约重试已实现 MAPPED + tests
地形阻挡 对 movement segment 分段检查 ATTRIBUTE_BLOCK,下一位置命中则 BlockMovement is_blocked + direct/分轴推进 PARTIAL:采样步长和 BlockMovement 时序不同
Actor 碰撞 使用每个 MSM 的真实 collision sphere;动态调整;第二次碰撞直接停 固定 body 半径近似,按 static/动态尝试滑移 GAP:碰撞几何和多碰撞分支不 1:1
点地特效 成功 NEW_Goto 后显示 EFFECT_PICK ground_clicked 连接 click.mse MAPPED
空白点击停止 无 item/actor/ground 时 __OnClickSmart -> NEW_Stop _ray_ground 未命中时 _on_click -> stop() MAPPED + regression
输入中断 失焦/停止/攻击/技能清除预约或按键状态 _notificationstop、NetPlay 入口已接入 PARTIAL:窗口/网络中断组合待测

Equivalence matrix

项目 状态 证据
Preconditions MAPPED active、player、camera、world 和三道移动 gate 已定位
Branch structure PARTIAL Smart/Auto/Camera/Attack/Skill 绑定和两种鼠标状态尚未全部覆盖
Algorithms/formulas PARTIAL 转向/距离/到达算法已映射;collision sphere 与 segment sampling 是近似
State transition order PARTIAL 预约、输入、碰撞、动画和网络状态的交错顺序需真实包/场景 fixture
Constants/units PARTIAL 1m 近距离门、固定 0.55m body 半径、8m distance gate 未完全由资源证明
Timing/event sources PARTIAL 参考端 frame collision/update 与当前 Godot _process 同步点不同
Resource/data sources PARTIAL 参考端 .msm sphere/bone collision,当前移动层没有直接消费全部骨骼碰撞数据
Protocol side effects MAPPED ground/actor/item/skill 入口已接入 NetPlay;完整发送包序列仍需 fixture
Interruption/failure/cleanup PARTIAL 点地被阻挡、实体删除、失焦、死亡、换图中断和连续点击待测

Regression evidence

  • project/player_move_test.gdSrc/Dst、三道 gate、预约、Actor 阻挡/死亡/skip collision、坐骑转向、空白点击停止旧目标。
  • project/movement_parity_test.gd:转向阻尼、到达精度和切线碰撞入口。
  • project/netplay_test.gdCLICK_ACTOR、CLICK_ITEM、CLICK_POSITION、USE_SKILL 预约分支。
  • project/test_click_target_effect_parity.gd:点地、目标光圈和飞行物静态障碍测试。
  • 本轮四项回归均退出码通过;其中 click/target/fly 测试仍报告 3 个 ObjectDB 泄漏和 1 个资源退出时仍在使用,说明效果节点的清理虽满足断言,但退出生命周期尚不干净。

Deep audit round 2026-09-20

本轮按 40250 鼠标输入、移动门、碰撞和地面 picking 的真实调用链复核:

  1. 40250 NEW_SetMouseState 读取鼠标绑定后,对 Smart 分支在 MBS_PRESSMBS_CLICK 分别调用 __OnPressSmart/__OnClickSmart;按住期间每帧可持续执行 item/actor/ground/screen 分支,并维护 m_isSmtMov。当前 PlayerController::_unhandled_input 只处理 InputEventMouseButton.pressed,没有左键 release、hold tick 或 MBS_PRESS 状态,因此“按住点地持续走”“按住实体持续攻击/交互”“Smart release 停止”不能视为已实现。
  2. 参考 __OnClickSmart 在没有拾取对象/地面时明确 NEW_Stop();当前 _on_click 已在 _ray_ground 未命中时调用 stop(),清除旧的 _is_going、预约目标和按键状态,避免 click miss 后继续移动。player_move_test.gd 已增加回归。
  3. 40250 NEW_CanMoveToDestPixelPosition/CheckAdvancing 使用当前 Actor motion 的真实 collision sphere、对象碰撞 AvoidObject/AdjustCollisionMovement,第二次碰撞或调整后仍相交时 BlockMovement;当前只用固定 ACTOR_BODY_RADIUS_M、静态 kind 判断、一次切线再 x/z 分轴推进,没有第二碰撞停止状态,也没有从 MSM 读取每个骨骼碰撞球。
  4. 当前 _ray_ground 以每 1m、最多 800m 的高度采样后二分;参考 GetPickingPointWithRay 由 terrain/object picking 和相机 ray 边界决定,桥、台阶和对象 height 的优先级不是同一算法。现有测试只用假 world 高度和静态 obstacle,未覆盖真实 bridge/object/attribute 边界。
  5. 四组回归均通过:player_move_test.gdmovement_parity_test.gdnetplay_test.gdtest_click_target_effect_parity.gd;最后一项退出时仍有 3 个 ObjectDB 泄漏和 1 个资源使用警告,说明效果断言通过不等价于退出/中断生命周期已清理。

Active EterLib picking-core review

  • EterLib/GrpCollisionObject.cpp 支持 bound box、cube、indexed mesh、mesh triangle、sphere 和 cylinder 的 ray intersectionRay.h 固定 start/direction/range/end 的单位与边界,lineintersect_utils.cpp 还提供线段/平行线最近点和调整算法。
  • 当前端点击/弹道分别使用 Godot ray/AABB/胶囊和简化地面采样;虽然 test_click_target_effect_parity 断言通过,但没有复用参考端同一 mesh-triangle/球柱体/线段最近点优先级,不能标记 picking 一致。
  • 本轮完成 GrpCollisionObject/Ray/line intersection 静态核对,点击碰撞合同继续为 PARTIAL

Status

PARTIAL。当前实现的输入/预约主链已建立,但碰撞几何、逐 Actor 调整和多碰撞停止条件与 40250 仍有结构差异;本轮不做无证据的算法替换。

Audit round 2026-09-21T04:35Z — collision geometry boundary

本轮重新沿 CActorInstance::CheckAdvancingTestActorCollisionAvoidObject/BlockMovementCMapOutdoor::GetPickingPointWithRay 对照 PlayerController::_process_get_blocking_actor_blocked_ray_ground

  • 40250 的阻挡判定来自 .msm 每个 Actor 的真实 collision sphere,并且在移动段/下一位置分别检查,动态调整后再次相交会进入 BlockMovement;当前端仍是固定 0.55m body 半径、单次切线和 x/z 分轴近似。
  • 40250 的点地射线由 terrain/object picking 和相机 ray range 决定;当前端仍是最多 800m、每 1m 高度采样后二分,桥/台阶/object height 的优先级没有 1:1 数据来源。
  • 现有 player_move_test.gdmovement_parity_test.gdnetplay_test.gdtest_click_target_effect_parity.gd 全部通过;最后一项仍有 3 个 ObjectDB 泄漏和 1 个资源退出警告,因此只证明当前 fixture 的行为,不证明真实碰撞资源和退出清理等价。

结论:本轮没有实施替换;合同继续 PARTIAL。下一轮应优先建立真实 .msm sphere/object picking fixture,再决定是否下沉共享碰撞几何,而不是继续调固定半径。