游戏SoundManager类如何检测SoundEffectInstances播放完成并移除?
检测音效播放完成并移除的实用思路
针对你维护SoundEffectInstances列表的需求,下面是几种直接可行的实现方案:
1. 绑定Completed事件(推荐)
每个SoundEffectInstance在播放完成时会触发Completed事件,直接在事件回调中移除实例是最精准的方式。
修改你的PlaySound方法,添加事件绑定逻辑:
public static void PlaySound(string name) { // 实际项目中应该根据name从资源字典/池里获取对应的SoundEffect var soundInstance = sTestSound.CreateInstance(); sAllSounds.Add(soundInstance); // 绑定播放完成事件 soundInstance.Completed += (sender, _) => { if (sender is SoundEffectInstance instance) { // 线程安全处理:如果音效事件在后台线程触发,需加锁 lock (sAllSounds) { sAllSounds.Remove(instance); instance.Dispose(); // 释放音效资源,避免内存泄漏 } } }; soundInstance.Play(); }
2. 帧循环中检查状态
如果你的游戏有主更新循环(比如Unity的Update、XNA的Update),可以在每一帧遍历列表,移除已停止的实例:
public static void Update() { // 反向遍历,避免移除元素时导致索引错乱 for (int i = sAllSounds.Count - 1; i >= 0; i--) { var instance = sAllSounds[i]; if (instance.State == SoundState.Stopped) { sAllSounds.RemoveAt(i); instance.Dispose(); } } }
这种方式适合不想依赖事件的场景,音效数量不多时性能完全没问题。
3. 协程等待播放完成(Unity专属)
如果是Unity环境,还可以用协程等待音效时长,完成后移除实例:
public static IEnumerator PlaySoundCoroutine(string name) { var sound = sTestSound; // 根据name获取对应SoundEffect var soundInstance = sound.CreateInstance(); sAllSounds.Add(soundInstance); soundInstance.Play(); // 等待音效播放时长 yield return new WaitForSeconds((float)sound.Duration.TotalSeconds); // 二次确认状态,避免中途被手动停止/暂停 if (soundInstance.State == SoundState.Stopped) { sAllSounds.Remove(soundInstance); soundInstance.Dispose(); } }
调用时通过StartCoroutine(PlaySoundCoroutine("音效名称"))启动即可。
额外注意点
- 无论用哪种方式,移除实例时一定要调用
Dispose(),否则会造成资源泄漏; - 如果音效事件在非主线程触发,操作
sAllSounds必须加锁,防止并发访问错误; - 若音效数量极大,可考虑用
ConcurrentBag<T>等线程安全集合替代List<T>。
内容的提问来源于stack exchange,提问作者Ruben
相关产品推荐
相关产品推荐

