简介:在 Unity 游戏开发中,音效是提升游戏体验的重要部分。然而,直接在各个脚本中调用 AudioSource.Play() 会导致管理混乱、代码冗余。本教程将使用 单例模式 + 列表(List)+字典(Dctionary)+事件系统 设计一个 AudioManager(音频管理器),实现背景音乐、音效播放、跨场景管理等功能,适用于大部分游戏项目

一、音频管理的核心问题

音效管理的常见问题:

音频播放混乱 —— 代码到处调用 AudioSource.Play(),不易维护
背景音乐在场景切换时停止 —— 需要让 BGM 跨场景播放
代码复用性低 —— 每个脚本都要维护 AudioSource,难以统一管理
性能问题 —— 频繁创建/销毁 AudioSource,可能导致卡顿

二、音频管理器的设计思路

  • 通过单例模式AudioManager全局唯一
  • 使用字典(Dictionary)存储不同音效,提高查询效率
  • 结合事件系统,方便其他组件触发音效

三、代码实现

1.定义音频类型(AudioType 枚举)

通过枚举(enum)定义不同音效的类型,这样每种音效都有一个唯一标识,便于管理

public enum AudioType
{
    BGM,
    Hit,
    Btn,
    GameOver,
    Collected,
    Trampoline,
    Jump,
    GameWin
}

2.设计音频管理器(AudioManager)

using System;
using System.Collections.Generic;
using UnityEngine;

public enum AudioType
{
    BGM,
    Hit,
    Btn,
    GameOver,
    Collected,
    Trampoline,
    Jump,
    GameWin
}
public class AudioManager : MonoBehaviour
{
    [Header("音频数据")]
    public List<AudioData> audioData;

    private Dictionary<AudioType,AudioData> _audioDataDic;
    
    [Serializable]
    public struct AudioData
    {
        public AudioType type;
        public AudioClip audioClip;
        public AudioSource audioSource;
    }

    //静态实例
    private static AudioManager _instance;
    //公共访问点
    public static AudioManager instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<AudioManager>();
                DontDestroyOnLoad(_instance.gameObject);
            }
            return _instance;
        }
    }

    //确保实例在场景切换时不会销毁
    private void Awake()
    {
        if (_instance == null)
        {
            _instance = this;
            DontDestroyOnLoad(this);//保持跨场景的持久性
        }
        else if (_instance != this)
        {
            Destroy(gameObject);//如果已经有实例存在,销毁多余的实例
        }
        
        //初始化AudioSource
        IniAudioSource();
    }

    private void OnEnable()
    {
        //播放背景音乐
        audioData[0].audioSource.Play();
        EventController.OnPlayAudio += PlayerAudio;
    }

    private void OnDisable()
    {
        EventController.OnPlayAudio -= PlayerAudio;
    }

    private void IniAudioSource()
    {
        _audioDataDic=new Dictionary<AudioType, AudioData>();
        
        foreach (var audio in audioData)
        {
            if (audio.audioSource != null)
            {
                audio.audioSource.clip = audio.audioClip;
            }
            //存入字典
            _audioDataDic[audio.type] = audio;
        }
    }
    /// <summary>
    /// 播放指定类型的音效
    /// </summary>
    /// <param name="audioType">音频类型</param>
    private void PlayerAudio(AudioType audioType)
    {
        if (_audioDataDic.TryGetValue(audioType, out AudioData audioData))
        {
            _audioDataDic[audioType].audioSource.Play();//哈希表查询提升效率
            Debug.Log(audioType);
        }
            
    }
}

四、核心功能

1.单例模式

单例模式确保AudioMnager全局唯一,切换场景不会失效

   //静态实例
    private static AudioManager _instance;
    //公共访问点
    public static AudioManager instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<AudioManager>();
                DontDestroyOnLoad(_instance.gameObject);
            }
            return _instance;
        }
    }

保证全局访问,让任何脚本都可以 AudioManager.instance.PlayAudio(AudioType.Hit)
防止重复创建,如果 AudioManager 已存在,则销毁新创建的实例

2.通过字典(Dictionary)提高查询效率

IniAudioSource() 方法中,我们把音频信息存入 字典(Dictionary)

   private void IniAudioSource()
    {
        _audioDataDic=new Dictionary<AudioType, AudioData>();
        
        foreach (var audio in audioData)
        {
            if (audio.audioSource != null)
            {
                audio.audioSource.clip = audio.audioClip;
            }
            //存入字典
            _audioDataDic[audio.type] = audio;
        }
    }
    /// <summary>
    /// 播放指定类型的音效
    /// </summary>
    /// <param name="audioType">音频类型</param>
    private void PlayerAudio(AudioType audioType)
    {
        if (_audioDataDic.TryGetValue(audioType, out AudioData audioData))
        {
            _audioDataDic[audioType].audioSource.Play();//哈希表查询提升效率
            Debug.Log(audioType);
        }
            
    }
}

相比 List 遍历,字典查询更快(O(1) 时间复杂度)
避免重复加载音效,节省内存

注意:Audio Clip和AudioSource是手动拖入的

3.通过事件系统播放音效

    private void OnEnable()
    {
        //播放背景音乐
        audioData[0].audioSource.Play();
        EventController.OnPlayAudio += PlayerAudio;
    }

    private void OnDisable()
    {
        EventController.OnPlayAudio -= PlayerAudio;
    }

 创建一个 EventController 全局管理音效播放事件

using UnityEngine;
using UnityEngine.Events;
public class EventController : MonoBehaviour
{
    public static UnityAction<AudioType> OnPlayAudio;

    public static void RaiseOnPlayAudio(AudioType type)
    {
        OnPlayAudio?.Invoke(type);
    }
}

然后在需要播放音效的地方启动该事件即可:

EventController.RaiseOnPlayAudio(AudioType.Jump);//传入音效类型

解耦代码,让 AudioManager 不依赖 其他脚本,而是监听 EventController.OnPlayAudio 事件
避免手动查找 AudioSource,提高代码复用性

五、总结

功能实现方式
背景音乐持续播放DontDestroyOnLoad(this)
防止多次创建实例单例模式 AudioManager.instance
音效高效管理Dictionary<AudioType, AudioData>
组件解耦事件系统 EventController.OnPlayAudio
适用场景

按钮点击、跳跃、BGM、胜利音效等

以上为个人理解,仅供参考,如果你觉得这篇文章有帮助,欢迎 点赞 + 收藏 + 关注!🔥🔥🔥

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐