2.6 KiB
2.6 KiB
2026-10-12
可拖拽判断(PickupItem 标记)
需求:scripts/item.gd 提供 can_pickup,只有为 true 时才能拖拽。
item.gd 本次改动:加了 class_name PickupItem 和说明注释,功能不变。
class_name PickupItem
extends Node
@export var can_pickup: bool = true
⚠ 关键发现:脚本挂载位置 ≠ 射线打中的节点
用户在 world/main.tscn 第 155 行把 item.gd 挂在了 SM_Bottle_12 上,但那个节点不是刚体。
实例来源 res://assets/Furniture_FREE-af040129/SM_Bottle_1.glb,实际结构是:
SM_Bottle_12 (Node3D) ← item.gd 挂在这一层(glb 实例的根)
└── SM_Bottle_1 (RigidBody3D) ← 射线实际打中的是这一层
├── SM_Bottle_1 (MeshInstance3D)
└── CollisionShape3D
实测确认:glb 内部节点的 owner 指向实例根(SM_Bottle_1.owner == SM_Bottle_12),
再往上为 null。所以「沿 owner 链向上找标记」正好一步命中。
实现(player_pickup.gd)
- 新增
@export var allow_unmarked: bool = false- false(默认)= 白名单:只有挂了
PickupItem且can_pickup=true的才拖得动 - true = 黑名单:没挂标记的默认可拖,只有显式设 false 的不能拖
- false(默认)= 白名单:只有挂了
- 新增
can_grab(collider) -> bool:先判is RigidBody3D,再查标记 - 新增
_find_item_marker(body):顺序 刚体自身 → 直接子节点 → owner 链向上- 向上时只查节点自身、不查子节点 —— 否则走到 World 层会遍历几百个子节点,而这是每物理帧都要跑的
_update_interact_state()的 hovering 判定改用can_grab()_grab()里再判一次(悬停状态来自上一物理帧,其间 can_pickup 可能已被改掉)
实测验证(headless,全部符合预期)
can_grab(SM_Bottle_1 RigidBody3D) = true ✓
can_grab(SM_Bottle_12 Node3D) = false ✓(不是刚体)
can_grab(Can2 内部刚体) = false ✓(严格/白名单模式)
allow_unmarked=true 后 Can2 = true ✓
⚠ 副作用(需告知用户)
默认白名单模式下,场景里所有没挂 item.gd 的刚体都拖不动了
(Can2、饭盒、水果等)。要么逐个挂标记,要么把 allow_unmarked 勾上。
MCP 备注
4 次 script_patch 均返回 replacements: 1 但伴随 gdscript_reload_failed (code 43)
假报错(fallback_line: true)—— 与 10-10 记录的是同一现象。
离线 --check-only 两个脚本均通过;PickupItem 已进 global_script_class_cache.cfg。