聲音的要素
1: 音頻文件AudioClip
2: 音源AudioSource;
3: 耳朵AudioListener;//全局只能有一個
4: 2D/3D音頻;//2D只是簡單地播放聲音,3D可以根據距離衰減音量
怎樣聽到聲音:
創建一個節點,掛載AudioSource組件,AudioSource組件關聯AudioClip屬性,設置聲音是否一開始就加載播放play on awake,是否循環播放,2D還是3D
場景中有一個節點有AudioListener組件,掛載AudioListener組件的節點在掛載AudioSource組件節點的聲音范圍內。運行,可以聽到聲音。
?
?
音樂與音效管理
1: 游戲里面一般分為音樂與音效;
2: 音樂指的是游戲的背景音樂;
3: 音效是游戲中的較短的音樂,來配合游戲的動作等;
4: Unity游戲沒有音樂和音效之分,都是AudioSource + AudioClip;
5: 2D音效沒有衰減,所以與位置沒有關系;
6: 3D音效需要有聲音所在節點的位置;
7: 游戲需求需要音樂和音效分開設置,開啟和關閉;
?
?
sound_manager腳本
首先,聲音包括音樂和音效,我們分兩大塊來管理,音樂為一塊,音效為一塊,這個塊實際上Unity內部是不分的,但是我們來分,
用字典數據類型保存URL和音源AudioSource之間的對應關系,兩個表,一個是音樂的,一個是音效的。
有了這兩個表,這樣如果我們要禁止背景音樂就去遍歷音樂表,如果我們要禁止音效就去遍歷音效表。
播放一個音效,需要new一個物體,在這個物體上加音源AudioSource組件,關聯好音頻文件AudioClip,這樣就會播放出一個音效,
播放出來的音效會有分2D和3D,2D隨便放在哪里,3D需要管理,有一個位置,所以加一個播放3D音效的接口。
1: 全局唯一的sound_manager;
2: 在場景里面創建一個物體(做為聲音的根節點),而且設置這個物體場景切換也不會刪除;
3: 編寫接口播放背景音樂play_music;
4: 編寫接口播放背景音效play_effect; // 2D聲音
5: 音樂和音效內部實現都是一樣的, 只不過要把url分組管理, 音效為一組,音樂為一組;
6: 提供開關音效接口set_mute(),并將值寫入本地,下一次打開游戲的時候會讀取這個值,維持上一次的設置。
7: 提供開關背景音樂接口switch_music(),并將值寫入本地,下一次打開游戲的時候會讀取這個值,維持上一次的設置。
8: 添加一個play_effect接口在指定的坐標出訪一個聲音, 可以用于3D音效;
9: 添加一個接口停止掉背景音樂stop_music(url);
10: 添加一個接口刪除掉播放的背景音樂clear_music(url), clear_effect(url);
11: 聲音文件來自Resouce還是assetBundle,可以通過資源管理來封裝,目前從Resource里面加載;
12: 加一個腳本,每隔0.5秒掃描一次已經播放完的聲音組件,將它disable;
?
?
聲音管理實例
1.創建Unity項目工程和文件目錄,保存場景
2.在Resources文件夾下創建一個sounds文件夾,把背景音樂Login.mp3和音效Close.mp3(第70)放進去
3.為了統一管理游戲中的聲音,寫一個腳本sound_manager來管理
打開sound_manager.cs,初始化sound_manager
using System.Collections; using System.Collections.Generic; using UnityEngine;public class sound_manager {// (1) 聲音根節點的物體;// (2) 保證這個節點在場景切換的時候不會刪除,這樣就不用再初始化一次;// (3) 所有播放聲音的生源節點,都是在這個節點下static GameObject sound_play_object;//這個就是根節點// url --> AudioSource 映射, 區分音樂,音效static Dictionary<string, AudioSource> musics=null;//音樂表static Dictionary<string, AudioSource> effects = null;//音效表public static void init(){sound_play_object = new GameObject("sound_play_object");//初始化根節點GameObject.DontDestroyOnLoad(sound_play_object);//場景切換的時候不會刪除根節點//初始化音樂表和音效表musics = new Dictionary<string, AudioSource>();effects = new Dictionary<string, AudioSource>();} }
?
4.創建一個空節點,下面掛載一個腳本game_scene來測試音樂和音效
打開game_scene.cs,寫一個語句測試初始化
using System.Collections; using System.Collections.Generic; using UnityEngine;public class game_scene : MonoBehaviour {// Use this for initializationvoid Start () {sound_manager.init();//初始化音樂音效管理 }// Update is called once per framevoid Update () {} }
?
5.運行,初始化,節點如下
?
6.接下來開始寫背景音樂和音效的接口
打開sound_manager.cs
using System.Collections; using System.Collections.Generic; using UnityEngine;public class sound_manager {// (1) 聲音根節點的物體;// (2) 保證這個節點在場景切換的時候不會刪除,這樣就不用再初始化一次;// (3) 所有播放聲音的生源節點,都是在這個節點下static GameObject sound_play_object;//這個就是根節點static bool is_music_mute = false;//存放當前全局背景音樂是否靜音的變量static bool is_effect_mute = false;//存放當前音效是否靜音的變量// url --> AudioSource 映射, 區分音樂,音效static Dictionary<string, AudioSource> musics=null;//音樂表static Dictionary<string, AudioSource> effects = null;//音效表//初始化public static void init(){sound_play_object = new GameObject("sound_play_object");//初始化根節點sound_play_object.AddComponent<sound_scan>();//把聲音檢測組件掛載到根節點下GameObject.DontDestroyOnLoad(sound_play_object);//場景切換的時候不會刪除根節點//初始化音樂表和音效表musics = new Dictionary<string, AudioSource>();effects = new Dictionary<string, AudioSource>();// 從本地來加載這個開關if (PlayerPrefs.HasKey("music_mute"))//判斷is_music_mute有沒有保存在本地 {int value = PlayerPrefs.GetInt("music_mute");is_music_mute = (value == 1);//int轉換bool,如果value==1,返回true,否則就是false }// 從本地來加載這個開關if (PlayerPrefs.HasKey("effect_mute"))//判斷is_effect_mute有沒有保存在本地 {int value = PlayerPrefs.GetInt("effect_mute");is_effect_mute = (value == 1);//int轉換bool,如果value==1,返回true,否則就是false }}//播放指定背景音樂的接口public static void play_music(string url, bool is_loop = true){AudioSource audio_source = null;if (musics.ContainsKey(url))//判斷是否已經在背景音樂表里面了 {audio_source = musics[url];//是就直接賦值過去 }else//不是就新建一個空節點,節點下再新建一個AudioSource組件 {GameObject s = new GameObject(url);//創建一個空節點s.transform.parent = sound_play_object.transform;//加入節點到場景中 audio_source = s.AddComponent<AudioSource>();//空節點添加組件AudioSourceAudioClip clip = Resources.Load<AudioClip>(url);//代碼加載一個AudioClip資源文件audio_source.clip = clip;//設置組件的clip屬性為clipaudio_source.loop = is_loop;//設置組件循環播放audio_source.playOnAwake = true;//再次喚醒時播放聲音audio_source.spatialBlend = 0.0f;//設置為2D聲音 musics.Add(url, audio_source);//加入到背景音樂字典中,下次就可以直接賦值了 }audio_source.mute = is_music_mute;audio_source.enabled = true;audio_source.Play();//開始播放 }//停止播放指定背景音樂的接口public static void stop_music(string url){AudioSource audio_source = null;if (!musics.ContainsKey(url))//判斷是否已經在背景音樂表里面了 {return;//沒有這個背景音樂就直接返回 }audio_source = musics[url];//有就把audio_source直接賦值過去audio_source.Stop();//停止播放 }//停止播放所有背景音樂的接口public static void stop_all_music(){foreach (AudioSource s in musics.Values){s.Stop();}}//刪除指定背景音樂和它的節點public static void clear_music(string url){AudioSource audio_source = null;if (!musics.ContainsKey(url))//判斷是否已經在背景音樂表里面了 {return;//沒有這個背景音樂就直接返回 }audio_source = musics[url];//有就把audio_source直接賦值過去musics[url] = null;//指定audio_source組件清空GameObject.Destroy(audio_source.gameObject);//刪除掉掛載指定audio_source組件的節點 }//切換背景音樂靜音開關public static void switch_music(){// 切換靜音和有聲音的狀態is_music_mute = !is_music_mute;//把當前是否靜音寫入本地int value = (is_music_mute) ? 1 : 0;//bool轉換intPlayerPrefs.SetInt("music_mute", value);// 遍歷所有背景音樂的AudioSource元素foreach (AudioSource s in musics.Values){s.mute = is_music_mute;//設置為當前的狀態 }}//當我的界面的靜音按鈕要顯示的時候,到底是顯示關閉,還是開始狀態;public static bool music_is_off(){return is_music_mute;}//接下來開始是音效的接口//播放指定音效的接口public static void play_effect(string url, bool is_loop = false){AudioSource audio_source = null;if (effects.ContainsKey(url))//判斷是否已經在音效表里面了 {audio_source = effects[url];//是就直接賦值過去 }else//不是就新建一個空節點,節點下再新建一個AudioSource組件 {GameObject s = new GameObject(url);//創建一個空節點s.transform.parent = sound_play_object.transform;//加入節點到場景中 audio_source = s.AddComponent<AudioSource>();//空節點添加組件AudioSourceAudioClip clip = Resources.Load<AudioClip>(url);//代碼加載一個AudioClip資源文件audio_source.clip = clip;//設置組件的clip屬性為clipaudio_source.loop = is_loop;//設置組件循環播放audio_source.playOnAwake = true;//再次喚醒時播放聲音audio_source.spatialBlend = 0.0f;//設置為2D聲音 effects.Add(url, audio_source);//加入到音效字典中,下次就可以直接賦值了 }audio_source.mute = is_effect_mute;audio_source.enabled = true;audio_source.Play();//開始播放 }//停止播放指定音效的接口public static void stop_effect(string url){AudioSource audio_source = null;if (!effects.ContainsKey(url))//判斷是否已經在音效表里面了 {return;//沒有這個背景音樂就直接返回 }audio_source = effects[url];//有就把audio_source直接賦值過去audio_source.Stop();//停止播放 }//停止播放所有音效的接口public static void stop_all_effect(){foreach (AudioSource s in effects.Values){s.Stop();}}//刪除指定音效和它的節點public static void clear_effect(string url){AudioSource audio_source = null;if (!effects.ContainsKey(url))//判斷是否已經在音效表里面了 {return;//沒有這個音效就直接返回 }audio_source = effects[url];//有就把audio_source直接賦值過去effects[url] = null;//指定audio_source組件清空GameObject.Destroy(audio_source.gameObject);//刪除掉掛載指定audio_source組件的節點 }//切換音效靜音開關public static void switch_effect(){// 切換靜音和有聲音的狀態is_effect_mute = !is_effect_mute;//把當前是否靜音寫入本地int value = (is_effect_mute) ? 1 : 0;//bool轉換intPlayerPrefs.SetInt("effect_mute", value);// 遍歷所有音效的AudioSource元素foreach (AudioSource s in effects.Values){s.mute = is_effect_mute;//設置為當前的狀態 }}//當我的界面的靜音按鈕要顯示的時候,到底是顯示關閉,還是開始狀態;public static bool effect_is_off(){return is_effect_mute;}//播放3D的音效public static void play_effect3D(string url, Vector3 pos, bool is_loop = false){AudioSource audio_source = null;if (effects.ContainsKey(url)){audio_source = effects[url];}else{GameObject s = new GameObject(url);s.transform.parent = sound_play_object.transform;s.transform.position = pos;//3D音效的位置 audio_source = s.AddComponent<AudioSource>();AudioClip clip = Resources.Load<AudioClip>(url);audio_source.clip = clip;audio_source.loop = is_loop;audio_source.playOnAwake = true;audio_source.spatialBlend = 1.0f; // 3D音效 effects.Add(url, audio_source);}audio_source.mute = is_effect_mute;audio_source.enabled = true;audio_source.Play();}//優化策略接口public static void disable_over_audio(){//遍歷背景音樂表foreach(AudioSource s in musics.Values){if (!s.isPlaying)//判斷是否在播放 {s.enabled = false;//不在播放就直接隱藏 }}//遍歷音效表foreach (AudioSource s in effects.Values){if (!s.isPlaying)//判斷是否在播放 {s.enabled = false;//不在播放就直接隱藏 }}}}
?
7.在game_scene腳本里面寫測試語句,測試接口是否可以使用
打開game_scene.cs
using System.Collections; using System.Collections.Generic; using UnityEngine;public class game_scene : MonoBehaviour {// Use this for initializationvoid Start () {sound_manager.init();//初始化音樂音效管理//sound_manager.play_music("sounds/Login");//播放背景音樂//this.InvokeRepeating("test_music_mute", 1, 3); sound_manager.play_effect("sounds/Close");//播放音效if (sound_manager.effect_is_off())//如果當前是靜音,就切換成有聲音的狀態 {sound_manager.switch_effect();}this.InvokeRepeating("again", 3, 3);//每隔3秒調用一次//this.InvokeRepeating("test_effect_mute", 1, 3); }//背景音樂靜音切換測試函數void test_music_mute(){Debug.Log("test_music_mute");sound_manager.switch_music();}//音效靜音切換測試函數void test_effect_mute(){Debug.Log("test_effect_mute");sound_manager.switch_effect();}//隱藏AudioSource組件優化測試函數void again(){sound_manager.play_effect("sounds/Close");}// Update is called once per framevoid Update () {} }
?
8.還有一個問題就是,在某個聲音播放完成的時候,可以隱藏AudioSource組件,這樣就不會去調用組件的相關接口,游戲性能就能有所提升,這個接口在第6步已經寫好了,測試語句也在第7步顯現出來了,現在就寫一個掃描的腳本
可以創建一個腳本sound_scan來檢測哪些聲音已經播放完成,播完的節點就把它的AudioSource組件隱藏,這個腳本0.5s啟動一次去檢測聲音節點。
打開sound_scan.cs
using System.Collections; using System.Collections.Generic; using UnityEngine;public class sound_scan : MonoBehaviour {// Use this for initializationvoid Start () {//固定一個節奏去掃描,每隔0.5s掃描一次this.InvokeRepeating("scan",0, 0.5f);}// Update is called once per framevoid Update () {}//定時器函數void scan(){sound_manager.disable_over_audio();//調用隱藏AudioSource組件接口 } }
9.運行時的節點
播放背景音樂時
?
播放音效時
?
?
?
注意:
Unity代碼加載資源文件的時候是沒有文件后綴名的,看到什么名字就是什么名字