u3d插件xLua[七]例6 Coroutine

讲解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

相关推荐
还是大剑师兰特2 小时前
Unity 从零搭建简易3D场景(新手分步教程)
3d·unity·游戏引擎
神码编程2 小时前
【Unity】TankBattle联机坦克大战(三)创建关卡(生成地块)
unity·游戏引擎·帧同步·坦克大战
淡海水3 小时前
C#与常用数据结构源码剖析-全篇导览
开发语言·数据结构·unity·面试·职场和发展·c#
玖玥拾1 天前
Unity3D RPG 入门项目(四)背包拖拽、物品悬浮提示、项目开发思维
3d·unity·游戏引擎
xcLeigh1 天前
Unity基础:Start与Update方法——Unity脚本生命周期初探
java·unity·游戏引擎·教程
郝学胜-神的一滴1 天前
[简化版 GAMES 104] 现代游戏引擎 05:游戏引擎世界构建核心机制深度解析
c++·程序人生·unity·游戏引擎·计算机图形学·opengl
LONGZETECH2 天前
无人机实训高成本痛点解法:虚拟仿真实现 70% 耗材损耗下降
大数据·算法·unity·架构·无人机
五仁烧饼3 天前
Unity Addressables 静默资源策略
游戏·unity·addressables
fanfan_hongyun3 天前
unity 与cad 坐标系映射
unity·游戏引擎