|
11. 声音管理器
Scripts/Framework/Manager/创建SoundManager.cs,给BuildResources/Audio下面添加音频- publicclassSoundManager:MonoBehaviour{AudioSource m_MusicAudio;//背景音乐AudioSource m_SoundAudio;//音效privatefloat SoundVolume
- {get{return PlayerPrefs.GetFloat("SoundVolume",1.0f);}set{
- m_SoundAudio.volume =value;
- PlayerPrefs.SetFloat("SoundVolume",value);}}privatefloat MusicVolume
- {get{return PlayerPrefs.GetFloat("MusicVolume",1.0f);}set{
- m_MusicAudio.volume =value;
- PlayerPrefs.SetFloat("MusicVolume",value);}}privatevoidAwake(){//背景音乐需要循环,并且不能直接播放,因为背景音乐肯定有
- m_MusicAudio =this.gameObject.AddComponent<AudioSource>();
- m_MusicAudio.playOnAwake =false;
- m_MusicAudio.loop =true;//音效不需要循环,音效不一定有,不用设置playOnAwake
- m_SoundAudio =this.gameObject.AddComponent<AudioSource>();
- m_SoundAudio.loop =false;}publicvoidPlayMusic(string name){//音量小于0.1f因为听不见了,同时节省开销,直接不播放了if(this.MusicVolume <0.1f){return;}//如果背景音乐是正在播放的,也跳过string oldName ="";if(m_MusicAudio.clip !=null){
- oldName = m_MusicAudio.clip.name;}if(oldName == name.Substring(0,name.IndexOf("."))){//m_MusicAudio.Play();return;}//否则就去播放背景音乐
- Manager.Resource.LoadMusic(name,(UnityEngine.Object obj)=>{
- m_MusicAudio.clip = obj asAudioClip;
- m_MusicAudio.Play();});}//暂停播放背景音乐publicvoidPauseMusic(){
- m_MusicAudio.Pause();}//继续播放背景音乐publicvoidOnUnPauseMusic(){
- m_MusicAudio.UnPause();}//停止播放背景音乐publicvoidStopMusic(){
- m_MusicAudio.clip =null;
- m_MusicAudio.Stop();}//设置背景音乐音量publicvoidSetMusicVolume(floatvalue){this.MusicVolume =value;}//设置音效音量publicvoidSetSoundVolume(floatvalue){this.SoundVolume =value;}}
复制代码 设计UI
- privatestaticSoundManager _sound;publicstaticSoundManager Sound
- {get{return _sound;}}publicvoidAwake(){
- _resource =this.gameObject.AddComponent<ResourceManager>();
- _lua =this.gameObject.AddComponent<LuaManager>();
- _ui =this.gameObject.AddComponent<UIManager>();
- _entity =this.gameObject.AddComponent<EntityManager>();
- _scene =this.gameObject.AddComponent<MySceneManager>();
- _sound =this.gameObject.AddComponent<SoundManager>();}
复制代码
XLua.CSharpCallLua
如果希望把一个lua函数适配到一个C# delegate(一类是C#侧各种回调:UI事件,delegate参数,比如List:ForEach;另外一类场景是通过LuaTable的Get函数指明一个lua函数绑定到一个delegate)。或者把一个lua table适配到一个C# interface,该delegate或者interface需要加上该配置。
:::info
如果lua需要添加Action,并且带参数,需要添加这个标签
:::- [CSharpCallLua]publicstaticList<Type> mymodule_cs_call_lua_list =newList<Type>(){typeof(UnityEngine.Events.UnityAction<float>),};
复制代码 为什么需要这么作,因为Slider的Action要传float并且,lua中的Action也要传float,无参数的不会报错,有参数需要在静态列表中添加说明。添加完之后,在XLua-GenerateCode一下,就可以用了。
再次构建Bundle会报错,因为这些扩展方法是编辑器下使用的,lua不允许使用,避免编辑器方法生成code代码,需要生成黑名单列表
如果出现不播放声音的问题,是因为delegate之前出现的错误,导致直接把音量0写进playerprefs了,所以一直没有办法播放,音量为0.- //黑名单[BlackList]publicstaticList<List<string>> BlackList =newList<List<string>>(){newList<string>(){"UnityEngine.Light","ShadowRadius"},newList<string>(){"UnityEngine.Light","shadowRadius"},newList<string>(){"UnityEngine.Light","SetLightDirty"},newList<string>(){"UnityEngine.Light","shadowAngle"},};
复制代码 重新构建Bundle就可以工作了。- functionOnInit()print("lua OnInit")
- end
- functionOnOpen()print("lua OnOpen")--Manager.Scene:LoadScene("Test01","scene.Scene01")local btn_play_music = self.transform:Find("Options Panel/ButtonsAndSlider/Play Music/Play Music On Button"):GetComponent("Button");local btn_stop_music = self.transform:Find("Options Panel/ButtonsAndSlider/Play Music/Play Music Off Button"):GetComponent("Button");local btn_pause_music = self.transform:Find("Options Panel/ButtonsAndSlider/Pause Music/Pause Music On Button"):GetComponent("Button");local btn_unpause_music = self.transform:Find("Options Panel/ButtonsAndSlider/Pause Music/Pause Music Off Button"):GetComponent("Button");local btn_play_sound = self.transform:Find("Options Panel/ButtonsAndSlider/Play Sound/Play Sound Button"):GetComponent("Button");local slider_music_volume = self.transform:Find("Options Panel/ButtonsAndSlider/Music Volume/Music Volume Slider"):GetComponent("Slider");local slider_sound_volume = self.transform:Find("Options Panel/ButtonsAndSlider/Sound Volume/Sound Volume Slider"):GetComponent("Slider");
-
- btn_play_music.onClick:AddListener(function()
- Manager.Sound:PlayMusic("main.mp3");
- end
- )
- btn_stop_music.onClick:AddListener(function()
- Manager.Sound:StopMusic();
- end
- )
- btn_pause_music.onClick:AddListener(function()
- Manager.Sound:PauseMusic();
- end
- )
- btn_unpause_music.onClick:AddListener(function()
- Manager.Sound:OnUnPauseMusic();
- end
- )
- btn_play_sound.onClick:AddListener(function()
- Manager.Sound:PlaySound("ui_select.mp3");
- end
- )
-
- slider_music_volume.onValueChanged:AddListener(function(volume)
- Manager.Sound:SetMusicVolume(volume);print(volume);
- end
- )
- slider_sound_volume.onValueChanged:AddListener(function(volume)
- Manager.Sound:SetSoundVolume(volume);print(volume);
- end
- )
-
- slider_music_volume.value=1;
- slider_sound_volume.value=1;
- end
- functionUpdate()--print("lua Update")
- end
- functionOnClose()print("lua OnClose")
- end
复制代码 由于Action没有在退出前清理掉,会报错,虽然不影响,但还是休整一下。- functionOnInit()print("lua OnInit")
- end
- functionOnOpen()print("lua OnOpen")--Manager.Scene:LoadScene("Test01","scene.Scene01")
- btn_play_music = self.transform:Find("Options Panel/ButtonsAndSlider/Play Music/Play Music On Button"):GetComponent("Button");
- btn_stop_music = self.transform:Find("Options Panel/ButtonsAndSlider/Play Music/Play Music Off Button"):GetComponent("Button");
- btn_pause_music = self.transform:Find("Options Panel/ButtonsAndSlider/Pause Music/Pause Music On Button"):GetComponent("Button");
- btn_unpause_music = self.transform:Find("Options Panel/ButtonsAndSlider/Pause Music/Pause Music Off Button"):GetComponent("Button");
- btn_play_sound = self.transform:Find("Options Panel/ButtonsAndSlider/Play Sound/Play Sound Button"):GetComponent("Button");
- slider_music_volume = self.transform:Find("Options Panel/ButtonsAndSlider/Music Volume/Music Volume Slider"):GetComponent("Slider");
- slider_sound_volume = self.transform:Find("Options Panel/ButtonsAndSlider/Sound Volume/Sound Volume Slider"):GetComponent("Slider");
-
- slider_music_volume.value= Manager.Sound.MusicVolume;
- slider_music_volume.value= Manager.Sound.SoundVolume;
-
- btn_play_music.onClick:AddListener(function()
- Manager.Sound:PlayMusic("main.mp3");
- end
- )
- btn_stop_music.onClick:AddListener(function()
- Manager.Sound:StopMusic();
- end
- )
- btn_pause_music.onClick:AddListener(function()
- Manager.Sound:PauseMusic();
- end
- )
- btn_unpause_music.onClick:AddListener(function()
- Manager.Sound:OnUnPauseMusic();
- end
- )
- btn_play_sound.onClick:AddListener(function()
- Manager.Sound:PlaySound("ui_select.mp3");
- end
- )
-
- slider_music_volume.onValueChanged:AddListener(function(volume)
- Manager.Sound:SetMusicVolume(volume);
- end
- )
- slider_sound_volume.onValueChanged:AddListener(function(volume)
- Manager.Sound:SetSoundVolume(volume);
- end
- )
- end
- functionUpdate()--print("lua Update")
- end
- functionOnClose()print("lua OnClose")
- btn_play_music.onClick:RemoveAllListeners();
- btn_stop_music.onClick:RemoveAllListeners();
- btn_pause_music.onClick:RemoveAllListeners();
- btn_unpause_music.onClick:RemoveAllListeners();
- btn_play_sound.onClick:RemoveAllListeners();
- slider_music_volume.onValueChanged:RemoveAllListeners();
- slider_sound_volume.onValueChanged:RemoveAllListeners();
- end
复制代码 由于删除了Listener,在OnClose函数中,但是OnClose函数的调用和UILogic的逻辑相关,因此队UILgoic也进行调整- //继承LuaBehaviourpublicclassUILogic:LuaBehaviour{Action m_LuaOnOpen;Action m_LuaOnClose;publicoverridevoidInit(string luaName){base.Init(luaName);
- m_ScriptEnv.Get("OnOpen",out m_LuaOnOpen);
- m_ScriptEnv.Get("OnClose",out m_LuaOnClose);}publicvoidOnOpen(){
- m_LuaOnOpen?.Invoke();}publicvoidOnClose(){
- m_LuaOnClose?.Invoke();}protectedoverridevoidClear(){OnClose();base.Clear();
- m_LuaOnOpen =null;
- m_LuaOnClose =null;}}
复制代码 对于Package模式下,一个音乐多次播放会加载多次bundle,但是bundle不能多次加载。需要处理。- //存放加载过的BundleprivateDictionary<string, AssetBundle> m_AssetBundles =newDictionary<string, AssetBundle>();AssetBundleGetBundle(string name){AssetBundle bundle =null;if(m_AssetBundles.TryGetValue(name,out bundle)){return bundle;}returnnull;}IEnumeratorLoadBundleAsync(string assetName,Action<UObject> action =null){if(assetName.EndsWith(".unity")){
- action?.Invoke(null);yieldbreak;}string bundleName = m_BundleInfos[assetName].BundleName;//这个是小写的bundle.ab的路径名string bundlePath = Path.Combine(PathUtil.BundleResourcePath, bundleName);List<string> dependences = m_BundleInfos[assetName].Dependeces;//判断是否已经加载过bundle了AssetBundle bundle =GetBundle(bundleName);if(bundle ==null){if(dependences !=null&& dependences.Count >0){//递归加载依赖bundle,因为依赖的资源目录名就是bundle资源名for(int i =0; i < dependences.Count; i++){yieldreturnLoadBundleAsync(dependences[i]);}}//创建异步加载bundle申请AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(bundlePath);yieldreturn request;
- bundle = request.assetBundle;
- m_AssetBundles.Add(bundleName, request.assetBundle);}// if (assetName.EndsWith(".unity"))// {// action?.Invoke(null);// yield break;// }//从bundle申请加载指定路径名的文件,例如prefabAssetBundleRequest bundleRequest = bundle.LoadAssetAsync(assetName);yieldreturn bundleRequest;//如果回调和request都不为空,语法糖
- action?.Invoke(bundleRequest?.asset);}
复制代码 12. 事件管理器
Framework-Manager添加EventManager- usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;/// <summary>/// 逻辑:EventManager的m_Events相当于事件中心,其他脚本的事件都先给定一个id并注册到这个字典中,当需要运行的时候,从事件中心里查找对应id的事件执行/// </summary>publicclassEventManager:MonoBehaviour{//一个参数给lua使用,lua的多个参数可以封装成table传进来publicdelegatevoidEventHandler(object args);privateDictionary<int, EventHandler> m_Events =newDictionary<int, EventHandler>();//订阅事件publicvoidSubscribe(int id,EventHandler e){if(m_Events.ContainsKey(id))//相当于实现多播委托
- m_Events[id]+= e;else
- m_Events.Add(id, e);}//取消订阅事件publicvoidUnSubscribe(int id,EventHandler e){if(m_Events.ContainsKey(id)){if(m_Events[id]!=null)
- m_Events[id]-= e;}else{if(m_Events[id]==null)
- m_Events.Remove(id);}}//执行事件publicvoidFire(int id,object args =null){EventHandler handler;if(m_Events.TryGetValue(id,out handler)){handler(args);}}}
复制代码 Manager添加EventManager- publicstaticEventManager Event
- {get{return _event;}}publicvoidAwake(){
- _resource =this.gameObject.AddComponent<ResourceManager>();
- _lua =this.gameObject.AddComponent<LuaManager>();
- _ui =this.gameObject.AddComponent<UIManager>();
- _entity =this.gameObject.AddComponent<EntityManager>();
- _scene =this.gameObject.AddComponent<MySceneManager>();
- _sound =this.gameObject.AddComponent<SoundManager>();
- _event =this.gameObject.AddComponent<EventManager>();}
复制代码 接下来GameStart等脚本中的匿名委托都可以用EventManager进行处理- publicclassGameStart:MonoBehaviour{publicGameMode GameMode;// Start is called before the first frame updatevoidStart(){//开始时订阅事件
- Manager.Event.Subscribe(10000, OnLuaInit);
-
- AppConst.GameMode =this.GameMode;DontDestroyOnLoad(this);
- Manager.Resource.ParseVersionFile();
- Manager.Lua.Init();}voidOnLuaInit(object args){//初始化完成之后(lua都加载完),在执行回调
- Manager.Lua.StartLua("main");//输入的文件名//输入的是函数名XLua.LuaFunction func = Manager.Lua.LuaEnv.Global.Get<XLua.LuaFunction>("Main");
- func.Call();}publicvoidOnApplicationQuit(){
- Manager.Event.UnSubscribe(10000, OnLuaInit);}}
复制代码 修改LuaManager- publicclassLuaManager:MonoBehaviour{//所有的lua文件名,获取所有lua,然后进行预加载,ResourceManager查找lua文件放进来publicList<string> LuaNames =newList<string>();//缓存lua脚本内容privateDictionary<string,byte[]> m_LuaScripts;//定义一个lua虚拟机,消耗比较大,,全局只需要一个,,需要using XLua;publicLuaEnv LuaEnv;//如果是Editor模式下,直接从luapath就把所有lua读取到字典中了,然后在调用,属于同步加载后同步使用的情况//但是在其他模式下,需要从bundle异步加载lua需要等待,如果等待时start就调用了,属于异步加载同步使用的情况,需要预加载//需要创建一个回调通知publicvoidInit(){//初始化虚拟机
- LuaEnv =newLuaEnv();//外部调用require时,会自动调用loader来获取文件
- LuaEnv.AddLoader(Loader);
- m_LuaScripts =newDictionary<string,byte[]>();#if UNITY_EDITORif(AppConst.GameMode == GameMode.EditorMode)EditorLoadLuaScript();else#endifLoadLuaScript();}voidLoadLuaScript(){foreach(var name in LuaNames){//异步的需要一个回调(=>后面那一坨,当LoadLua执行时完,执行回调并invoke把结果返回),obj就是返回的lua的对象
- Manager.Resource.LoadLua(name,(UnityEngine.Object obj)=>{//LoadLua调用完会把bundle加载好的bundleRequest.asset传进来用obj接受//把这个lua根据名称添加到m_LuaScripts中AddLuaScript(name,(obj asTextAsset).bytes);//在ResourceManager中解析版本文件时加载所有lua文件到LuaNames//如果LuaNames全部都加载到m_LuaScripts集合中,就清空LuaNames,退出循环if(m_LuaScripts.Count >= LuaNames.Count){//所有lua文件加载完成了。就可以执行使用lua函数的方法了
- Manager.Event.Fire(10000);
- LuaNames.Clear();
- LuaNames =null;}});}}#if UNITY_EDITOR//编辑器模式下直接加载lua文件,并把lua名字和内容放到集合内voidEditorLoadLuaScript(){//搜索所有lua文件string[] luaFiles = Directory.GetFiles(PathUtil.LuaPath,"*.bytes", SearchOption.AllDirectories);for(int i =0; i < luaFiles.Length; i++){string fileName = PathUtil.GetStandardPath(luaFiles[i]);//读取lua文件byte[] file = File.ReadAllBytes(fileName);//把读取的lua文件添加进去AddLuaScript(PathUtil.GetUnityPath(fileName), file);}
- Manager.Event.Fire(10000);}#endif}
复制代码 关于声音管理中出现的C# callback报错,是因为C#先释放了LuaEnv,但是Lua中有回调函数还没有取消订阅,在这里需要处理。
编写脚本Framework-Extension-UnityEx- usingUnityEngine.UI;[XLua.LuaCallCSharp]publicstaticclassUnityEx{//对按钮的事件的监听做一个扩展//C#监听的事件变成了C#的匿名委托了,而不是lua的方法,所以lua的方法变成了临时变量publicstaticvoidOnClickSet(thisButton button,object callback){XLua.LuaFunction func = callback asXLua.LuaFunction;//监听事件前,先移除,因为不销毁这个物体,所以如果每次打开ui都监听,会监听无数个事件
- button.onClick.RemoveAllListeners();
- button.onClick.AddListener(()=>{
- func?.Call();});}publicstaticvoidOnValueChangedSet(thisSlider slider,object callback){XLua.LuaFunction func = callback asXLua.LuaFunction;//监听事件前,先移除,因为不销毁这个物体,所以如果每次打开ui都监听,会监听无数个事件
- slider.onValueChanged.RemoveAllListeners();
- slider.onValueChanged.AddListener((floatvalue)=>{
- func?.Call(value);});}}
复制代码P11扩展方法
- functionOnInit()print("lua OnInit")
- end
- functionOnOpen()print("lua OnOpen")--Manager.Scene:LoadScene("Test01","scene.Scene01")
- btn_play_music = self.transform:Find("Options Page/Options Panel/ButtonsAndSlider/Play Music/Play Music On Button"):GetComponent("Button");
- btn_stop_music = self.transform:Find("Options Page/Options Panel/ButtonsAndSlider/Play Music/Play Music Off Button"):GetComponent("Button");
- btn_pause_music = self.transform:Find("Options Page/Options Panel/ButtonsAndSlider/Pause Music/Pause Music On Button"):GetComponent("Button");
- btn_unpause_music = self.transform:Find("Options Page/Options Panel/ButtonsAndSlider/Pause Music/Pause Music Off Button"):GetComponent("Button");
- btn_play_sound = self.transform:Find("Options Page/Options Panel/ButtonsAndSlider/Play Sound/Play Sound Button"):GetComponent("Button");
- slider_music_volume = self.transform:Find("Options Page/Options Panel/ButtonsAndSlider/Music Volume/Music Volume Slider"):GetComponent("Slider");
- slider_sound_volume = self.transform:Find("Options Page/Options Panel/ButtonsAndSlider/Sound Volume/Sound Volume Slider"):GetComponent("Slider");
-
- slider_music_volume.value= Manager.Sound.MusicVolume;
- slider_sound_volume.value= Manager.Sound.SoundVolume;--原版btn_play_music.onClick:AddListener(btn_play_music:OnClickSet(function()
- Manager.Sound:PlayMusic("main.mp3");
- end
- )
- btn_stop_music:OnClickSet(function()
- Manager.Sound:StopMusic();
- end
- )
- btn_pause_music:OnClickSet(function()
- Manager.Sound:PauseMusic();
- end
- )
- btn_unpause_music:OnClickSet(function()
- Manager.Sound:OnUnPauseMusic();
- end
- )
- btn_play_sound:OnClickSet(function()
- Manager.Sound:PlaySound("ui_select.mp3");
- end
- )
-
- slider_music_volume:OnValueChangedSet(function(volume)
- Manager.Sound:SetMusicVolume(volume);
- end
- )
- slider_sound_volume:OnValueChangedSet(function(volume)
- Manager.Sound:SetSoundVolume(volume);
- end
- )
- end
- functionUpdate()--print("lua Update")
- end
- functionOnClose()print("lua OnClose")
- end
复制代码- publicclassUILogic:LuaBehaviour{Action m_LuaOnOpen;Action m_LuaOnClose;publicoverridevoidInit(string luaName){base.Init(luaName);
- m_ScriptEnv.Get("OnOpen",out m_LuaOnOpen);
- m_ScriptEnv.Get("OnClose",out m_LuaOnClose);}publicvoidOnOpen(){
- m_LuaOnOpen?.Invoke();}publicvoidOnClose(){
- m_LuaOnClose?.Invoke();}protectedoverridevoidClear(){//OnClose();base.Clear();
- m_LuaOnOpen =null;
- m_LuaOnClose =null;}}
复制代码 把前面那些OnClose的又改回去了。
XLua.ReflectionUse
一个C#类型类型加了这个配置,xLua会生成link.xml阻止il2cpp的代码剪裁。
对于扩展方法,必须加上LuaCallCSharp或者ReflectionUse才可以被访问到。
建议所有要在Lua访问的类型,要么加LuaCallCSharp,要么加上ReflectionUse,这才能够保证在各平台都能正常运行。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|