@tool class_name Sfx2D extends AudioStreamPlayer2D @export var loop := false # 0 一个接一个循环; >0 则每 loop_round_time 播放一次 @export var loop_round_time := 0.0 @export var debug_play := false: set(val): debug_play = false if not Engine.is_editor_hint() or not is_node_ready(): return if loop_round_time > 0.0: timer.wait_time = loop_round_time timer.start() else: timer.stop() play() var timer: Timer func _ready() -> void: bus = &"game_sfx" process_mode = Node.PROCESS_MODE_PAUSABLE if Engine.is_editor_hint(): return timer = Timer.new() timer.autostart = autoplay and loop and loop_round_time > 0.0 and not Engine.is_editor_hint() timer.one_shot = false timer.wait_time = max(0.1, loop_round_time) timer.timeout.connect(_on_timer_timeout) add_child(timer) finished.connect(_on_finished) # ground 退出时,process mode 切换为 always,ease out SceneManager.ground_transition_pre_paused.connect(_on_ground_transition_pre_paused) func _on_ground_transition_pre_paused(): if not playing: return print("[GroundTransition] Sfx2D %s ease killing..." % name) easing_kill(1.0) func _on_timer_timeout() -> void: if not loop or loop_round_time <= 0.0: timer.stop() return play() func _on_finished() -> void: if not loop: timer.stop() else: if loop_round_time <= 0: timer.stop() play() else: timer.start(loop_round_time) # queue free 导致 sfx 无法播放,使用全局声源 func global_play() -> void: if stream: AudioManager.play_sfx(stream) # 注意:会导致 volume db 变化 func easing_kill(duration: float = 2.0) -> void: # stop with easing if playing: var tween = create_tween() tween.bind_node(self) tween.tween_property(self, "volume_linear", 0.0, duration) tween.tween_callback(stop)