xlua - lua协程模拟Unity协程
目的:能在lua中像下面这样方便的使用协程
public class CoTest : MonoBehaviour { void Start() { var enu = MyCo(); StartCoroutine(enu); } IEnumerator MyCo() { Debug.Log($"frame 1: {Time.frameCount}"); yield return null; Debug.Log($"frame 2: {Time.frameCount}"); Debug.Log($"time 1: {Time.time}"); yield return new WaitForSeconds(1.2f); Debug.Log($"time 2: {Time.time}"); } }
Assets/Lua/LuaCoMgr.lua.txt
local _Time = CS.UnityEngine.Time local Mgr = {} LuaCoMgr = Mgr local Need_Wait_Flag = {} local Inner = {} local m_IdIcr = 0 local m_RunningCos = {} function Mgr.Start(func, args) local co = coroutine.create(func) m_IdIcr = m_IdIcr + 1 local coId = m_IdIcr local isSucc, yieldParam = coroutine.resume(co, args) --执行到第1个yield处暂停 if not isSucc then if debug and debug.traceback then local strackTrace = debug.traceback(co) print("Mgr.Start: co first start err: "..strackTrace) else print("Mgr.Start: co first start err") end return end if "dead" ~= coroutine.status(co) then local coCtxt = { id = coId, co = co, lastResumeTime = _Time.time, } if args then coCtxt.desc = args.desc end coCtxt.IsWait = Inner.IsNeedWait(yieldParam) table.insert(m_RunningCos, coCtxt) return coId else --已执行结束 end return 0 end function Mgr.StopCoById(coId) for i, v in ipairs(m_RunningCos) do if v.id == coId then v.stopFlag = true return true end end return false end function Mgr.WaitForSeconds(sec) local endTime = _Time.time + sec local obj = { flag = Need_Wait_Flag, IsWait = function() local leftTime = endTime - _Time.time return (leftTime > 0) end, } coroutine.yield(obj) --在此处暂停, resume唤醒时将得到yield这边传的参数 end function Mgr.WaitForFrames(frames) local endFrame = _Time.frameCount + frames local obj = { flag = Need_Wait_Flag, IsWait = function() local leftFrame = endFrame - _Time.frameCount return (leftFrame > 0) end, } coroutine.yield(obj) --在此处暂停, resume唤醒时将得到yield这边传的参数 end function Inner.IsNeedWait(yieldParam) if nil == yieldParam or "table" ~= type(yieldParam) then return nil end if yieldParam.flag ~= Need_Wait_Flag then return nil end local isWait = yieldParam.IsWait if nil == isWait or "function" ~= type(isWait) then return nil end return isWait end function Inner.ResumeCo(i, coCtxt) local isSucc, yieldParam = coroutine.resume(coCtxt.co) if not isSucc then --协程执行失败? if debug and debug.traceback then local strackTrace = debug.traceback(coCtxt.co) print("co resume fail: id:"..coCtxt.id..", "..strackTrace) else print("co resume fail: id:"..coCtxt.id) end table.remove(m_RunningCos, i) else if "dead" == coroutine.status(coCtxt.co) then print("co finish: id:"..coCtxt.id) table.remove(m_RunningCos, i) else coCtxt.IsWait = Inner.IsNeedWait(yieldParam) --resume后可能还要wait end end end function Mgr.OnUpdate() local curTime = _Time.time for i=#m_RunningCos,1,-1 do local coCtxt = m_RunningCos[i] if coCtxt.stopFlag or "dead" == coroutine.status(coCtxt.co) then table.remove(m_RunningCos, i) else local isWait = coCtxt.IsWait if isWait and isWait() then --wait local deltaTime = curTime - coCtxt.lastResumeTime if deltaTime >= 30 then print("!!!!!! co wait too long: id:"..coCtxt.coId..", time:"..deltaTime) end else coCtxt.lastResumeTime = curTime Inner.ResumeCo(i, coCtxt) end end end end
lua测试代码: Assets/Lua/Test5.lua.txt
require("Lua.LuaCoMgr") local _Time = CS.UnityEngine.Time; LuaCoMgr.Start(function() print("frame 1:".._Time.frameCount) LuaCoMgr.WaitForFrames(1) print("frame 2:".._Time.frameCount) print("time 1:".._Time.time) LuaCoMgr.WaitForSeconds(1.2) print("time 2:".._Time.time) end)
c#部分: Assets/Test5/Test5.cs
public class Test5 : MonoBehaviour { private LuaEnv m_LuaEnv; private LuaTable m_LuaCoMgr; private LuaFunction m_LuaCoMgr_OnUpdate; void Start() { m_LuaEnv = new LuaEnv(); m_LuaEnv.AddLoader((ref string filePath) => { filePath = filePath.Replace('.', '/'); filePath = $"Assets/{filePath}.lua.txt"; Debug.Log($"filePath:{filePath}"); var txtAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(filePath); return Encoding.UTF8.GetBytes(txtAsset.text); }); m_LuaEnv.Global.Set("Test5MonoInst", this); //在lua中增加一个全局变量,可以在lua中用TestMonoInst来访问c#对象 m_LuaEnv.DoString("require('Lua.Test5')"); m_LuaCoMgr = m_LuaEnv.Global.Get<LuaTable>("LuaCoMgr"); m_LuaCoMgr_OnUpdate = m_LuaCoMgr.Get<LuaFunction>("OnUpdate"); } void Update() { m_LuaCoMgr_OnUpdate.Action(0); } void OnDestroy() { m_LuaCoMgr = null; m_LuaCoMgr_OnUpdate = null; if (null != m_LuaEnv) { m_LuaEnv.Dispose(); } } }