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

如何通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 19:33:11