概述
在项目中有时候会出现需要Lua监听C#消息的情况,如进入游戏,我们将进入游戏的代码放在C#,将游戏中的界面代码放在Lua中。选择在C#中调用lua代码的方式是非常不建议的,这样会提高代码的耦合性。那么我们能想到的是与之前一篇博客中提到的一样,使用事件消息来进行通信。
消息基类
关于消息我们有很多种,比如从Lua到C#的,C#之间的,Lua与Lua的。那么我们就需要一个基类NotifyBase来进行消息的处理。消息类型的定义会比较麻烦。
消息中我们有监听时传值,触发时传值。
多个监听中会使用同一个触发值,但他们的监听值无关联
以下两个监听:
__EventMessage:AddNotify("StartGame", RefshAge,{age = 100})
__EventMessage:AddNotify("StartGame", RefshName,{name = "Sun"})
触发:
LogicalMessageNotify.Instance.Notify(ELogicalMessageType.StartGame, person);
RefshAge能收到的参数为RefshAge(person,age)
RefshName能收到的参数为RefshName(person,name)
此时我们如果从Lua向C#请求监听应该如何存储消息名,方法,参数?
protected List<string> m_ltNotifyNames = null; //消息名列表
protected List<List<object>> m_ltNotifyParams = null; //广播参数列表
protected List<List<LogicalMessageNotifyCallback>> m_NotifyFuncs = null; //广播回调函数列表
//触发参数不需要列表,在触发的时候直接统一给值就可以了
以上三个列表是如何关联的?
首先我们将Lua传过来的消息获取到消息名,再将消息名与m_ltNotifyNames中的值对比,有就直接获取索引,没有就新建一个塞入后获取索引。
然后我们根据索引从m_ltNotifyParams 中找到该事件的广播参数列表,之前有说到,一个广播消息会对应多个监听,所以监听的参数会有多个,那么此时应该如何将回调参数与回调函数对应?
当添加监听的时候会在该消息的参数列表中加入一个参数,在该消息的回调函数列表中加入一个函数。两者就会属于同一个位置。即能保证参数不错位。
添加监听
添加监听
/// <summary>
/// 添加监听
/// </summary>
/// <param name="funcType"></param>
/// <param name="func"></param>
/// <param name="value"></param>
public void AddNotify(string funcType, LogicalMessageNotifyCallback func, object value = null)
{
if (func == null || string.IsNullOrEmpty(funcType) == true) return;
if (m_ltNotifyNames == null)
{
m_ltNotifyNames = new List<string>();
m_ltNotifyParams = new List<List<object>>();
m_NotifyFuncs = new List<List<LogicalMessageNotifyCallback>>();
}
int index = GlobalConfig.BinaryFind(m_ltNotifyNames, funcType); //二分查找该事件是否存在于m_ltNotifyNames中
if (index < 0)
{
index = GlobalConfig.BinaryInsert(m_ltNotifyNames, funcType); //把方法名插入到m_ltNotifyNames数组中
m_ltNotifyParams.Insert(index, new List<object>()); //每个消息都有参数,参数列表对应的索引与事件名关联
m_NotifyFuncs.Insert(index, new List<LogicalMessageNotifyCallback>()); //消息回调方法,与事件名关联
}
List<LogicalMessageNotifyCallback> list = m_NotifyFuncs[index];
if (list.Contains(func) == false) //列表中不存在本次添加的事件
{
list.Add(func);
m_ltNotifyParams[index].Add(value);
}
else
{
LogManager.LogError("重复注册事件");
}
}
移除监听
/// <summary>
/// 移除监听
/// </summary>
/// <param name="funcType"></param>
/// <param name="func"></param>
public void RemoveNotify(string funcType, LogicalMessageNotifyCallback func)
{
if (func == null) return;
if (m_ltNotifyNames == null) return;
int index = GlobalConfig.BinaryFind(m_ltNotifyNames, funcType);
if (index < 0) return;
List<LogicalMessageNotifyCallback> list = m_NotifyFuncs[index];
if (list.Count != 0)
{
int i = list.IndexOf(func);
if (i >= 0)
{
list.RemoveAt(i);
m_ltNotifyParams[index].RemoveAt(i);
}
}
}
触发消息
/// <summary>
/// 广播一条消息
/// </summary>
/// <param name="funcType"></param>
/// <param name="value"></param>
public void Notify(string funcType, object value = null)
{
if (m_ltNotifyNames == null) return;
int index = GlobalConfig.BinaryFind(m_ltNotifyNames, funcType);
if (index < 0) return;
List<LogicalMessageNotifyCallback> list = m_NotifyFuncs[index];
if (list.Count > 0)
{
List<object> objList = m_ltNotifyParams[index];
for (int i = list.Count - 1; i >= 0; i--)
{
list[i](value, objList[i]);
}
}
}
然而一些并没有结束!
我们上面做到了消息的监听移除和触发,接下来我们则需要开始写Lua监听事件部分的代码
Lua与C#都需要有消息码来限制传递的是什么消息,这样才能够在项目越来越大的时候使项目看起来不会杂乱。
/// <summary>
/// 消息枚举
/// </summary>
public enum ELogicalMessageType
{
SYSTEM = 0,
StartGame,
Count,
}
在Lua中也做同样的代码进行监听
CSLogicalMessageTypes =
{
StartGame = "StartGame",
}
在监听类初始化的时候将消息枚举存储到m_list中,不在该枚举中的消息不会进行监听。m_notify 则为上面代码的实力类,用于做真的消息处理,而此类作为消息的筛选。筛选过程根据项目需求自行添加。
protected List<string> m_list = new List<string>(); //存储监听消息名
protected NotifyBase m_notify = new NotifyBase();
public LogicalMessageNotify()
{
for (int i = 0; i < (int)ELogicalMessageType.Count; ++i)
{
m_list.Add(Enum.GetName(typeof(ELogicalMessageType), i)); //获取到ELogicalMessageType没居中的第i个名字
}
}