toLua[六] Examples 05_LuaCoroutine分析

一.运行工程

本例展示Lua模拟Unity的Coroutine携程

二.C#代码分析

cs 复制代码
using UnityEngine;
using System;
using System.Collections;
using LuaInterface;

//例子5和6展示的两套协同系统勿交叉使用,此为推荐方案
public class TestCoroutine : MonoBehaviour 
{
    public TextAsset luaFile = null;
    private LuaState lua = null;
    private LuaLooper looper = null;

	void Awake () 
    {
#if UNITY_5 || UNITY_2017 || UNITY_2018
        Application.logMessageReceived += ShowTips;
#else
        Application.RegisterLogCallback(ShowTips);
#endif        
        new LuaResLoader();
        lua  = new LuaState();
        lua.Start();
        LuaBinder.Bind(lua);
        DelegateFactory.Init();         
        looper = gameObject.AddComponent<LuaLooper>();
        looper.luaState = lua;

        lua.DoString(luaFile.text, "TestLuaCoroutine.lua");
        LuaFunction f = lua.GetFunction("TestCortinue");
        f.Call();
        f.Dispose();
        f = null;        
    }

    void OnApplicationQuit()
    {
        looper.Destroy();
        lua.Dispose();
        lua = null;
#if UNITY_5 || UNITY_2017 || UNITY_2018
        Application.logMessageReceived -= ShowTips;
#else
        Application.RegisterLogCallback(null);
#endif
    }

    string tips = null;

    void ShowTips(string msg, string stackTrace, LogType type)
    {
        tips += msg;
        tips += "\r\n";
    }

    void OnGUI()
    {
        GUI.Label(new Rect(Screen.width / 2 - 300, Screen.height / 2 - 200, 600, 400), tips);

        if (GUI.Button(new Rect(50, 50, 120, 45), "Start Counter"))
        {
            tips = null;
            LuaFunction func = lua.GetFunction("StartDelay");
            func.Call();
            func.Dispose();
        }
        else if (GUI.Button(new Rect(50, 150, 120, 45), "Stop Counter"))
        {
            LuaFunction func = lua.GetFunction("StopDelay");
            func.Call();
            func.Dispose();
        }
        else if (GUI.Button(new Rect(50, 250, 120, 45), "GC"))
        {
            lua.DoString("collectgarbage('collect')", "TestCoroutine.cs");
            Resources.UnloadUnusedAssets();
        }
    }
}

2.1 DoString执行文件的方式

首先来看第九行:

cs 复制代码
public TextAsset luaFile = null;

TextAsset类可以获取文件的文本内容;用TextAsset.text可以访问文件的文本

因此第28行可以用DoString来执行TestLuaCoroutine.lua文件

cs 复制代码
lua.DoString(luaFile.text, "TestLuaCoroutine.lua");

故本例演示了除LuaState:DoFile(string fileName)和LuaState:Require(string fileName)之外执行Lua文件的第三种方式

Unity手册的解释:

文本资源的一个特性就在于它可用于存储二进制数据。通过为文件提供扩展名 .bytes,即可将其作为文本资源加载,并可通过 bytes 属性来访问数据

这解释了TestLuaCoroutine文件为什么以.bytes为后缀名

参考:

文本资源

2.2 LuaLooper类

第25行给对象加了脚本组件LuaLooper

cs 复制代码
looper = gameObject.AddComponent<LuaLooper>();
cs 复制代码
/*
Copyright (c) 2015-2017 topameng(topameng@qq.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using System;
using UnityEngine;
using LuaInterface;

public class LuaLooper : MonoBehaviour 
{    
    public LuaBeatEvent UpdateEvent
    {
        get;
        private set;
    }

    public LuaBeatEvent LateUpdateEvent
    {
        get;
        private set;
    }

    public LuaBeatEvent FixedUpdateEvent
    {
        get;
        private set;
    }

    public LuaState luaState = null;

    void Start() 
    {
        try
        {
            UpdateEvent = GetEvent("UpdateBeat");
            LateUpdateEvent = GetEvent("LateUpdateBeat");
            FixedUpdateEvent = GetEvent("FixedUpdateBeat");
        }
        catch (Exception e)
        {
            Destroy(this);
            throw e;
        }        
	}

    LuaBeatEvent GetEvent(string name)
    {
        LuaTable table = luaState.GetTable(name);

        if (table == null)
        {
            throw new LuaException(string.Format("Lua table {0} not exists", name));
        }

        LuaBeatEvent e = new LuaBeatEvent(table);
        table.Dispose();
        table = null;
        return e;
    }

    void ThrowException()
    {
        string error = luaState.LuaToString(-1);
        luaState.LuaPop(2);                
        throw new LuaException(error, LuaException.GetLastError());
    }

    void Update()
    {
#if UNITY_EDITOR
        if (luaState == null)
        {
            return;
        }
#endif
        if (luaState.LuaUpdate(Time.deltaTime, Time.unscaledDeltaTime) != 0)
        {
            ThrowException();
        }

        luaState.LuaPop(1);
        luaState.Collect();
#if UNITY_EDITOR
        luaState.CheckTop();
#endif
    }

    void LateUpdate()
    {
#if UNITY_EDITOR
        if (luaState == null)
        {
            return;
        }
#endif
        if (luaState.LuaLateUpdate() != 0)
        {
            ThrowException();
        }

        luaState.LuaPop(1);
    }

    void FixedUpdate()
    {
#if UNITY_EDITOR
        if (luaState == null)
        {
            return;
        }
#endif
        if (luaState.LuaFixedUpdate(Time.fixedDeltaTime) != 0)
        {
            ThrowException();
        }

        luaState.LuaPop(1);
    }

    public void Destroy()
    {
        if (luaState != null)
        {
            if (UpdateEvent != null)
            {
                UpdateEvent.Dispose();
                UpdateEvent = null;
            }

            if (LateUpdateEvent != null)
            {
                LateUpdateEvent.Dispose();
                LateUpdateEvent = null;
            }

            if (FixedUpdateEvent != null)
            {
                FixedUpdateEvent.Dispose();
                FixedUpdateEvent = null;
            }

            luaState = null;
        }
    }

    void OnDestroy()
    {
        if (luaState != null)
        {
            Destroy();
        }
    }
}

LuaLooper的作用是驱动协程的运行,这里暂不作展开

三.Lua代码分析

3.1 TestLuaCoroutine.lua.bytes

Lua 复制代码
function fib(n)
    local a, b = 0, 1
    while n > 0 do
        a, b = b, a + b
        n = n - 1
    end

    return a
end

function CoFunc()
    print('Coroutine started')    
    for i = 0, 10, 1 do
        print(fib(i))                    
        coroutine.wait(0.1)						
    end	
	print("current frameCount: "..Time.frameCount)
	coroutine.step()
	print("yield frameCount: "..Time.frameCount)

	local www = UnityEngine.WWW("http://www.baidu.com")
	coroutine.www(www)
	local s = tolua.tolstring(www.bytes)
	print(s:sub(1, 128))
    print('Coroutine ended')
end

function TestCortinue()	
    coroutine.start(CoFunc)
end

local coDelay = nil

function Delay()
	local c = 1

	while true do
		coroutine.wait(1) 
		print("Count: "..c)
		c = c + 1
	end
end

function StartDelay()
	coDelay = coroutine.start(Delay)
end

function StopDelay()
	coroutine.stop(coDelay)
end

* fib函数负责计算一个斐那波契n <br>

* coroutine.start 启动一个lua协同 <br>

* coroutine.wait 协同中等待一段时间,单位:秒 <br>

* coroutine.step 协同中等待一帧. <br>

* coroutine.www 等待一个WWW完成. <br>

* tolua.tolstring 转换byte数组为lua字符串缓冲 <br>

* coroutine.stop 停止一个正在lua将要执行的协同 <br>

TestLuaCoroutine中全部围绕者全局模块coroutine中函数的使用,接下来看coroutine模块

3.2 coroutine.lua

Lua 复制代码
local create = coroutine.create
local running = coroutine.running
local resume = coroutine.resume
local yield = coroutine.yield
local error = error
local unpack = unpack
local debug = debug
local FrameTimer = FrameTimer
local CoTimer = CoTimer

local comap = {}
local pool = {}
setmetatable(comap, {__mode = "kv"})

function coroutine.start(f, ...)	
	local co = create(f)
	
	if running() == nil then
		local flag, msg = resume(co, ...)
	
		if not flag then					
			error(debug.traceback(co, msg))
		end					
	else
		local args = {...}
		local timer = nil		
		
		local action = function()												
			comap[co] = nil
			timer.func = nil
			local flag, msg = resume(co, unpack(args))						
			table.insert(pool, timer)
	
			if not flag then	
				timer:Stop()														
				error(debug.traceback(co, msg))						
			end		
		end
			
		if #pool > 0 then
			timer = table.remove(pool)
			timer:Reset(action, 0, 1)
		else
			timer = FrameTimer.New(action, 0, 1)
		end
		
		comap[co] = timer
		timer:Start()		
	end

	return co
end

function coroutine.wait(t, co, ...)
	local args = {...}
	co = co or running()		
	local timer = nil
		
	local action = function()		
		comap[co] = nil		
		timer.func = nil
		local flag, msg = resume(co, unpack(args))
		
		if not flag then	
			timer:Stop()						
			error(debug.traceback(co, msg))			
			return
		end
	end
	
	timer = CoTimer.New(action, t, 1)
	comap[co] = timer	
	timer:Start()
	return yield()
end

function coroutine.step(t, co, ...)
	local args = {...}
	co = co or running()		
	local timer = nil
	
	local action = function()	
		comap[co] = nil					
		timer.func = nil
		local flag, msg = resume(co, unpack(args))
		table.insert(pool, timer)
	
		if not flag then	
			timer:Stop()																			
			error(debug.traceback(co, msg))
			return	
		end		
	end
				
	if #pool > 0 then
		timer = table.remove(pool)
		timer:Reset(action, t or 1, 1)
	else
		timer = FrameTimer.New(action, t or 1, 1)
	end

	comap[co] = timer
	timer:Start()
	return yield()
end

function coroutine.www(www, co)			
	co = co or running()			
	local timer = nil			
			
	local action = function()				
		if not www.isDone then		
			return		
		end		
				
		comap[co] = nil
		timer:Stop()		
		timer.func = nil
		local flag, msg = resume(co)			
		table.insert(pool, timer)	
			
		if not flag then												
			error(debug.traceback(co, msg))			
			return			
		end				
	end		
				
	if #pool > 0 then
		timer = table.remove(pool)
		timer:Reset(action, 1, -1)
	else	
		timer = FrameTimer.New(action, 1, -1)	
	end
	comap[co] = timer	
 	timer:Start()
 	return yield()
end

function coroutine.stop(co)
 	local timer = comap[co] 	 	

 	if timer ~= nil then
 		comap[co] = nil
 		timer:Stop()  		
 	end
end

哪里导入了coroutine模块呢,全局搜索coroutine"可以看到是tolua.lua的require

3.3 tolua.lua

Lua 复制代码
if jit then		
	if jit.opt then		
		jit.opt.start(3)				
	end		
	
	print("ver"..jit.version_num.." jit: ", jit.status())
	print(string.format("os: %s, arch: %s", jit.os, jit.arch))
end

if DebugServerIp then  
  require("mobdebug").start(DebugServerIp)
end

require "misc.functions"
Mathf		= require "UnityEngine.Mathf"
Vector3 	= require "UnityEngine.Vector3"
Quaternion	= require "UnityEngine.Quaternion"
Vector2		= require "UnityEngine.Vector2"
Vector4		= require "UnityEngine.Vector4"
Color		= require "UnityEngine.Color"
Ray			= require "UnityEngine.Ray"
Bounds		= require "UnityEngine.Bounds"
RaycastHit	= require "UnityEngine.RaycastHit"
Touch		= require "UnityEngine.Touch"
LayerMask	= require "UnityEngine.LayerMask"
Plane		= require "UnityEngine.Plane"
Time		= reimport "UnityEngine.Time"

list		= require "list"
utf8		= require "misc.utf8"

require "event"
require "typeof"
require "slot"
require "System.Timer"
require "System.coroutine"
require "System.ValueType"
require "System.Reflection.BindingFlags"

可以看到tolua.lua对同级目录包括子目录(LuaFramework\ToLua\Lua)下多个lua文件进行require

3.4 tolua库的导入

通过搜索tolua.lua可以看到其执行来自在LuaState

Tip:lua文件的执行如果搜索名称没搜到需要加.lua后缀名一起搜索

LuaState.cs:

cs 复制代码
        void OpenBaseLuaLibs()
        {
            DoFile("tolua.lua");            //tolua table名字已经存在了,不能用require
            LuaUnityLibs.OpenLuaLibs(L);
        }

四.小结

至此,该携程example所涉及代码及其先后执行逻辑有了整体的把握和理解,具体coroutine的实现先没有深究的必要,等用到时再回来看即可

相关推荐
真鬼12317 小时前
【Unity WebGL】内嵌网页与双向通信
unity·游戏引擎·webgl
zyh______1 天前
C#语法糖(按照实用性排序)
unity·c#
avi91111 天前
[AI教做人]CinemachineCamera 各项调整配置参数入门 + Profiler
unity·游戏开发·camera·cinemachine·visual camera·镜头配置
郝学胜-神的一滴2 天前
[简化版 GAMES 101] 计算机图形学 16:纹理走样、Mipmap、三线性插值、各向异性过滤原理全解
unity·游戏引擎·godot·图形渲染·three.js·opengl·unreal
吴梓穆3 天前
untiy TextMeshPro (TMP) text 操作 中文字符集 字体材质操作(添加纹理 添加描边 添加阴影)
unity
想你依然心痛3 天前
嵌入式单元测试:Unity/CMock框架与硬件在环测试——测试驱动、桩函数
unity·单元测试·游戏引擎
xcLeigh3 天前
Unity基础:Game视图详解——游戏预览、分辨率模拟与性能显示
游戏·unity·游戏引擎·音频·视频·game·play模式
xcLeigh4 天前
Unity基础:Scene视图操作完全指南——视角控制、物体选择与场景导航
unity·游戏引擎·scene·试图·场景导航
mxwin4 天前
Unity Shader exp 函数的算法与渲染应用
算法·unity·游戏引擎·shader
mxwin4 天前
Unity URP Exposure曝光原理与实战应用
unity·游戏引擎