可拖拽判断(PickupItem 标记)

This commit is contained in:
2026-10-12 17:05:19 +08:00
parent 77fefd2505
commit 02c8d36f8d
6 changed files with 144 additions and 29 deletions
+55
View File
@@ -0,0 +1,55 @@
# 2026-10-12
## 可拖拽判断(PickupItem 标记)
**需求**:`scripts/item.gd` 提供 `can_pickup`,只有为 true 时才能拖拽。
**item.gd 本次改动**:加了 `class_name PickupItem` 和说明注释,功能不变。
```gdscript
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`。