技术拾遗
开发过程中遇到的小问题及解决方法记录
#001 Windows 平台攻击动画卡住问题
日期:2026-01-29
现象
- 在 Windows 平台上,玩家角色攻击时第一段攻击动画会直接卡住
- 过一会儿才恢复,无法完成动作也无法接上预输入的第二段连招
- 同样的代码在 macOS 上运行正常
问题代码
# attack_component.gd
func handle_attack_input(input_direction: Vector2):
if attack_cooldown > 0:
attack_cooldown -= get_process_delta_time() # ❌ 问题在这里
if is_attacking:
attack_timer += get_process_delta_time() # ❌ 问题在这里
# ...
而调用方是在 _physics_process() 中:
# player.gd
func _physics_process(_delta):
attack_component.handle_attack_input(direction)
根因分析
| 函数 | 用途 |
|---|---|
get_process_delta_time() |
返回 _process() 的帧时间(与显示帧率同步,可变) |
get_physics_process_delta_time() |
返回 _physics_process() 的帧时间(固定物理帧率,通常 60fps) |
在 _physics_process() 中调用了依赖 get_process_delta_time() 的函数,导致:
- Windows 上更容易出问题:Windows 对窗口焦点、VSync、帧率调度的处理与 macOS
不同。当窗口失焦或帧率波动时,
get_process_delta_time()可能返回 0 或极小值 - 攻击计时器不增长:
attack_timer += 0导致永远达不到attack_duration,动画就"卡住"了 - 预输入窗口无法触发:计时器不增长意味着永远进不了预输入检测的时间窗口
解决方案
让函数显式接收 delta 参数,而不是依赖全局获取:
# attack_component.gd
func handle_attack_input(input_direction: Vector2, delta: float = -1.0):
# 如果没有传入 delta,使用物理帧时间作为后备
if delta < 0:
delta = get_physics_process_delta_time()
if attack_cooldown > 0:
attack_cooldown -= delta # ✅ 使用传入的 delta
if is_attacking:
attack_timer += delta # ✅ 使用传入的 delta
# ...
# player.gd
func _physics_process(delta):
attack_component.handle_attack_input(direction, delta) # ✅ 传入 delta
经验总结
- 永远不要在
_physics_process()中使用get_process_delta_time(),反之亦然 - 显式传递 delta 参数比依赖全局获取更安全、更可复用
- 跨平台测试很重要,Windows 和 macOS 在帧率调度上存在差异
- 当遇到"某平台正常、另一平台异常"的问题时,优先检查时间相关的代码
涉及文件:
gdt/scripts/player/components/attack_component.gdgdt/scripts/player/player.gd
#002 Y-Sort 容器中的层级控制
日期:2026-01-29
场景
- 需要在角色周围添加气场/光环效果
- 效果应该在角色后面(被角色遮挡),但在地图上面(不被地图遮挡)
失败尝试
| 方案 | 问题 |
|---|---|
气场作为角色子节点,z_index = -1 |
被地图挡住(地图 z_index 可能也是 0 或更高) |
气场作为角色子节点,z_index = 50 |
跑到角色前面了 |
使用 show_behind_parent = true |
在 Y-Sort 容器中表现不符合预期 |
根因分析
在 Y-Sort 容器 (y_sort_enabled = true) 中:
- 只有直接子节点参与排序
- 子节点的子节点(孙节点)不会单独参与排序,它们随父节点一起绘制
- 排序依据是
global_position.y:y 值越小越先绘制(视觉上在"后面")
所以,当气场是 Player 的子节点时,它无法独立参与 Y-Sort 排序。
解决方案
把气场作为 YSort_Container 的直接子节点(与 Player 同级),通过调整 y 坐标让它在 Player "后面"绘制:
# aura_effect.gd
@export var y_offset: float = -1.0 # 负值 = 在 Y-Sort 中先绘制 = 视觉上在后面
func _process(delta):
if follow_target:
# 气场的 y 坐标 = 玩家 y 坐标 + 偏移
global_position = follow_target.global_position + Vector2(0, y_offset)
# player.gd
func _setup_aura_effect():
# 添加到 YSort_Container(Player 的父节点)而不是 Player 本身
var ysort_container = get_parent()
ysort_container.add_child(aura_effect)
aura_effect.set_follow_target(self)
排序结果
Y-Sort 排序 (按 y 坐标):
[0] TileMapLayer: y=0.0 ← 地图,先绘制
[1] AuraEffect: y=599.0 ← 气场,后绘制(在地图上面)
[2] Player: y=600.0 ← 玩家,最后绘制(在气场上面)
经验总结
- Y-Sort 只对直接子节点生效,想让某个效果独立参与排序必须放到同一层级
- 通过微调 y 坐标控制绘制顺序是比 z_index 更可靠的方法(在 Y-Sort 中)
- 调试时输出各节点的
global_position.y可以直观看到排序顺序
涉及文件:
gdt/scripts/player/components/aura_effect.gdgdt/scripts/player/player.gd
持续更新中...