@tool class_name Sfx extends AudioStreamPlayer const META_ORIGINAL_STREAM = &"original_stream" @export_enum("交互与效果音", "BGM", "场景背景音") var mode := "交互与效果音": set(value): _set_up_process_mode_by_mode() mode = value notify_property_list_changed() # 当前播放状态注册;节点销毁前卸载 # TODO BGM 过程抑制场景音效;场景音效随玩家运动呼吸 (结合 Sfx2D) # 感应玩家移动:装饰音 var default_db := 0.0 # 只有 场景背景音 生效 var scene_loop := true var scene_sense_player_mov := false func _ready() -> void: bus = &"game_sfx" default_db = volume_db if Engine.is_editor_hint(): return # 记录原 stream if stream: set_meta(META_ORIGINAL_STREAM, stream) else: set_meta(META_ORIGINAL_STREAM, null) _set_up_process_mode_by_mode() finished.connect(_on_finished) # ground 退出时,process mode 切换为 always,ease out SceneManager.ground_transition_pre_paused.connect(_on_ground_transition_pre_paused) SceneManager.pause_counter_updated.connect(_on_pause_counter_updated) var playing_on_debugging_paused = false func _on_pause_counter_updated() -> void: _set_up_process_mode_by_mode() func _set_up_process_mode_by_mode(): # 如果是 debug panel,则 pause if mode == "场景背景音" and not SceneManager.pause_counter_arr.has("debugging"): process_mode = Node.PROCESS_MODE_ALWAYS else: if SceneManager.pause_counter_arr.has("debugging"): playing_on_debugging_paused = playing process_mode = Node.PROCESS_MODE_PAUSABLE func _on_ground_transition_pre_paused(): if not playing: return print("[GroundTransition] Sfx %s ease killing..." % name) easing_kill(1.0) func _on_finished() -> void: if mode == "场景背景音" and scene_loop: play() func reset_original_stream(ignore_null := true): var original_stream = get_meta(META_ORIGINAL_STREAM) if original_stream != null: replace_stream(original_stream) elif not ignore_null: stream = null func replace_stream(new_stream: AudioStream) -> void: stream = new_stream if (playing_on_debugging_paused or autoplay) and is_node_ready(): play() func stream_was_replaced() -> bool: return get_meta(META_ORIGINAL_STREAM) != stream func resart(ease_duration := 1.0): easing_kill(ease_duration).tween_callback(play) # queue free 导致 sfx 无法播放,使用全局声源 func global_play() -> void: if stream: AudioManager.play_sfx(stream) func easing_kill(duration: float = 2.0) -> Tween: # stop with easing var tween = create_tween() tween.bind_node(self) if playing: tween.tween_property(self, "volume_linear", 0.0, duration) tween.tween_callback(stop) # set volume_db back to default tween.tween_callback(func(): volume_db = default_db) return tween func _get_property_list() -> Array[Dictionary]: if mode != "场景背景音": return [] # # 只有 场景背景音 生效 # var scene_loop := true # var scene_autostart := true # var scene_sense_player_mov := false return [ { "name": "场景背景音配置项", "type": TYPE_NIL, "usage": PROPERTY_USAGE_GROUP, }, {"name": "循环播放", "type": TYPE_BOOL}, {"name": "感应玩家操作", "type": TYPE_BOOL} ] func _property_can_revert(property: StringName) -> bool: return property == "循环播放" or property == "感应玩家操作" func _property_get_revert(property: StringName) -> Variant: if property == "循环播放": return true elif property == "感应玩家操作": return false return null func _set(property: StringName, value: Variant) -> bool: if mode != "场景背景音": return false elif property == "循环播放": scene_loop = value return true elif property == "感应玩家操作": scene_sense_player_mov = value return true return false func _get(property: StringName) -> Variant: if mode != "场景背景音": return null elif property == "循环播放": return scene_loop elif property == "感应玩家操作": return scene_sense_player_mov return null