Files

2.6 KiB
Raw Permalink Blame History

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 的不能拖
  • 新增 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。