7.9 KiB
7.9 KiB
2026-10-09
简易准心(Crosshair)
- 新增
ui/crosshair.gd:@tool extends Control,用_draw()画十字(gap/length/thickness/dot 可导出调参),mouse_filter = IGNORE,监听NOTIFICATION_RESIZED重绘。 - 挂载约定:
CanvasLayer > Crosshair(Control),Control 锚点为 Full Rect,中心即屏幕中心;推荐挂在CameraRig之下 —— 因为player.gd _ready()对非 authority 会camera_rig.queue_free(),远端玩家准心随之一并释放,无需额外代码。 - 项目 stretch 模式为
canvas_items+expand,CanvasLayer 会自动跟随缩放。
后续改动:只保留「点」
- 最终形态:准心 = 屏幕中心一个点,无十字、无样式开关(
enum Style已移除)。 - 导出参数:
dot_color/dot_radius(默认 2.5) /outline/outline_color/outline_width。 _draw()先画描边圆再画主体圆,保证浅背景可见。
player.gd 拆解方案(待执行)
- 现状:
character/player/player.gd155 行,混了视角 / 移动跳跃 / 拾取 / 多人权限四类职责。 - 方案:子节点组件化(与既有
PlayerAnimator同模式)。根节点降为协调器(~40 行),新增Look/Motor/Pickup三个 Node 子节点,各自挂player_look.gd/player_motor.gd/player_pickup.gd。 - 三条硬约束:①
move_and_slide()留在根节点,Motor 只写body.velocity;② 物理顺序由根节点显式编排(motor.step → move_and_slide → animator → pickup),不让子节点各写_physics_process;③ 非 authority 时三个组件的输入/物理必须一并关闭。 - 范式定位:组合优于继承 + 组件模式(Component Pattern);协调器部分带 Facade/Mediator 色彩;
setup()注入属手工依赖注入。不是 ECS(Godot 节点是「带行为的对象」,非纯数据组件)。 - 状态:已落地(2026-10-09)。新增
player_look.gd/player_motor.gd/player_pickup.gd(均带class_name:PlayerLook / PlayerMotor / PlayerPickup);player.gd重写为协调器(~65 行);player.tscn追加 Look / Motor / Pickup 三个 Node 子节点(挂 FaceRig 之后,ext_resource id 13_look / 14_motor / 15_pickup,未写 uid 由 Godot 补)。 - 场景格式注意:本项目的 .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)。
按 Q 投掷
player_pickup.gd接入throw_carry(Q 键)→_throw():取-_hold_pos.global_transform.basis.z作投掷方向(持握点挂在摄像机下且仅有平移,其 basis 即视线朝向,故无需再注入摄像机),set_linear_velocity(dir * throw_speed)后置_current = null。- 新增导出参数
throw_speed: float = 12.0。 - 备选手感(注释里留了行):改用
apply_central_impulse则重物飞得慢。
可拖拽时准心变手型
- 新增素材
character/assets/hand_cursor.svg(新建了character/assets/目录):简笔手(4 指 + 手掌 + 拇指的圆角矩形),双层 —— 底层深色#1a1a1astroke-width 4 当描边,上层白色 stroke-width 1.5 覆盖内部接缝。32×32,未写<title>(避免 ThorVG 解析风险)。 ui/crosshair.gd:加class_name Crosshair;新增hand_texture(自动加载上面的 svg) /hand_size(=26)、set_grabbable(bool);_draw()在_grabbable && hand_texture != null时走_draw_hand(),否则走_draw_dot()。player_pickup.gd:新增signal hover_changed(grabbable: bool),physics_step()里先调_update_hover()——用_ray.force_raycast_update()强制刷新(否则结果滞后于本帧 move_and_slide,跑动中瞄到物体慢一帧),只在状态变化时发信号。player.gd:@onready var crosshair: Crosshair = $CameraRig/HUD/Crosshair;_ready()权威分支里pickup.hover_changed.connect(_on_pickup_hover_changed);handler 里既切准心,也Input.set_custom_mouse_cursor()换系统指针(含指尖热点),鼠标未捕获时该行无副作用。- 注意:用户侧已把
AnimationPlayer从Body/AnimationPlayer提到 Player 根节点($AnimationPlayer),并把animator变量改名为animation_player;相机 scale 改为 5。
换用真实光标素材(grab / grab_hover)
- 素材来源:
C:\Users\yvmou\Downloads\cursors\light\(light 主题 = 深色手型,32×32 RGBA)。 - 已复制到
character/assets/grab.png(指着可拖拽物)与character/assets/grab_hover.png(正抓着),Godot 已生成.import。 - 删除了临时的
character/assets/hand_cursor.svg及其.import(已被真实素材取代)。 ui/crosshair.gd改为三态:enum GrabState { NONE, HOVER, HOLDING };导出hover_texture/hold_texture/hand_size(=32,1:1 不缩放) /icon_modulate/sync_os_cursor;公开texture_for_state(state)供系统指针复用素材;_sync_os_cursor()内部完成Input.set_custom_mouse_cursor(Engine.is_editor_hint()下跳过),热点在图标高度 12% 处 —— 系统指针逻辑已从 player.gd 移入准心。player_pickup.gd:信号由hover_changed(bool)改为interact_state_changed(hovering, holding),_update_hover()改名_update_interact_state()并同时跟踪_holding。player.gd:handler 改名_on_pickup_interact_state_changed(false),只负责把两个布尔量收敛成Crosshair.GrabState(优先级 抓着 > 指着 > 没有)。- 待用户确认:像素手在非整数倍分辨率缩放下发糊的话,需把 Crosshair 的
texture_filter设为 Nearest。
拖拽时物体一卡一卡(已修)
- 根因(几何 + 控制律):
physics_step()用v = to_hold * pick_power是一阶跟随,人匀速前进时物体必有稳态滞后,滞后距离 = 移动速度 / pick_power。当时为walk_speed 5 ÷ pick_power 10 = 0.5 m,而持握点离身体仅0.44 m(InteractionPos local z = -0.4439,Player 缩放 0.2 × Camera 缩放 5 = 净 1.0,故世界距离就是 0.444 m)→ 物体退进玩家胶囊体 → 物理推出 / 拖拽拉回互相打架 → 高频抖动。 - 修复 1(前馈):
desired = to_hold * pick_power + _body.velocity—— 匀速行走时滞后归零。这是根治。 - 修复 2(碰撞例外):
_grab()里_current.add_collision_exception_with(_body),_drop()/_throw()经_forget()用remove_collision_exception_with解除。只解除「物体 ↔ 玩家」这一对,物体与场景其他碰撞体照常交互。 - 另加
@export var max_drag_speed: float = 30.0速度上限,物体离得远时不会被一股脑拽飞。 - 接口变更:
PlayerPickup.setup(ray, hold_pos, body)多了第三个参数(玩家本体),player.gd里传self。_grab()加了is_instance_valid(_current)早退,避免重复抓取时漏掉碰撞例外。