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
日历
最新文章
随机文章
热门文章
分类
存档
- 2025年11月(1)
- 2025年9月(3)
- 2025年7月(4)
- 2025年6月(5)
- 2025年5月(1)
- 2025年4月(5)
- 2025年3月(4)
- 2025年2月(3)
- 2025年1月(1)
- 2024年12月(5)
- 2024年11月(5)
- 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
- Open CASCADE
- CascadeStudio
交流QQ群
-
Flash游戏设计: 86184192
Unity游戏设计: 171855449
游戏设计订阅号






