From 3920b880410083171069f36368c4782240ef55c4 Mon Sep 17 00:00:00 2001 From: yvmou_ Date: Fri, 9 Oct 2026 15:14:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=BA=E9=95=BF=E6=8C=89?= =?UTF-8?q?=E6=8B=96=E6=8B=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .workbuddy/memory/2026-10-09.md | 7 +++++++ character/player/player.gd | 3 +++ character/player/player.tscn | 4 ++-- character/player/player_pickup.gd | 33 ++++++++++++++++++++----------- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/.workbuddy/memory/2026-10-09.md b/.workbuddy/memory/2026-10-09.md index ff9eb88..232f1fc 100644 --- a/.workbuddy/memory/2026-10-09.md +++ b/.workbuddy/memory/2026-10-09.md @@ -19,3 +19,10 @@ - 场景格式注意:本项目的 .tscn 节点带非标准 `unique_id=`(Godot 4.7 特性),`unique_id` 与 `load_steps` 均可省略;`paint_panel.tscn` 就完全没写,可作参照。 - 已确认无外部脚本引用被搬走的属性(仅 `world/main.tscn` 通过场景 uid 引用玩家场景)。 - 顺手去掉了原 `_pickObject` 里的两行 `print()` 调试输出。 + +### 拾取改为长按拖拽 +- `player_pickup.gd`:由「按一下切换」改为「按下抓起、松开放下」——`is_action_pressed("carry")` → `_grab()`,`is_action_released("carry")` → `_drop()`。 +- `carry` 动作确认绑定鼠标左键(`button_index: 1`);项目里另有未被任何脚本使用的 `throw_carry`(绑 Q 键),可作投掷功能的预留。 +- 加了 `Input.mouse_mode != MOUSE_MODE_CAPTURED` 早退,并在 `player.gd` 重新锁定鼠标那一支补 `get_viewport().set_input_as_handled()`,避免「点一下重新锁定鼠标」顺带抓起物体(两种分支执行顺序下都成立)。 +- `physics_step()` 与 `has_object()` 改用 `is_instance_valid()` —— 防止拖拽途中物体被 destroy 时取属性报错。 +- `_drop()` 保留线速度(松手有惯性 / 可甩出);想要「就地放下」需补 `set_linear_velocity(Vector3.ZERO)`。 diff --git a/character/player/player.gd b/character/player/player.gd index 44facd6..b8ea464 100644 --- a/character/player/player.gd +++ b/character/player/player.gd @@ -53,6 +53,9 @@ func _unhandled_input(event: InputEvent) -> void: elif event is InputEventMouseButton and event.pressed \ and Input.mouse_mode != Input.MOUSE_MODE_CAPTURED: set_mouse_captured(true) + # 吃掉这一下点击:它的意义只是「把鼠标锁回游戏」, + # 不该顺带触发拾取 —— 否则重新聚焦时会凭空抓起一个物体。 + get_viewport().set_input_as_handled() func set_mouse_captured(captured: bool) -> void: diff --git a/character/player/player.tscn b/character/player/player.tscn index c2c581c..a249790 100644 --- a/character/player/player.tscn +++ b/character/player/player.tscn @@ -33,11 +33,11 @@ shape = SubResource("CapsuleShape3D_rkbax") transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.6, 0) [node name="Camera" type="Camera3D" parent="CameraRig" unique_id=52294557] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.6066488, -0.11179829) +transform = Transform3D(5, 0, 0, 0, 5, 0, 0, 0, 5, 0, -0.6916404, -0.11179829) [node name="InteractionRay" type="RayCast3D" parent="CameraRig/Camera" unique_id=388601766] transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 0, 0) -target_position = Vector3(0, 0.45, 0) +target_position = Vector3(0, 0.5, 0) [node name="InteractionPos" type="Marker3D" parent="CameraRig/Camera" unique_id=816323614] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.44393378) diff --git a/character/player/player_pickup.gd b/character/player/player_pickup.gd index 6174d3f..c73ef77 100644 --- a/character/player/player_pickup.gd +++ b/character/player/player_pickup.gd @@ -1,11 +1,12 @@ class_name PlayerPickup extends Node -## 拾取组件:射线看中的 RigidBody3D 会被吸附到 InteractionPos 前。 -## 只管「拿 / 放 / 拖」,不碰移动与视角。 +## 拾取组件:**按住左键拖拽,松手放下**。 +## 按下时用射线抓起正前方的 RigidBody3D,按住期间持续吸在 InteractionPos 前, +## 松开左键即放开。只管「抓 / 拖 / 放」,不碰移动与视角。 ## ## 接法:Player 下挂一个普通 Node,脚本挂上去;由 Player._ready() 注入射线与持握点。 -## 拖拽力度:物体被拉向持握点的速度系数 +## 拖拽力度:物体被拉向持握点的速度系数。越大跟手越紧,但过大容易来回抖。 @export var pick_power: float = 10.0 var _current: RigidBody3D = null @@ -20,24 +21,31 @@ func setup(ray: RayCast3D, hold_pos: Marker3D) -> void: func _unhandled_input(event: InputEvent) -> void: + # 只有鼠标锁进游戏(真正在玩)时才响应拖拽。 + # 否则「点一下把鼠标锁回来」的那次点击会顺带抓起一个物体。 + if Input.mouse_mode != Input.MOUSE_MODE_CAPTURED: + return + # 长按拖拽:按下抓起,松开放下。 + # 中间不需要任何输入处理 —— 物体靠 physics_step() 每物理帧持续吸住。 if event.is_action_pressed("carry"): - if _current == null: - _pick() - else: - _release() + _grab() + elif event.is_action_released("carry"): + _drop() ## 每物理帧由 Player 调用一次,排在 move_and_slide() 之后。 ## 用速度把物体拽向持握点,而不是直接改 global_position —— ## 这样它仍会和场景里其他刚体正常碰撞,不会被硬塞进墙里。 func physics_step() -> void: - if _current == null or _hold_pos == null: + # 用 is_instance_valid 而不是 `== null`:物体可能在拖拽途中被 destroy, + # 那种情况下引用不是 null 而是「已被释放」,直接取 global_position 会报错。 + if not is_instance_valid(_current) or not is_instance_valid(_hold_pos): return var to_hold := _hold_pos.global_position - _current.global_position _current.set_linear_velocity(to_hold * pick_power) -func _pick() -> void: +func _grab() -> void: if _ray == null: return var collider := _ray.get_collider() @@ -45,10 +53,13 @@ func _pick() -> void: _current = collider as RigidBody3D -func _release() -> void: +func _drop() -> void: + # 故意不清零线速度:松手时保留拖动惯性,甩出去的手感更自然。 + # 想要「就地放下、不再滑出去」的话,这里补一句: + # if is_instance_valid(_current): _current.set_linear_velocity(Vector3.ZERO) _current = null ## 当前是否抓着东西。调试用,也方便以后加「搬运时不能开枪」这类规则。 func has_object() -> bool: - return _current != null + return is_instance_valid(_current)