• Unity Xlua热更新框架(七):声音与事件管理


    11. 声音管理器

    • 背景音乐
      • 播放、暂停、恢复、停止、音量
    • 音效
      • 播放、音量

    Scripts/Framework/Manager/创建SoundManager.cs,给BuildResources/Audio下面添加音频

    public class SoundManager : MonoBehaviour
    {
        AudioSource m_MusicAudio;//背景音乐
        AudioSource m_SoundAudio;//音效
    
        private float SoundVolume
        {
            get { return PlayerPrefs.GetFloat("SoundVolume", 1.0f); }
            set
            {
                m_SoundAudio.volume = value;
                PlayerPrefs.SetFloat("SoundVolume", value);
            }
        }
        private float MusicVolume
        {
            get { return PlayerPrefs.GetFloat("MusicVolume", 1.0f); }
            set
            {
                m_MusicAudio.volume = value;
                PlayerPrefs.SetFloat("MusicVolume", value);
            }
        }
    
        private void Awake()
        {
            //背景音乐需要循环,并且不能直接播放,因为背景音乐肯定有
            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;
        }
    
        public void PlayMusic(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 as AudioClip;
                m_MusicAudio.Play();
            });
        }
        //暂停播放背景音乐
        public void PauseMusic()
        {
            m_MusicAudio.Pause();
        }
        //继续播放背景音乐
        public void OnUnPauseMusic()
        {
            m_MusicAudio.UnPause();
        }
        //停止播放背景音乐
        public void StopMusic()
        {
            m_MusicAudio.clip = null;
            m_MusicAudio.Stop();
        }
    
        //设置背景音乐音量
        public void SetMusicVolume(float value)
        {
            this.MusicVolume = value;
        }
        //设置音效音量
        public void SetSoundVolume(float value)
        {
            this.SoundVolume = value;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    设计UI
    image.png

    private static SoundManager _sound;
    public static SoundManager Sound
    {
        get { return _sound; }
    }
    public void Awake()
    {
        _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>();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    image.png

    XLua.CSharpCallLua

    如果希望把一个lua函数适配到一个C# delegate(一类是C#侧各种回调:UI事件,delegate参数,比如List:ForEach;另外一类场景是通过LuaTable的Get函数指明一个lua函数绑定到一个delegate)。或者把一个lua table适配到一个C# interface,该delegate或者interface需要加上该配置。
    :::info
    如果lua需要添加Action,并且带参数,需要添加这个标签
    :::

    [CSharpCallLua]
    public static List<Type> mymodule_cs_call_lua_list = new List<Type>()
    {
        typeof(UnityEngine.Events.UnityAction<float>),
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    为什么需要这么作,因为Slider的Action要传float并且,lua中的Action也要传float,无参数的不会报错,有参数需要在静态列表中添加说明。添加完之后,在XLua-GenerateCode一下,就可以用了。
    image.png
    再次构建Bundle会报错,因为这些扩展方法是编辑器下使用的,lua不允许使用,避免编辑器方法生成code代码,需要生成黑名单列表
    如果出现不播放声音的问题,是因为delegate之前出现的错误,导致直接把音量0写进playerprefs了,所以一直没有办法播放,音量为0.

    //黑名单
    [BlackList]
    public static List<List<string>> BlackList = new List<List<string>>()  {
        new List<string>(){"UnityEngine.Light", "ShadowRadius"},
    	new List<string>(){"UnityEngine.Light", "shadowRadius"},
        new List<string>(){"UnityEngine.Light", "SetLightDirty"},
        new List<string>(){"UnityEngine.Light", "shadowAngle"},
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    重新构建Bundle就可以工作了。

    function OnInit()
        print("lua OnInit")
    end
    
    function OnOpen()
        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
    
    function Update()
        --print("lua Update")
    end
    
    function OnClose()
        print("lua OnClose")
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    由于Action没有在退出前清理掉,会报错,虽然不影响,但还是休整一下。

    function OnInit()
        print("lua OnInit")
    end
    
    function OnOpen()
        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
    
    function Update()
        --print("lua Update")
    end
    
    function OnClose()
        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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    由于删除了Listener,在OnClose函数中,但是OnClose函数的调用和UILogic的逻辑相关,因此队UILgoic也进行调整

    //继承LuaBehaviour
    public class UILogic : LuaBehaviour
    {
        Action m_LuaOnOpen;
        Action m_LuaOnClose;
        public override void Init(string luaName)
        {
            base.Init(luaName);
            m_ScriptEnv.Get("OnOpen", out m_LuaOnOpen);
            m_ScriptEnv.Get("OnClose", out m_LuaOnClose);
        }
    
        public void OnOpen()
        {
            m_LuaOnOpen?.Invoke();
        }
        public void OnClose()
        {
            m_LuaOnClose?.Invoke();
        }
        protected override void Clear()
        {
            OnClose();
            base.Clear();
            m_LuaOnOpen = null;
            m_LuaOnClose = null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    对于Package模式下,一个音乐多次播放会加载多次bundle,但是bundle不能多次加载。需要处理。

    //存放加载过的Bundle
    private Dictionary<string, AssetBundle> m_AssetBundles = new Dictionary<string, AssetBundle>();
    
    AssetBundle GetBundle(string name)
    {
        AssetBundle bundle = null;
        if (m_AssetBundles.TryGetValue(name, out bundle))
        {
            return bundle;
        }
        return null;
    }
    
    IEnumerator LoadBundleAsync(string assetName,Action<UObject> action = null)
    {
        if (assetName.EndsWith(".unity"))
        {
            action?.Invoke(null);
            yield break;
        }
        
        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++)
                {
                    yield return LoadBundleAsync(dependences[i]);
                }
            }
    
            //创建异步加载bundle申请
            AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(bundlePath);
            yield return request;
    		bundle = request.assetBundle;
            m_AssetBundles.Add(bundleName, request.assetBundle);
        }
    
        // if (assetName.EndsWith(".unity"))
        // {
        //     action?.Invoke(null);
        //     yield break;
        // }
        //从bundle申请加载指定路径名的文件,例如prefab
        AssetBundleRequest bundleRequest = bundle.LoadAssetAsync(assetName);
        yield return bundleRequest;
    
        //如果回调和request都不为空,语法糖
        action?.Invoke(bundleRequest?.asset);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    12. 事件管理器

    Framework-Manager添加EventManager

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    /// 
    /// 逻辑:EventManager的m_Events相当于事件中心,其他脚本的事件都先给定一个id并注册到这个字典中,当需要运行的时候,从事件中心里查找对应id的事件执行
    /// 
    public class EventManager : MonoBehaviour
    {
        //一个参数给lua使用,lua的多个参数可以封装成table传进来
        public delegate void EventHandler(object args);
    
        private Dictionary<int, EventHandler> m_Events = new Dictionary<int, EventHandler>();
        
        //订阅事件
        public void Subscribe(int id, EventHandler e)
        {
            if (m_Events.ContainsKey(id))
                //相当于实现多播委托
                m_Events[id] += e;
            else
                m_Events.Add(id, e);
        }
        
        //取消订阅事件
        public void UnSubscribe(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);
            }
        }
        
        //执行事件
        public void Fire(int id, object args = null)
        {
            EventHandler handler;
            if (m_Events.TryGetValue(id, out handler))
            {
                handler(args);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    Manager添加EventManager

    public static EventManager Event
    {
        get { return _event; }
    }
    
    public void Awake()
    {
        _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>();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    接下来GameStart等脚本中的匿名委托都可以用EventManager进行处理

    public class GameStart : MonoBehaviour
    {
        public GameMode GameMode;
    
        // Start is called before the first frame update
        void Start()
        {
            //开始时订阅事件
            Manager.Event.Subscribe(10000, OnLuaInit);
            
            AppConst.GameMode = this.GameMode;
            DontDestroyOnLoad(this);
    
            Manager.Resource.ParseVersionFile();
            Manager.Lua.Init();
        }
    
        void OnLuaInit(object args)
        {
            //初始化完成之后(lua都加载完),在执行回调
            Manager.Lua.StartLua("main"); //输入的文件名
            //输入的是函数名
            XLua.LuaFunction func = Manager.Lua.LuaEnv.Global.Get<XLua.LuaFunction>("Main");
            func.Call();
        }
    
        public void OnApplicationQuit()
        {
            Manager.Event.UnSubscribe(10000, OnLuaInit);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    修改LuaManager

    public class LuaManager : MonoBehaviour
    {
        //所有的lua文件名,获取所有lua,然后进行预加载,ResourceManager查找lua文件放进来
        public List<string> LuaNames = new List<string>();
    
        //缓存lua脚本内容
        private Dictionary<string, byte[]> m_LuaScripts;
    
        //定义一个lua虚拟机,消耗比较大,,全局只需要一个,,需要using XLua;
        public LuaEnv LuaEnv;
        
        //如果是Editor模式下,直接从luapath就把所有lua读取到字典中了,然后在调用,属于同步加载后同步使用的情况
        //但是在其他模式下,需要从bundle异步加载lua需要等待,如果等待时start就调用了,属于异步加载同步使用的情况,需要预加载
        //需要创建一个回调通知
        public void Init()
        {
            //初始化虚拟机
            LuaEnv = new LuaEnv();
            //外部调用require时,会自动调用loader来获取文件
            LuaEnv.AddLoader(Loader);
    
            m_LuaScripts = new Dictionary<string, byte[]>();
    
    #if UNITY_EDITOR
            if (AppConst.GameMode == GameMode.EditorMode)
                EditorLoadLuaScript();
            else
    #endif
                LoadLuaScript();
        }
    
        void LoadLuaScript()
        {
            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 as TextAsset).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名字和内容放到集合内
        void EditorLoadLuaScript()
        {
            //搜索所有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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    关于声音管理中出现的C# callback报错,是因为C#先释放了LuaEnv,但是Lua中有回调函数还没有取消订阅,在这里需要处理。
    编写脚本Framework-Extension-UnityEx

    using UnityEngine.UI;
    
    [XLua.LuaCallCSharp]
    public static class UnityEx
    {
        //对按钮的事件的监听做一个扩展
        //C#监听的事件变成了C#的匿名委托了,而不是lua的方法,所以lua的方法变成了临时变量
        public static void OnClickSet(this Button button, object callback)
        {
            XLua.LuaFunction func = callback as XLua.LuaFunction;
            //监听事件前,先移除,因为不销毁这个物体,所以如果每次打开ui都监听,会监听无数个事件
            button.onClick.RemoveAllListeners();
            button.onClick.AddListener(
                () =>
                {
                    func?.Call();
                }
            );
        }
        
        public static void OnValueChangedSet(this Slider slider, object callback)
        {
            XLua.LuaFunction func = callback as XLua.LuaFunction;
            //监听事件前,先移除,因为不销毁这个物体,所以如果每次打开ui都监听,会监听无数个事件
            slider.onValueChanged.RemoveAllListeners();
            slider.onValueChanged.AddListener(
                (float value) =>
                {
                    func?.Call(value);
                }
            );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    P11扩展方法

    function OnInit()
        print("lua OnInit")
    end
    
    function OnOpen()
        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
    
    function Update()
        --print("lua Update")
    end
    
    function OnClose()
        print("lua OnClose")
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    public class UILogic : LuaBehaviour
    {
        Action m_LuaOnOpen;
        Action m_LuaOnClose;
        public override void Init(string luaName)
        {
            base.Init(luaName);
            m_ScriptEnv.Get("OnOpen", out m_LuaOnOpen);
            m_ScriptEnv.Get("OnClose", out m_LuaOnClose);
        }
    
        public void OnOpen()
        {
            m_LuaOnOpen?.Invoke();
        }
        public void OnClose()
        {
            m_LuaOnClose?.Invoke();
        }
        protected override void Clear()
        {
            //OnClose();
            base.Clear();
            m_LuaOnOpen = null;
            m_LuaOnClose = null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    把前面那些OnClose的又改回去了。

    XLua.ReflectionUse

    一个C#类型类型加了这个配置,xLua会生成link.xml阻止il2cpp的代码剪裁。
    对于扩展方法,必须加上LuaCallCSharp或者ReflectionUse才可以被访问到。
    建议所有要在Lua访问的类型,要么加LuaCallCSharp,要么加上ReflectionUse,这才能够保证在各平台都能正常运行。

  • 相关阅读:
    32.(前端)主页布局
    前端开发学习之【Vue】-下
    MySql数据恢复方法个人总结
    CENTOS7安装PROMETHUS(普罗米修斯)监控系统完整版
    手绘地图制作的关键点之“图层覆盖”
    在PostgreSQL中如何实现递归查询,例如使用WITH RECURSIVE构建层次结构数据?
    React@16.x(42)路由v5.x(7)常见应用场景(4)- 路由切换动画
    《算法通关村—如何基于数组(或者链表)实现栈》
    Kingbase备份与还原及表的约束(Kylin)
    高原制氧机的工作原理以及对高原地区生活质量的积极影响
  • 原文地址:https://blog.csdn.net/weixin_42264818/article/details/128211372