如何通过Audio Player实现多音效同时播放?
修复SoundManager以支持多音效同时播放
你的现有代码存在几个核心问题:仅靠两个固定的AVAudioPlayer实例无法支撑多音效同时播放,刚创建播放器就判断isPlaying的逻辑完全无效(此时播放还未启动),也没有监听播放结束的机制来清理资源。下面是重构后的完整实现:
改进后的SoundManager代码
import AVFoundation class SoundManager: NSObject, AVAudioPlayerDelegate { // 单例实例 static let shared = SoundManager() // 管理所有正在播放的音频播放器 private var activePlayers: [AVAudioPlayer] = [] private override init() { super.init() configureAudioSession() } func playSoundEffect(sound: String, model: Model) { guard !model.soundMuted else { return } switch sound { case "swoosh": playSound(named: "Swoosh", type: "mp3") case "key tap": playSound(named: "Key Tap", type: "mp3") default: break } } private func playSound(named soundName: String, type soundType: String) { guard let path = Bundle.main.path(forResource: soundName, ofType: soundType), let url = URL(string: path) ?? URL(fileURLWithPath: path) else { print("ERROR: 找不到音效文件 \(soundName).\(soundType)") return } do { let player = try AVAudioPlayer(contentsOf: url) player.delegate = self player.prepareToPlay() // 预加载音频,提升播放响应速度 player.play() // 将播放器加入活跃数组 activePlayers.append(player) } catch { print("ERROR: 播放音效失败 - \(error.localizedDescription)") } } // MARK: - AVAudioPlayerDelegate func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { // 播放结束后从活跃数组中移除该播放器,释放资源 if let index = activePlayers.firstIndex(of: player) { activePlayers.remove(at: index) } } private func configureAudioSession() { do { try AVAudioSession.sharedInstance().setCategory(.playback, options: [.mixWithOthers]) try AVAudioSession.sharedInstance().setActive(true) } catch { print("设置音频会话失败: \(error.localizedDescription)") } } // 可选:检查某个音效是否正在播放 func isSoundPlaying(named soundName: String) -> Bool { return activePlayers.contains { player in guard let url = player.url else { return false } let fileName = url.deletingPathExtension().lastPathComponent return fileName == soundName && player.isPlaying } } }
关键改进点说明
- 用数组管理活跃播放器:
activePlayers数组会记录所有正在播放的音频实例,支持任意数量的音效同时播放,不会出现实例被覆盖的问题。 - 监听播放结束事件:通过
AVAudioPlayerDelegate的audioPlayerDidFinishPlaying方法,在音效播放完成后自动从数组中移除对应的播放器,避免内存泄漏。 - 优化播放逻辑:去掉了无效的
isPlaying判断,每个音效创建独立的播放器实例,直接调用play()即可实现同时播放。 - 预加载音频:调用
prepareToPlay()提前加载音频数据,减少播放时的延迟,提升游戏体验。
使用方式
和之前的调用逻辑完全一致:
SoundManager.shared.playSoundEffect(sound: "swoosh", model: model)
如果需要检测某个音效是否正在播放,可以调用:
let isSwooshPlaying = SoundManager.shared.isSoundPlaying(named: "Swoosh")
内容的提问来源于stack exchange,提问作者Jason W
相关产品推荐
相关产品推荐

