'''音效播放管理器 可以通過預設的音效表和對應Node的Signal播放對應的音效 ''' class_name ReedSFXManager extends Node @export var bindings: Array[SFXSignalBinding] = [] @export var enable_debug: bool = true func _ready() -> void: _bind_all() func _bind_all() -> void: _dbg_fmt("binding count=%d manager=%s", [bindings.size(), get_path()]) for binding in bindings: _dbg_fmt( "try bind: target_node=%s signal=%s", [binding.target_node, binding.signal_name] ) if not has_node(binding.target_node): push_warning( "[SFX] target node not found: %s (manager=%s)" % [binding.target_node, get_path()] ) continue var target := get_node(binding.target_node) _dbg_fmt("target resolved: %s path=%s", [target, target.get_path()]) if not target.has_signal(binding.signal_name): push_warning( "[SFX] signal not found: %s on %s" % [binding.signal_name, target.get_path()] ) continue var cb := func(ctx = null): _dbg_fmt( "callback fired: binding=%s ctx=%s", [binding, ctx] ) _on_sfx_signal(binding, ctx) if target.is_connected(binding.signal_name, cb): _dbg_fmt("already connected: %s", [binding.signal_name]) else: var err := target.connect(binding.signal_name, cb) _dbg_fmt("connect result=%d (OK=0)", [err]) func _on_sfx_signal(binding: SFXSignalBinding, args: Variant) -> void: if enable_debug and binding.debug_print: print_debug(binding.get_debug_message()) match binding.spawn_space: SFXSignalBinding.SpawnSpace.GLOBAL: _play_global(binding) SFXSignalBinding.SpawnSpace.WORLD_POSITION: if args is Dictionary and args.has("world_pos"): _play_at_position(binding, args["world_pos"]) func _dbg(msg: String) -> void: if not enable_debug: return print("[SFX]", msg) func _dbg_fmt(fmt: String, args: Array = []) -> void: if not enable_debug: return print("[SFX]", fmt % args) # ----------------------------- # 播放逻辑 # ----------------------------- func _play_global(binding: SFXSignalBinding) -> void: var target := get_node(binding.target_node) if not binding.audio_stream: return var player := _create_player(binding) add_child(player) player.global_position = target.global_position + binding.offset if binding.autoplay: player.play() func _play_at_position(binding: SFXSignalBinding, pos: Vector2) -> void: if not binding.audio_stream: return var player := _create_player(binding) add_child(player) player.global_position = pos + binding.offset if binding.autoplay: player.play() func _create_player(binding: SFXSignalBinding) -> AudioStreamPlayer2D: var player := AudioStreamPlayer2D.new() player.stream = binding.audio_stream player.volume_db = binding.volume_db player.pitch_scale = binding.pitch_scale player.finished.connect(player.queue_free) return player