讲解xLua例7 AsyncTest,用顺序写法代替传回调写法
一.例7 AsyncTest代码分析
先说结论,这个demo目的是用顺序写法代替传回调写法,实现上是用Lua的协程API,控制函数执行,中断
1.1 AsyncTest.cs
执行async_test.lua的入口文件
using UnityEngine;
using XLua;
using System.Collections.Generic;
using System;
namespace XLuaTest
{
public class AsyncTest : MonoBehaviour
{
LuaEnv luaenv = null;
void Start()
{
luaenv = new LuaEnv();
luaenv.DoString("require 'async_test'");
}
// Update is called once per frame
void Update()
{
if (luaenv != null)
{
luaenv.Tick();
}
}
}
}
1.2 async_test.lua
业务代码,在buy函数中实现了将给message_box传回调的做法写成了顺序执行的写法,这种写法必须吃透掌握,在xLua项目实战中可能大量使用,需要扫清读写障碍。
local util = require 'xlua.util'
local message_box = require 'message_box'
-------------------------async_recharge-----------------------------
local function async_recharge(num, cb) --模拟的异步充值
print('requst server...')
cb(true, num)
end
local recharge = util.async_to_sync(async_recharge)
-------------------------async_recharge end----------------------------
local buy = function()
message_box.alert("您余额不足,请充值!", "余额提醒")
if message_box.confirm("确认充值10元吗?", "确认框") then
local r1, r2 = recharge(10)
print('recharge result:', r1, r2)
message_box.alert("充值成功!", "提示")
else
print('cancel')
message_box.alert("取消充值!", "提示")
end
print('recharge finished')
end
--将按钮监听点击事件,绑定buy方法
CS.UnityEngine.GameObject.Find("Button"):GetComponent("Button").onClick:AddListener(util.coroutine_call(buy))
1.3 message_box.lua
本质上就是调用util的async_to_sync方法
local util = require 'xlua.util'
local sync_alert = util.async_to_sync(CS.XLuaTest.MessageBox.ShowAlertBox)
local sync_confirm = util.async_to_sync(CS.XLuaTest.MessageBox.ShowConfirmBox)
--构造alert和confirm函数
return {
alert = function(message, title)
sync_alert(message, title)
end;
confirm = function(message, title)
local ret = sync_confirm(message, title)
return ret == true
end;
}
1.4 util.lua
函数coroutine_call:创建一个协程并运行(coroutine.create创建的协程初始状态为suspend)
函数async_to_sync:这个函数是理解串行写法替代传回调的关键:
1.每一次在业务区调用async_to_sync时,async_func执行一次(C#的ShowAlertBox,用来显示对话框),执行coroutine.yield(),业务区代码会中断:状态从running变成suspend
2.调用async_func时,cb_func会被传到C#侧的onFinished委托,点击button后cb_func会执行
3.cb_func执行后,业务区代码会继续执行直到下一次执行async_to_sync
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
1.5 MessageBox.cs
主要是UI上的实现
using UnityEngine;
using UnityEngine.UI;
using XLua;
using System.Collections.Generic;
using System;
using UnityEngine.Events;
namespace XLuaTest
{
public class MessageBox : MonoBehaviour
{
public static void ShowAlertBox(string message, string title, Action onFinished = null)
{
var alertPanel = GameObject.Find("Canvas").transform.Find("AlertBox");
if (alertPanel == null)
{
alertPanel = (Instantiate(Resources.Load("AlertBox")) as GameObject).transform;
alertPanel.gameObject.name = "AlertBox";
alertPanel.SetParent(GameObject.Find("Canvas").transform);
alertPanel.localPosition = new Vector3(-6f, -6f, 0f);
}
alertPanel.Find("title").GetComponent<Text>().text = title;
alertPanel.Find("message").GetComponent<Text>().text = message;
var button = alertPanel.Find("alertBtn").GetComponent<Button>();
UnityAction onclick = () =>
{
if (onFinished != null)
{
onFinished();
}
button.onClick.RemoveAllListeners();
alertPanel.gameObject.SetActive(false);
};
//防止消息框未关闭时多次被调用
button.onClick.RemoveAllListeners();
button.onClick.AddListener(onclick);
alertPanel.gameObject.SetActive(true);
}
public static void ShowConfirmBox(string message, string title, Action<bool> onFinished = null)
{
var confirmPanel = GameObject.Find("Canvas").transform.Find("ConfirmBox");
if (confirmPanel == null)
{
confirmPanel = (Instantiate(Resources.Load("ConfirmBox")) as GameObject).transform;
confirmPanel.gameObject.name = "ConfirmBox";
confirmPanel.SetParent(GameObject.Find("Canvas").transform);
confirmPanel.localPosition = new Vector3(-8f, -18f, 0f);
}
confirmPanel.Find("confirmTitle").GetComponent<Text>().text = title;
confirmPanel.Find("conmessage").GetComponent<Text>().text = message;
var confirmBtn = confirmPanel.Find("confirmBtn").GetComponent<Button>();
var cancelBtn = confirmPanel.Find("cancelBtn").GetComponent<Button>();
Action cleanup = () =>
{
confirmBtn.onClick.RemoveAllListeners();
cancelBtn.onClick.RemoveAllListeners();
confirmPanel.gameObject.SetActive(false);
};
UnityAction onconfirm = () =>
{
if (onFinished != null)
{
onFinished(true);
}
cleanup();
};
UnityAction oncancel = () =>
{
if (onFinished != null)
{
onFinished(false);
}
cleanup();
};
//防止消息框未关闭时多次被调用
confirmBtn.onClick.RemoveAllListeners();
confirmBtn.onClick.AddListener(onconfirm);
cancelBtn.onClick.RemoveAllListeners();
cancelBtn.onClick.AddListener(oncancel);
confirmPanel.gameObject.SetActive(true);
}
}
public static class MessageBoxConfig
{
[CSharpCallLua]
public static List<Type> CSharpCallLua = new List<Type>()
{
typeof(Action),
typeof(Action<bool>),
typeof(UnityAction),
};
}
}
二.例7 AsyncTest运行展示
运行效果:

三.例7 AsyncTest总结
1.顺序写法代替传回调写法这种做法要能理解,会使用
2.util中的async_recharge和coroutine_call在商业xLua项目中可能大量使用,可以在项目中搜索出现数量


被折叠的 条评论
为什么被折叠?



