基于ET框架的Unity游戏音效管理组件

介绍

本组件是基于强大的ET框架之上的,此处附上et框架链接:https://github.com/egametang/ET

当然如果不使用et的话,也可自行修改为单例模式进行使用,非常简单易用

本音效组件主要分三个文件:

SoundComponent.cs——游戏音效管理组件

SoundData.cs——每个音效单元类

EditorCreateAudio.cs——创建音效预设(Editor类)

特点:支持控制全局音量变化,音乐静音及音效静音

流程:.mp3等资源--> . prefab预制体并附上SoundData单元类--> SoundComponent中通过ab加载. prefab并实例化--> SoundComponent调用SoundData去控制每一个音效

组件部分思路源于海马哥,谢谢!

SoundData(音效资源类)

请看代码,一目了然,简单明了

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

namespace ETModel
{
    [RequireComponent(typeof(AudioSource))]
    public class SoundData : MonoBehaviour
    {
        //音频源控件
        public new AudioSource audio;

        /// <summary>
        /// 是否强制重新播放
        /// </summary>
        //[HideInInspector]
        public bool isForceReplay = false;

        /// <summary>
        /// 是否循环播放
        /// </summary>
        //[HideInInspector]
        public bool isLoop = false;

        /// <summary>
        /// 音量
        /// </summary>
        //[HideInInspector]
        public float volume = 1;

        /// <summary>
        /// 延迟
        /// </summary>
        //[HideInInspector]
        public ulong delay = 0;

        public AudioSource GetAudio()
        {
            return audio;
        }

        public bool IsPlaying
        {
            get
            {
                return audio != null && audio.isPlaying;
            }
        }
        public bool IsPause
        {
            get;
            set;
        }
        public void Dispose()
        {
            Destroy(gameObject);
        }

        /// <summary>
        /// 音效类型
        /// </summary>
        public SoundType Sound { get; set; }

        public bool Mute
        {
            get { return audio.mute; }
            set { audio.mute = value; }
        }

        public float Volume
        {
            get { return audio.volume; }
            set { audio.volume = value; }
        }
    }

    /// <summary>
    /// 音效类型
    /// </summary>
    public enum SoundType
    {
        Music,//长音乐
        Sound,//短音乐
    }
}

EditorCreateAudio(工具类)

自动将目录下的音效资源文件创建成Prefab预制体,方便加载和管理

PS:务必给Prefab加上ab标识,打进需要的ab包,也可以自行在类中拓展标上ab标识的代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;

namespace ETModel
{
    public class EditorCreateAudio : MonoBehaviour
    {
        //音效资源路径
        private static string audiosDir = "assets/ETAB/audios";
        //导出预制体路径
        private static string prefabDir = "assets/ETAB/SoundPrefabs";

        [MenuItem("Tools/创建音效预设", priority = 1004)]
        static void CreateAudioPrefab()
        {
            string[] _patterns = new string[] { "*.mp3","*.wav", "*.ogg" };//识别不同的后缀名
            List<string> _allFilePaths = new List<string>();
            foreach (var item in _patterns)
            {
                string[] _temp = Directory.GetFiles(audiosDir, item, SearchOption.AllDirectories);
                _allFilePaths.AddRange(_temp);
            }
            foreach (var item in _allFilePaths)
            {
                System.IO.FileInfo _fi = new System.IO.FileInfo(item);
                var _tempName = _fi.Name.Replace(_fi.Extension, "").ToLower();
                AudioClip _clip = AssetDatabase.LoadAssetAtPath<AudioClip>(item);
                if (null != _clip)
                {
                    GameObject _go = new GameObject();
                    _go.name = _tempName;
                    AudioSource _as = _go.AddComponent<AudioSource>();
                    _as.playOnAwake = false;
                    SoundData _data = _go.AddComponent<SoundData>();
                    _data.audio = _as;
                    _data.audio.clip = _clip;
                    string path = $"{prefabDir}/{_tempName}.prefab";
                    var temp = PrefabUtility.CreatePrefab(path, _go);

                    //添加ab标记
                    AssetImporter importer = AssetImporter.GetAtPath(path);
                    if (importer == null || temp == null)
                    {
                        Debug.LogError("error: " + path);
                        return;
                    }
                    importer.assetBundleName = "sound.unity3d";

                    GameObject.DestroyImmediate(_go);
                    EditorUtility.SetDirty(temp);
                    Resources.UnloadAsset(_clip);
                }
            }
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }
    }
}

SoundComponent(音效管理组件)

各部分我都有注释,各位可以自行理解,简易上手

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace ETModel
{
    [ObjectSystem]
    public class SoundComponentAwakeSystem : AwakeSystem<SoundComponent>
    {
        public override void Awake(SoundComponent self)
        {
            self.Awake();
        }
    }

    /// <summary>
    /// 游戏音效管理组件
    /// </summary>
    public class SoundComponent : Component
    {
        public static SoundComponent Instance;

        /// <summary>
        /// 控制游戏全局音量
        /// </summary>
        public float SoundVolume
        {
            get
            {
                return _soundVolume;
            }
            set
            {
                _soundVolume = Mathf.Clamp(value, 0, 1);
                foreach (SoundData clip in m_clips.Values)
                {
                    clip.Volume = _soundVolume * clip.volume;
                }
            }
        }
        private float _soundVolume = 0.8f;

        //所有音效
        private Dictionary<string, SoundData> m_clips = new Dictionary<string, SoundData>();

        //根据类型分类所有音效
        private Dictionary<SoundType, Dictionary<string, SoundData>> _allClips = new Dictionary<SoundType, Dictionary<string, SoundData>>()
        { { SoundType.Music, new Dictionary<string, SoundData>() }, { SoundType.Sound, new Dictionary<string, SoundData>() } };

        //catch ab资源
        private static Dictionary<string, SoundData> abSounds = new Dictionary<string, SoundData>();

        //根物体
        private Transform root;

        /// <summary>
        /// 音乐静音
        /// </summary>
        public bool MusicMute
        {
            get { return _musicMute; }
            set
            {
                _musicMute = value;
                foreach (var soundData in _allClips[SoundType.Music].Values)
                {
                    soundData.Mute = _musicMute;
                }
                PlayerPrefs.SetInt("MusicMute", value ? 1 : 0);
            }
        }
        private bool _musicMute = false;

        /// <summary>
        /// 音效静音
        /// </summary>
        public bool SoundMute
        {
            get { return _soundMute; }
            set
            {
                _soundMute = value;
                foreach (var soundData in _allClips[SoundType.Sound].Values)
                {
                    soundData.Mute = _soundMute;
                }
                PlayerPrefs.SetInt("SoundMute", value ? 1 : 0);
            }
        }
        private bool _soundMute = false;

        public void Awake()
        {
            Instance = this;

            _musicMute = PlayerPrefs.GetInt("MusicMute", 0) == 1;
            _soundMute = PlayerPrefs.GetInt("SoundMute", 0) == 1;

            root = new GameObject("SoundDatas").transform;
            GameObject.DontDestroyOnLoad(root.gameObject);
        }

        private bool IsContainClip(string clipName)
        {
            lock (m_clips)
            {
                if (m_clips.ContainsKey(clipName))
                    return true;
                return false;
            }
        }

        private SoundData GetAudioSource(string clipName)
        {
            if (IsContainClip(clipName))
                return m_clips[clipName];
            return null;
        }

        private void AddClip(string clipName, SoundData data, SoundType type)
        {
            lock (m_clips)
            {
                data.IsPause = false;
                data.transform.transform.SetParent(root);
                data.Sound = type;
                if (IsContainClip(clipName))
                {
                    m_clips[clipName] = data;
                    _allClips[type][clipName] = data;
                }
                else
                {
                    m_clips.Add(clipName, data);
                    _allClips[type].Add(clipName, data);
                }
            }
        }

        /// <summary>
        /// 短暂的声音和特效
        /// 无法暂停
        /// 异步加载音效
        /// </summary>
        public async void PlayClip(string clipName, float volume = 1)
        {
            SoundData sd = await LoadSound(clipName);
            if (sd != null)
            {
                sd.volume = Mathf.Clamp(volume, 0, 1);
                sd.Mute = SoundMute;
                if (!IsContainClip(clipName))
                {
                    AddClip(clipName, sd, SoundType.Sound);
                }
                PlayMusic(clipName, sd);
            }
            else
            {
                Log.Error($"没有此音效 ={ clipName}");
            }
        }

        /// <summary>
        /// 播放长音乐 背景音乐等
        /// 可以暂停 继续播放
        /// 异步加载音效
        /// </summary>
        /// <param name="clipName">声音的预设名字(不包括前缀路径名)</param>
        /// <param name="delay">延迟播放 单位秒</param>
        /// <param name="volume">音量</param>
        /// <param name="isloop">是否循环播放</param>
        /// /// <param name="forceReplay">是否强制重头播放</param>
        public async void PlayMusic(string clipName, ulong delay = 0, float volume = 1, bool isloop = false, bool forceReplay = false)
        {
            SoundData sd = await LoadSound(clipName);
            if (sd != null)
            {
                sd.isForceReplay = forceReplay;
                sd.isLoop = isloop;
                sd.delay = delay;
                sd.volume = Mathf.Clamp(volume, 0, 1);
                sd.Mute = MusicMute;
                if (!IsContainClip(clipName))
                {
                    AddClip(clipName, sd, SoundType.Music);
                }
                PlayMusic(clipName, sd);
            }
            else
            {
                Log.Error($"没有此音效 ={ clipName}");
            }
        }

        //加载声音
        private async Task<SoundData> LoadSound(string soundName)
        {
            ResourcesComponent resourcesComponent = Game.Scene.GetComponent<ResourcesComponent>();
            if (!abSounds.ContainsKey(soundName) || abSounds[soundName] == null)
            {
                await resourcesComponent.LoadBundleAsync("sound.unity3d");
                abSounds.Add(soundName, GameObject.Instantiate((GameObject)resourcesComponent.GetAsset("sound.unity3d", soundName)).GetComponent<SoundData>());
                resourcesComponent.UnloadBundle("sound.unity3d");
            }
            return abSounds[soundName];
        }

        //播放SoundData
        private void PlayMusic(string clipName, SoundData asource)
        {
            if (null == asource)
                return;
            bool forceReplay = asource.isForceReplay;
            asource.audio.volume = asource.volume * SoundVolume;
            asource.audio.loop = asource.isLoop;
            if (!forceReplay)
            {
                if (!asource.IsPlaying)
                {
                    if (!asource.IsPause)
                        asource.audio.Play(asource.delay);
                    else
                        Resume(clipName);
                }
            }
            else
            {
                asource.audio.PlayDelayed(asource.delay);
                asource.audio.PlayScheduled(0);
            }
        }

        /// <summary>
        /// 停止并销毁声音
        /// </summary>
        /// <param name="clipName"></param>
        public void Stop(string clipName)
        {
            SoundData data = GetAudioSource(clipName);
            if (null != data)
            {
                if (_allClips[data.Sound].ContainsKey(clipName))
                {
                    _allClips[data.Sound].Remove(clipName);
                }
                m_clips.Remove(clipName);
                abSounds.Remove(clipName);
                data.Dispose();
            }
        }

        /// <summary>
        /// 暂停声音
        /// </summary>
        /// <param name="clipName"></param>
        public void Pause(string clipName)
        {
            SoundData data = GetAudioSource(clipName);
            if (null != data)
            {
                data.IsPause = true;
                data.audio.Pause();
            }
        }

        /// <summary>
        /// 继续播放
        /// </summary>
        /// <param name="clipName"></param>
        public void Resume(string clipName)
        {
            SoundData data = GetAudioSource(clipName);
            if (null != data)
            {
                data.IsPause = false;
                data.audio.UnPause();
            }
        }

        /// <summary>
        /// 销毁所有声音
        /// </summary>
        public void DisposeAll()
        {
            foreach (var allClip in _allClips.Values)
            {
                allClip.Clear();
            }
            foreach (var item in m_clips)
            {
                item.Value.Dispose();
            }
            m_clips.Clear();
        }
    }
}

下载:

GitHub(国外):

ET模组仓库: https://github.com/egametang/ET-Modules.git

本人工具仓库:https://github.com/HealthyChina/HealthyResource.git

Gitee(国内):

本人工具仓库:https://gitee.com/healthyZ/HealthyResource.git

转载请声明出处和作者(渐渐),你的点赞,分享,转发将是对我最大的鼓励!

码字不易,感谢阅读!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容