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

110 lines
11 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.
# 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;逐个 `TestActorCollision``AdjustDynamicCollisionMovement`,第二个碰撞或调整后仍相交时 `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_PRESS``MBS_CLICK`Smart 按住会持续执行 `__OnPressSmart`,可持续预约地面/Actor/物品;松开才执行 `__OnClickSmart`。当前 `PlayerController._unhandled_input` 只处理左键按下,未实现同一条鼠标 Smart press/hold 状态机,因此按住鼠标连续移动、按住点 Actor/物品和 screen-direction 分支不能证明与参考一致。
- 40250 的 Smart 优先级是 item -> actor -> ground -> screen,并且 actor/item 在按住与点击时走不同的预约/攻击/拾取逻辑。当前单击优先级已映射,但 actor 分支直接 `target_selected`item 通过 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 |
| 输入中断 | 失焦/停止/攻击/技能清除预约或按键状态 | `_notification``stop`、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.gd`Src/Dst、三道 gate、预约、Actor 阻挡/死亡/skip collision、坐骑转向、空白点击停止旧目标。
- `project/movement_parity_test.gd`:转向阻尼、到达精度和切线碰撞入口。
- `project/netplay_test.gd`CLICK_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_PRESS``MBS_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.gd``movement_parity_test.gd``netplay_test.gd``test_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 intersection`Ray.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::CheckAdvancing``TestActorCollision`
`AvoidObject`/`BlockMovement``CMapOutdoor::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.gd``movement_parity_test.gd``netplay_test.gd``test_click_target_effect_parity.gd` 全部通过;最后一项仍有 3 个 ObjectDB 泄漏和 1 个资源退出警告,因此只证明当前 fixture 的行为,不证明真实碰撞资源和退出清理等价。
结论:本轮没有实施替换;合同继续 `PARTIAL`。下一轮应优先建立真实 `.msm` sphere/object picking fixture,再决定是否下沉共享碰撞几何,而不是继续调固定半径。