xLua——LuaManager
作者:追风剑情 发布于:2021-8-25 14:44 分类:Lua
using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.IO; using UnityEngine; using XLua; /// <summary> /// Lua管理器 /// </summary> public class LuaManager : MonoBehaviour { public static LuaManager Instance { get; private set; } private const float GC_INTERVAL = 1;//1 second private float _lastGCTime = 0; private LuaEnv _luaEnv = new LuaEnv(); private List<WeakReference> luaBehList = new List<WeakReference>(); private Canvas _canvas; //加载列表 private List<UILoadingItem> _uiLoadingList = new List<UILoadingItem>(); private Dictionary<string, string> _luaChunkDic = new Dictionary<string, string>(); /// <summary> /// 执行Lua代码块 /// </summary> /// <param name="chunk">Lua代码块</param> /// <param name="chunkName">发生error时的debug显示信息中使用,通常等于脚本文件名(否则IDE调试不了)</param> /// <param name="env">这个代码块的环境变量</param> /// <returns>Lua代码返回的一个或多个值</returns> public object[] DoString(string chunk, string chunkName = "chunk", LuaTable env = null) { return _luaEnv.DoString(chunk, chunkName, env); } //加载一个代码块,但不执行,只返回类型可以指定为一个delegate或者一个LuaFunction public LuaFunction LoadString(string chunk, string chunkName = "chunk", LuaTable env = null) { return LoadString<LuaFunction>(chunk, chunkName, env); } //加载一个代码块,但不执行,只返回类型可以指定为一个delegate或者一个LuaFunction public T LoadString<T>(string chunk, string chunkName = "chunk", LuaTable env = null) { return _luaEnv.LoadString<T>(chunk, chunkName, env); } public LuaTable NewLuaTable() { if (_luaEnv == null) return null; return _luaEnv.NewTable(); } //为每个脚本设置一个独立的环境,可一定程度上防止脚本间全局变量、函数冲突 public LuaTable NewEvnLuaTable() { if (_luaEnv == null) return null; LuaTable scriptEnv = _luaEnv.NewTable(); LuaTable meta = _luaEnv.NewTable(); meta.Set("__index", _luaEnv.Global); scriptEnv.SetMetaTable(meta); meta.Dispose(); return scriptEnv; } //通过 lua chunk 创建 LuaTable public LuaTable CreateLuaTable(string chunk, string chunkName = "chunk") { LuaTable scriptEnv = NewEvnLuaTable(); object[] returns = _luaEnv.DoString(chunk, chunkName, scriptEnv); //判断Lua文件是否返回了一个作为类的table对象 if (returns != null || returns.Length > 0) { LuaTable classTable = returns[0] as LuaTable; string className; classTable.Get<string, string>("__name", out className); Debug.LogFormat("CS: return lua class {0}", className); scriptEnv = classTable; } else { //Lua文件没按照OOP方式编程,这里认为采用了函数式编程 Debug.Log("Lua UI No value was returned"); } return scriptEnv; } //通过Lua文件创建LuaBehaviour对象 public LuaBehaviour CreateLuaBehaviour(string chunk, string chunkName = "chunk") { LuaTable scriptEnv = CreateLuaTable(chunk, chunkName); GameObject go = new GameObject(chunkName); var luaBehaviour = go.AddComponent<LuaBehaviour>(); luaBehaviour.AttachLuaScript(scriptEnv); luaBehList.Add(new WeakReference(luaBehaviour)); return luaBehaviour; } //添加LuaBehaviour组件 public void AddLuaBehaviour(GameObject go, string chunk=null, LuaTable parameter = null) { var luaBehaviour = go.GetComponent<LuaBehaviour>(); if (luaBehaviour == null) luaBehaviour = go.AddComponent<LuaBehaviour>(); luaBehList.Add(new WeakReference(luaBehaviour)); if (string.IsNullOrEmpty(chunk)) return; LuaTable scriptEnv = CreateLuaTable(chunk); luaBehaviour.AttachLuaScript(scriptEnv, parameter); } public void AddLuaBehaviour(GameObject go, LuaTable scriptEnv, LuaTable parameter = null) { var luaBehaviour = go.GetComponent<LuaBehaviour>(); if (luaBehaviour == null) luaBehaviour = go.AddComponent<LuaBehaviour>(); luaBehList.Add(new WeakReference(luaBehaviour)); luaBehaviour.AttachLuaScript(scriptEnv, parameter); } //UI的默认Canvas public Canvas DefaultCanvas { get { if (_canvas == null) { GameObject go = GameObject.Find("Canvas"); _canvas = go.GetComponent<Canvas>(); } return _canvas; } } //加载 lua bundle (app.lua) public void LoadLuaBundle(string url, Action callback=null, Action<string> errorback=null) { StartCoroutine(CoroutineLoadLuaBundle(url, callback, errorback)); } private IEnumerator CoroutineLoadLuaBundle(string url, Action callback = null, Action<string> errorback = null) { yield return null; WWW www = new WWW(url); while (!www.isDone) yield return www; if (!string.IsNullOrEmpty(www.error)) { Debug.LogErrorFormat("{0}\n{1}", www.error, url); errorback?.Invoke(url); yield break; } AssetBundle ab = www.assetBundle; if (ab == null) { Debug.LogErrorFormat("Error: assetBundle=null \n{0}", url); errorback?.Invoke(url); yield break; } //将lua chunk缓存到字典 string[] names = ab.GetAllAssetNames(); foreach (var name in names) { if (_luaChunkDic.ContainsKey(name)) continue; TextAsset textAsset = ab.LoadAsset<TextAsset>(name); _luaChunkDic.Add(name, textAsset.text); } callback?.Invoke(); } //执行Lua Chunk public object[] DoLuaChunk(string chunkName) { chunkName = chunkName.ToLower(); if (!_luaChunkDic.ContainsKey(chunkName)) { Debug.LogErrorFormat("No Lua Chunk: {0}", chunkName); return null; } string chunk = _luaChunkDic[chunkName]; return DoString(chunk, chunkName, NewEvnLuaTable()); } //加载Resources下的UI public GameObject LoadUIFromResources(string uiName, string chunk) { //加载 GameObject ui = Resources.Load<GameObject>(uiName); if (ui == null) { Debug.LogErrorFormat("Resources unable to load {0}.prefab", uiName); return null; } //克隆UI GameObject ui_clone = GameObject.Instantiate<GameObject>(ui); ui_clone.transform.SetParent(DefaultCanvas.transform); ui_clone.transform.localPosition = Vector3.zero; ui_clone.transform.localRotation = Quaternion.identity; ui_clone.transform.localScale = Vector3.one; ui_clone.transform.SetAsLastSibling(); //检查是否有LuaBehaviour组件 LuaBehaviour luaBehaviour = ui_clone.GetComponent<LuaBehaviour>(); if (luaBehaviour == null) luaBehaviour = ui_clone.AddComponent<LuaBehaviour>(); //关联Lua脚本 LuaTable scriptEnv = CreateLuaTable(chunk, uiName); luaBehaviour.AttachLuaScript(scriptEnv); luaBehList.Add(new WeakReference(luaBehaviour)); return ui_clone; } //从URL加载Lua文件 public void LoadLuaFromURL(string url, Action<LuaTable> callback, Action<string> errorback = null) { StartCoroutine(CoroutineLoadLuaFromURL(url, callback, errorback)); } private IEnumerator CoroutineLoadLuaFromURL(string url, Action<LuaTable> callback, Action<string> errorback = null) { yield return null; WWW www = new WWW(url); while (!www.isDone) yield return www; if (!string.IsNullOrEmpty(www.error)) { Debug.LogErrorFormat("{0}\n{1}", www.error, url); errorback?.Invoke(url); yield break; } AssetBundle ab = www.assetBundle; if (ab == null) { Debug.LogErrorFormat("Error: assetBundle=null \n{0}", url); errorback?.Invoke(url); yield break; } object[] objs = ab.LoadAllAssets(); if (objs == null || objs.Length == 0) { Debug.LogErrorFormat("Error: lua assetBundle is empty. \n{0}", url); errorback?.Invoke(url); yield break; } TextAsset chunk = objs[0] as TextAsset; object[] tables = DoString(chunk.text); if (tables == null || tables.Length <= 0) { Debug.LogErrorFormat("Error: the lua chunk is not returned. \n{0}", url); errorback?.Invoke(url); yield break; } LuaTable table = tables[0] as LuaTable; callback?.Invoke(table); } //从URL加载UI public void LoadUIFromURL(string url, Action<GameObject> callback, Action<string> errorback=null) { StartCoroutine(CoroutineLoadUIFromURL(url, callback, errorback)); } private IEnumerator CoroutineLoadUIFromURL(string url, Action<GameObject> callback, Action<string> errorback=null) { yield return null; WWW www = new WWW(url); while (!www.isDone) yield return www; if (!string.IsNullOrEmpty(www.error)) { Debug.LogErrorFormat("{0}\n{1}", www.error, url); errorback?.Invoke(url); yield break; } AssetBundle ab = www.assetBundle; if (ab == null) { Debug.LogErrorFormat("Error: assetBundle=null \n{0}", url); errorback?.Invoke(url); yield break; } object[] objs = ab.LoadAllAssets(); if (objs == null || objs.Length == 0) { Debug.LogErrorFormat("Error: UI assetBundle is empty. \n{0}", url); errorback?.Invoke(url); yield break; } GameObject ui = objs[0] as GameObject; if (ui == null) { Debug.LogErrorFormat("type is not GameObject. \n{0}", url); errorback?.Invoke(url); yield break; } GameObject clone = GameObject.Instantiate<GameObject>(ui); callback?.Invoke(clone); } //异步 加载UI & 关联Lua脚本 public void LoadUIAsync(string uiUrl, string chunkUrl, LuaTable parameter = null, Action < GameObject> callback=null, Action<string> errorback=null) { UILoadingItem item = new UILoadingItem(); item.parameter = parameter; item.Parent = DefaultCanvas.transform; item.OnCreateLuaBehaviour = (luaBehaviour) => { luaBehList.Add(new WeakReference(luaBehaviour)); }; item.OnCompleted = callback; item.OnError = errorback; _uiLoadingList.Add(item); //加载lua string chunkName = Path.GetFileNameWithoutExtension(chunkUrl); bool hasChunk = _luaChunkDic.ContainsKey(chunkName); if (hasChunk) item.scriptEnv = CreateLuaTable(_luaChunkDic[chunkName]); else LoadLuaFromURL(chunkUrl, item.OnLuaCompleted, item.OnErrorCallback); //加载UI LoadUIFromURL(uiUrl, item.OnUICompleted, item.OnErrorCallback); } //Lua 自定义加载器 private byte[] OnCustomLoader(ref string filepath) { string chunkName = filepath.ToLower(); Debug.LogFormat("CustomLoader({0})", chunkName); string chunk = string.Empty; if (_luaChunkDic.ContainsKey(chunkName)) chunk = _luaChunkDic[chunkName]; byte[] bytes = Encoding.UTF8.GetBytes(chunk); return bytes; } private void Awake() { Instance = this; DontDestroyOnLoad(gameObject); _luaEnv.AddLoader(OnCustomLoader); } private void Start() { StartCoroutine(CheckLuaBehList()); } private void Update() { if (_luaEnv != null) { //定时GC if (Time.time - _lastGCTime > GC_INTERVAL) { _luaEnv.Tick(); _lastGCTime = Time.time; } } //移除在加载过程中出错或已完成的UI for (int i=0; i<_uiLoadingList.Count; i++) { var item = _uiLoadingList[i]; if (item.IsDone || item.IsError) { item.Dispose(); _uiLoadingList.RemoveAt(i); break; } } } private IEnumerator CheckLuaBehList() { while(true) { //移除无效的LuaBehaviour对象 for (int i = 0; i < luaBehList.Count; i++) { if (!luaBehList[i].IsAlive) { luaBehList.RemoveAt(i); yield return null; } //避免遍历耗时太久 if (i % 100 == 0) yield return null; } yield return new WaitForSeconds(1f); } } private void OnDestroy() { StopAllCoroutines(); DisposeAllLuaReference(); //在调用Dispose()之前必须释放所有scriptEnv.Get()产生的引用(即,引用设为null) //否则,会抛出下面错误 //InvalidOperationException: try to dispose a LuaEnv with C# callback! _luaEnv.Dispose(); _luaEnv = null; Instance = null; } //切换场景时需要调用 //释放所有Lua引用以及触发Lua层的OnDestroy()回调 public void DisposeAllLuaReference() { for (int i = 0; i < luaBehList.Count; i++) { if (luaBehList[i].IsAlive) { (luaBehList[i].Target as LuaBehaviour).DisposeLuaReference(); } } luaBehList.Clear(); } } //UI Loading List Item public sealed class UILoadingItem { public GameObject UI { get; private set; } public LuaTable scriptEnv { get; set; } public LuaBehaviour luaBehaviour { get; private set; } public Transform Parent { get; set; } public bool IsError { get; private set; } public Action<LuaBehaviour> OnCreateLuaBehaviour; public Action<GameObject> OnCompleted; public Action<string> OnError; public LuaTable parameter = null; //是否加载完成 public bool IsDone { get { return UI != null && scriptEnv != null; } } private void AttachParent() { if (Parent == null) return; Transform trans = UI.transform; trans.SetParent(Parent); trans.localPosition = Vector3.zero; trans.localRotation = Quaternion.identity; trans.localScale = Vector3.one; } public void OnUICompleted(GameObject ui) { UI = ui; //检查是否有LuaBehaviour组件 luaBehaviour = UI.GetComponent<LuaBehaviour>(); if (luaBehaviour == null) luaBehaviour = UI.AddComponent<LuaBehaviour>(); if (scriptEnv != null) luaBehaviour.AttachLuaScript(scriptEnv, parameter); AttachParent(); OnCreateLuaBehaviour?.Invoke(luaBehaviour); if (scriptEnv != null) OnCompleted?.Invoke(ui); } public void OnLuaCompleted(LuaTable scriptEnv) { this.scriptEnv = scriptEnv; if (luaBehaviour != null) luaBehaviour.AttachLuaScript(scriptEnv, parameter); if (luaBehaviour != null) OnCompleted?.Invoke(UI); } public void OnErrorCallback(string url) { Debug.LogError(url); IsError = true; OnError?.Invoke(url); } public void Dispose() { UI = null; scriptEnv = null; luaBehaviour = null; Parent = null; OnCreateLuaBehaviour = null; OnCompleted = null; OnError = null; parameter = null; } }
调用方式
//将所有lua文件打包到app.lua中 string lua_url = string.Format("{0}/lua/app.lua", Application.streamingAssetsPath); LuaManager.Instance.LoadLuaBundle(lua_url, () => { Debug.Log("app.lua load completed."); //执行Lua侧入口程序 LuaManager.Instance.DoLuaChunk("Main"); //加载一个测试UI LuaManager.Instance.LoadUIAsync(ui_url, "luatestscript", parameter, (ui)=> { Debug.LogFormat("Load UI Completed! {0}", ui.name); }, (error)=> { Debug.LogError(error); }); });
标签: xLua
日历
最新文章
随机文章
热门文章
分类
存档
- 2024年11月(3)
- 2024年10月(5)
- 2024年9月(3)
- 2024年8月(3)
- 2024年7月(11)
- 2024年6月(3)
- 2024年5月(9)
- 2024年4月(10)
- 2024年3月(11)
- 2024年2月(24)
- 2024年1月(12)
- 2023年12月(3)
- 2023年11月(9)
- 2023年10月(7)
- 2023年9月(2)
- 2023年8月(7)
- 2023年7月(9)
- 2023年6月(6)
- 2023年5月(7)
- 2023年4月(11)
- 2023年3月(6)
- 2023年2月(11)
- 2023年1月(8)
- 2022年12月(2)
- 2022年11月(4)
- 2022年10月(10)
- 2022年9月(2)
- 2022年8月(13)
- 2022年7月(7)
- 2022年6月(11)
- 2022年5月(18)
- 2022年4月(29)
- 2022年3月(5)
- 2022年2月(6)
- 2022年1月(8)
- 2021年12月(5)
- 2021年11月(3)
- 2021年10月(4)
- 2021年9月(9)
- 2021年8月(14)
- 2021年7月(8)
- 2021年6月(5)
- 2021年5月(2)
- 2021年4月(3)
- 2021年3月(7)
- 2021年2月(2)
- 2021年1月(8)
- 2020年12月(7)
- 2020年11月(2)
- 2020年10月(6)
- 2020年9月(9)
- 2020年8月(10)
- 2020年7月(9)
- 2020年6月(18)
- 2020年5月(4)
- 2020年4月(25)
- 2020年3月(38)
- 2020年1月(21)
- 2019年12月(13)
- 2019年11月(29)
- 2019年10月(44)
- 2019年9月(17)
- 2019年8月(18)
- 2019年7月(25)
- 2019年6月(25)
- 2019年5月(17)
- 2019年4月(10)
- 2019年3月(36)
- 2019年2月(35)
- 2019年1月(28)
- 2018年12月(30)
- 2018年11月(22)
- 2018年10月(4)
- 2018年9月(7)
- 2018年8月(13)
- 2018年7月(13)
- 2018年6月(6)
- 2018年5月(5)
- 2018年4月(13)
- 2018年3月(5)
- 2018年2月(3)
- 2018年1月(8)
- 2017年12月(35)
- 2017年11月(17)
- 2017年10月(16)
- 2017年9月(17)
- 2017年8月(20)
- 2017年7月(34)
- 2017年6月(17)
- 2017年5月(15)
- 2017年4月(32)
- 2017年3月(8)
- 2017年2月(2)
- 2017年1月(5)
- 2016年12月(14)
- 2016年11月(26)
- 2016年10月(12)
- 2016年9月(25)
- 2016年8月(32)
- 2016年7月(14)
- 2016年6月(21)
- 2016年5月(17)
- 2016年4月(13)
- 2016年3月(8)
- 2016年2月(8)
- 2016年1月(18)
- 2015年12月(13)
- 2015年11月(15)
- 2015年10月(12)
- 2015年9月(18)
- 2015年8月(21)
- 2015年7月(35)
- 2015年6月(13)
- 2015年5月(9)
- 2015年4月(4)
- 2015年3月(5)
- 2015年2月(4)
- 2015年1月(13)
- 2014年12月(7)
- 2014年11月(5)
- 2014年10月(4)
- 2014年9月(8)
- 2014年8月(16)
- 2014年7月(26)
- 2014年6月(22)
- 2014年5月(28)
- 2014年4月(15)
友情链接
- Unity官网
- Unity圣典
- Unity在线手册
- Unity中文手册(圣典)
- Unity官方中文论坛
- Unity游戏蛮牛用户文档
- Unity下载存档
- Unity引擎源码下载
- Unity服务
- Unity Ads
- wiki.unity3d
- Visual Studio Code官网
- SenseAR开发文档
- MSDN
- C# 参考
- C# 编程指南
- .NET Framework类库
- .NET 文档
- .NET 开发
- WPF官方文档
- uLua
- xLua
- SharpZipLib
- Protobuf-net
- Protobuf.js
- OpenSSL
- OPEN CASCADE
- JSON
- MessagePack
- C在线工具
- 游戏蛮牛
- GreenVPN
- 聚合数据
- 热云
- 融云
- 腾讯云
- 腾讯开放平台
- 腾讯游戏服务
- 腾讯游戏开发者平台
- 腾讯课堂
- 微信开放平台
- 腾讯实时音视频
- 腾讯即时通信IM
- 微信公众平台技术文档
- 白鹭引擎官网
- 白鹭引擎开放平台
- 白鹭引擎开发文档
- FairyGUI编辑器
- PureMVC-TypeScript
- 讯飞开放平台
- 亲加通讯云
- Cygwin
- Mono开发者联盟
- Scut游戏服务器引擎
- KBEngine游戏服务器引擎
- Photon游戏服务器引擎
- 码云
- SharpSvn
- 腾讯bugly
- 4399原创平台
- 开源中国
- Firebase
- Firebase-Admob-Unity
- google-services-unity
- Firebase SDK for Unity
- Google-Firebase-SDK
- AppsFlyer SDK
- android-repository
- CQASO
- Facebook开发者平台
- gradle下载
- GradleBuildTool下载
- Android Developers
- Google中国开发者
- AndroidDevTools
- Android社区
- Android开发工具
- Google Play Games Services
- Google商店
- Google APIs for Android
- 金钱豹VPN
- TouchSense SDK
- MakeHuman
- Online RSA Key Converter
- Windows UWP应用
- Visual Studio For Unity
- Open CASCADE Technology
- 慕课网
- 阿里云服务器ECS
- 在线免费文字转语音系统
- AI Studio
- 网云穿
- 百度网盘开放平台
- 迅捷画图
- 菜鸟工具
- [CSDN] 程序员研修院
- 华为人脸识别
- 百度AR导航导览SDK
- 海康威视官网
- 海康开放平台
- 海康SDK下载
- git download
交流QQ群
-
Flash游戏设计: 86184192
Unity游戏设计: 171855449
游戏设计订阅号