讲解xLua例6 Coroutine,应该从中学习到什么。可将协程写法分为C#协程,lua协程和xLua协程。本文关注的是xLua协程。
一.例6 Coroutine代码分析
1.1 CoroutineTest.cs
用来执行coruntine_test.lua,没有业务逻辑
cs
using UnityEngine;
using XLua;
namespace XLuaTest
{
public class CoroutineTest : MonoBehaviour
{
LuaEnv luaenv = null;
// Use this for initialization
void Start()
{
luaenv = new LuaEnv();
luaenv.DoString("require 'coruntine_test'");
}
// Update is called once per frame
void Update()
{
if (luaenv != null)
{
luaenv.Tick();
}
}
void OnDestroy()
{
luaenv.Dispose();
}
}
}
1.2 coruntine_test.lua
业务代码,lua侧模拟调用C#协程,这里先注释掉一部分代码将demo简化,有助于理解这个demo:
1.cs_coroutine.start相当于c#的StartCoroutine
2.coroutine.yield(CS.UnityEngine.WaitForSeconds(1))相当于c#的yield return new WaitForSeconds(1f);
Lua
local cs_coroutine = (require 'cs_coroutine')
local a = cs_coroutine.start(function()
print('coroutine a started')
-- coroutine.yield(cs_coroutine.start(function()
-- print('coroutine b stated inside cotoutine a')
-- coroutine.yield(CS.UnityEngine.WaitForSeconds(1))
-- print('i am coroutine b')
-- end))
--print('coroutine b finish')
while true do
coroutine.yield(CS.UnityEngine.WaitForSeconds(1))
print('i am coroutine a')
end
end)
-- cs_coroutine.start(function()
-- print('stop coroutine a after 5 seconds')
-- coroutine.yield(CS.UnityEngine.WaitForSeconds(5))
-- cs_coroutine.stop(a)
-- print('coroutine a stoped')
-- end)
1.3 cs_coroutine.lua
Lua侧支持协程的module,获取脚本组件cs_coroutine_runner,用来执行协程方法StartCoroutine和StopCoroutine
Lua
local util = require 'xlua.util'
local gameobject = CS.UnityEngine.GameObject('Coroutine_Runner')
CS.UnityEngine.Object.DontDestroyOnLoad(gameobject)
local cs_coroutine_runner = gameobject:AddComponent(typeof(CS.XLuaTest.Coroutine_Runner))
return {
start = function(...)
return cs_coroutine_runner:StartCoroutine(util.cs_generator(...))
end;
stop = function(coroutine)
cs_coroutine_runner:StopCoroutine(coroutine)
end
}
1.4 Coroutine_Runner.cs
空的MonoBehaviour,用静态列表导出WaitForSeconds
cs
using UnityEngine;
using XLua;
using System.Collections.Generic;
using System.Collections;
using System;
namespace XLuaTest
{
public class Coroutine_Runner : MonoBehaviour
{
}
public static class CoroutineConfig
{
[LuaCallCSharp]
public static List<Type> LuaCallCSharp
{
get
{
return new List<Type>()
{
typeof(WaitForSeconds),
typeof(WWW)
};
}
}
}
}
1.5 util.lua
协程支持文件
Lua
local unpack = unpack or table.unpack
local function async_to_sync(async_func, callback_pos)
return function(...)
local _co = coroutine.running() or error ('this function must be run in coroutine')
local rets
local waiting = false
local function cb_func(...)
if waiting then
assert(coroutine.resume(_co, ...))
else
rets = {...}
end
end
local params = {...}
table.insert(params, callback_pos or (#params + 1), cb_func)
async_func(unpack(params))
if rets == nil then
waiting = true
rets = {coroutine.yield()}
end
return unpack(rets)
end
end
local function coroutine_call(func)
return function(...)
local co = coroutine.create(func)
assert(coroutine.resume(co, ...))
end
end
local move_end = {}
local generator_mt = {
__index = {
MoveNext = function(self)
self.Current = self.co()
if self.Current == move_end then
self.Current = nil
return false
else
return true
end
end;
Reset = function(self)
self.co = coroutine.wrap(self.w_func)
end
}
}
local function cs_generator(func, ...)
local params = {...}
local generator = setmetatable({
w_func = function()
func(unpack(params))
return move_end
end
}, generator_mt)
generator:Reset()
return generator
end
local function loadpackage(...)
for _, loader in ipairs(package.searchers) do
local func = loader(...)
if type(func) == 'function' then
return func
end
end
end
local function auto_id_map()
local hotfix_id_map = require 'hotfix_id_map'
local org_hotfix = xlua.hotfix
xlua.hotfix = function(cs, field, func)
local map_info_of_type = hotfix_id_map[typeof(cs):ToString()]
if map_info_of_type then
if func == nil then func = false end
local tbl = (type(field) == 'table') and field or {[field] = func}
for k, v in pairs(tbl) do
local map_info_of_methods = map_info_of_type[k]
local f = type(v) == 'function' and v or nil
for _, id in ipairs(map_info_of_methods or {}) do
CS.XLua.HotfixDelegateBridge.Set(id, f)
end
--CS.XLua.HotfixDelegateBridge.Set(
end
xlua.private_accessible(cs)
else
return org_hotfix(cs, field, func)
end
end
end
--和xlua.hotfix的区别是:这个可以调用原来的函数
local function hotfix_ex(cs, field, func)
assert(type(field) == 'string' and type(func) == 'function', 'invalid argument: #2 string needed, #3 function needed!')
local function func_after(...)
xlua.hotfix(cs, field, nil)
local ret = {func(...)}
xlua.hotfix(cs, field, func_after)
return unpack(ret)
end
xlua.hotfix(cs, field, func_after)
end
local function bind(func, obj)
return function(...)
return func(obj, ...)
end
end
--为了兼容luajit,lua53版本直接用|操作符即可
local enum_or_op = debug.getmetatable(CS.System.Reflection.BindingFlags.Public).__bor
local enum_or_op_ex = function(first, ...)
for _, e in ipairs({...}) do
first = enum_or_op(first, e)
end
return first
end
-- description: 直接用C#函数创建delegate
local function createdelegate(delegate_cls, obj, impl_cls, method_name, parameter_type_list)
local flag = enum_or_op_ex(CS.System.Reflection.BindingFlags.Public, CS.System.Reflection.BindingFlags.NonPublic,
CS.System.Reflection.BindingFlags.Instance, CS.System.Reflection.BindingFlags.Static)
local m = parameter_type_list and typeof(impl_cls):GetMethod(method_name, flag, nil, parameter_type_list, nil)
or typeof(impl_cls):GetMethod(method_name, flag)
return CS.System.Delegate.CreateDelegate(typeof(delegate_cls), obj, m)
end
local function state(csobj, state)
local csobj_mt = getmetatable(csobj)
for k, v in pairs(csobj_mt) do rawset(state, k, v) end
local csobj_index, csobj_newindex = state.__index, state.__newindex
state.__index = function(obj, k)
return rawget(state, k) or csobj_index(obj, k)
end
state.__newindex = function(obj, k, v)
if rawget(state, k) ~= nil then
rawset(state, k, v)
else
csobj_newindex(obj, k, v)
end
end
debug.setmetatable(csobj, state)
return state
end
local function print_func_ref_by_csharp()
local registry = debug.getregistry()
for k, v in pairs(registry) do
if type(k) == 'number' and type(v) == 'function' and registry[v] == k then
local info = debug.getinfo(v)
print(string.format('%s:%d', info.short_src, info.linedefined))
end
end
end
return {
async_to_sync = async_to_sync,
coroutine_call = coroutine_call,
cs_generator = cs_generator,
loadpackage = loadpackage,
auto_id_map = auto_id_map,
hotfix_ex = hotfix_ex,
bind = bind,
createdelegate = createdelegate,
state = state,
print_func_ref_by_csharp = print_func_ref_by_csharp,
}
二.例6 Coroutine运行展示
先打印coroutine a started,然后每隔1秒打印i am coroutine a

三.例6 Coroutine总结
例6 Coroutine,应该从中学习到什么?
1.lua模拟C#的协程写法(比如用StartCoroutine和yield return...)不是一个很简单的事,需要一些代码支持(cs_coroutine.lua和util.lua)
2.util.lua这种非面向对象table写法:阅读时先看return,对外提供方法放在return中,实现在local function中,一定要熟悉和适应(笔者第一个商业xLua项目看到这种写法很不习惯,不知道从哪里来的,现在知道了可能来自util.lua)
3.util.lua中的函数需要熟悉,商业xLua项目中有可能lua模拟C#的协程写法不一定用,但是去使用util.lua中的函数,或者把util.lua放在xLua之外的目录,应该知道util.lua不是自研的,来自xLua