You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Godot对话框音效异常:关卡启动自动播放、关闭后仍持续播放问题

问题根因

问题1(启动自动播放音效)

你在对话框的_ready()生命周期方法中直接调用了load_dialog(),只要对话框节点被加载到场景中,就会自动把test标记设为true,触发后续的音效播放逻辑,不需要任何触发条件。

问题2(快速按回车后音效持续播放)

你把音效播放方法MusicController.dialogsfx.play()放在了_physics_process中,每帧都会执行一次播放请求,一秒内会触发几十次播放。就算你调用了停止方法,后续帧的播放请求还会继续执行,导致音效停不下来;快速按回车触发对话切换时,还会出现上一个对话的播放逻辑没结束、新的对话播放逻辑又启动的叠加问题。


修复方案

1. 移除自动触发的对话加载逻辑

删掉对话框_ready()方法中的load_dialog()调用,改为在满足触发条件(比如NPC交互)时,从外部主动调用load_dialog()启动对话。

2. 重构音效播放逻辑

不要在帧循环中处理音效播放,仅在对话开始时调用一次播放,对话结束/中断时调用一次停止即可。


修改后代码

对话框代码

extends Control

var dialog = [
    'test1',
    'test2',
]
var dialog_index = 0
var finished = false
var test = false

func _ready():
    MusicController.dialogsfx.stop()
    # 移除了自动调用的load_dialog()
    Global.DialogBox = self
    
func _exit_tree():
    # 节点销毁前强制停止音效,避免漏停
    MusicController.dialogsfx.stop()
    Global.DialogBox = null


func _physics_process(delta):
    $"Ind".visible = finished
    if Input.is_action_just_pressed("ui_accept"):
        # 未播放完时按回车直接跳过当前对话
        if not finished:
            $Tween.stop_all()
            $RichTextLabel.percent_visible = 1
            finished = true
            MusicController.dialogsfx.stop()
            return
        load_dialog()
    if Global.player.can_move:
        MusicController.dialogsfx.stop()

func load_dialog():
    # 加载新对话前先清空上一个对话的状态
    $Tween.stop_all()
    MusicController.dialogsfx.stop()
    
    if dialog_index < dialog.size():
        test = true
        finished = false
        $RichTextLabel.bbcode_text = dialog[dialog_index]
        $RichTextLabel.percent_visible = 0
        $Tween.interpolate_property(
            $RichTextLabel, "percent_visible",  0, 1, 1,
            Tween.TRANS_LINEAR, Tween.EASE_IN_OUT
        )
        $Tween.start()
        # 仅调用一次音效播放
        MusicController.dialogsfx.play()
        
    else:
        Global.npc1.dialogsoundstop = true
        MusicController.dialogsfx.stop()
        Global.player.can_move = true
        Global.npc1.done = true
        test = false
        queue_free()
        
    dialog_index += 1
    
func _on_Tween_tween_completed(object, key):
    finished = true
    # 对话播放完成直接停止音效
    MusicController.dialogsfx.stop()

可选优化:逐字播放短音效

如果你的音效是单字短音效,需要每显示一个字播放一次,可替换为如下逐字显示逻辑,替代原来的Tween动画:

# 替换原来的Tween动画逻辑
func load_dialog():
    $Tween.stop_all()
    MusicController.dialogsfx.stop()
    
    if dialog_index < dialog.size():
        test = true
        finished = false
        $RichTextLabel.bbcode_text = ""
        # 启动逐字显示协程
        _show_text_by_char(dialog[dialog_index])
    else:
        Global.npc1.dialogsoundstop = true
        MusicController.dialogsfx.stop()
        Global.player.can_move = true
        Global.npc1.done = true
        test = false
        queue_free()
    dialog_index += 1

# 逐字显示方法
async func _show_text_by_char(text: String, char_interval: float = 0.05):
    for char in text:
        $RichTextLabel.bbcode_text += char
        MusicController.dialogsfx.play()
        await get_tree().create_timer(char_interval).timeout
    finished = true
    MusicController.dialogsfx.stop()

内容的提问来源于stack exchange,提问作者Ajajajaj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 21:45:06