在第三篇的流程代码中,多次出现了 GameEntry.Event.XXX的影子, 所以,今天就学习Event组件。
事件 (Event) – 游戏逻辑监听、抛出事件的机制。Game Framework 中的很多模块在完成操作后都会抛出内置事件,监听这些事件将大大解除游戏逻辑之间的耦合。用户也可以定义自己的游戏逻辑事件。
大白话说事件:
举个栗子, 有几只小猫和一个饲养员
每个小猫都有几个方法: 预约(Subscribe) 取消预约(UnSubscribe) DoSomething (Handler) DoOtherSomthing(Handler)
饲养员有一个小本本(集合), 可以在小本本里面添加信息(Add)或者擦除信息(Remove), 饲养员可以发送通知 Fire
------------------------------------------------
小猫A比较贪吃,跟饲养员预约说, 12点了及时通知我,我要吃小鱼干, 饲养员把小猫A的预约信息添加进了小本本;
小猫B比较贪玩,跟饲养员说预约说,12点了及时通知我,我要出去逛,饲养员把小猫B的预约信息添加进了小本本;
小猫C喜欢交友,跟饲养员预约说,12点了及时通知我,我要出去找朋友,饲养员把小猫C的预约信息添加进了小本本;
其他小猫都没有和饲养员预约任何事情;
过了一段时间,
小猫D找小猫B说, 我们一起去滑冰吧,小猫B痛快的同意了,跟饲养员说,我取消12点的预约了,饲养员擦除了(Remove)小猫B的预约;
时间过得很快,一转眼就到12点了
饲养员翻开小本本,看到小本本上还记录着小猫A和小猫C的预约信息, 逐一通知(Fire)小猫A和小猫C去干各自的事情
观察者模式:设计模式——观察者模式_做哈白日梦的博客-CSDN博客_设计模式观察者模式
---------------------------------------------------------------------------------
下面的脚本是ProcedureMenu.cs中的代码
//ProcedureMenu.cs
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
//本篇讨论这行代码
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);
m_StartGame = false;
//下一篇文章讨论这行代码
GameEntry.UI.OpenUIForm(UIFormId.MenuForm, this);
}
//事件处理函数
private void OnOpenUIFormSuccess(object sender, GameEventArgs e)
{
OpenUIFormSuccessEventArgs ne = (OpenUIFormSuccessEventArgs)e;
if (ne.UserData != this)
{
return;
}
m_MenuForm = (MenuForm)ne.UIForm.Logic;
}
注意看代码中的注释
追踪Game.Event.Subscribe函数,定位到 EventComponent.cs
public void Subscribe(int id, EventHandler handler)
{
//这里是添加到集合的操作
m_EventManager.Subscribe(id, handler);
}
继续追到 EventManager.cs
public void Subscribe(int id, EventHandler handler)
{
m_EventPool.Subscribe(id, handler);
}
继续追到 EventPool.cs, 最后把 id,handle 添加到 m_EventHandle字典中
m_EventHandlers.Add(id, handler);
public void Subscribe(int id, EventHandler handler)
{
if (handler == null)
{
throw new GameFrameworkException("Event handler is invalid.");
}
if (!m_EventHandlers.Contains(id))
{
//添加到集合
m_EventHandlers.Add(id, handler);
}
else if ((m_EventPoolMode & EventPoolMode.AllowMultiHandler) != EventPoolMode.AllowMultiHandler)
{
throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not allow multi handler.", id));
}
else if ((m_EventPoolMode & EventPoolMode.AllowDuplicateHandler) != EventPoolMode.AllowDuplicateHandler && Check(id, handler))
{
throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not allow duplicate handler.", id));
}
else
{
m_EventHandlers.Add(id, handler);
}
}
注册的时候把事件id和handler加入到了m_EventHandlers中
事件注册了, 那什么时候触发事件执行呢?
查找所有引用:发现在UIComponent组件中,Fire了事件
//UIComponent.cs
protected override void Awake()
{
base.Awake();
m_UIManager = GameFrameworkEntry.GetModule();
if (m_UIManager == null)
{
Log.Fatal("UI manager is invalid.");
return;
}
if (m_EnableOpenUIFormSuccessEvent)
{
m_UIManager.OpenUIFormSuccess += OnOpenUIFormSuccess;
}
m_UIManager.OpenUIFormFailure += OnOpenUIFormFailure;
if (m_EnableOpenUIFormUpdateEvent)
{
m_UIManager.OpenUIFormUpdate += OnOpenUIFormUpdate;
}
if (m_EnableOpenUIFormDependencyAssetEvent)
{
m_UIManager.OpenUIFormDependencyAsset += OnOpenUIFormDependencyAsset;
}
if (m_EnableCloseUIFormCompleteEvent)
{
m_UIManager.CloseUIFormComplete += OnCloseUIFormComplete;
}
}
private void OnOpenUIFormSuccess(object sender, GameFramework.UI.OpenUIFormSuccessEventArgs e)
{
//触发事件
m_EventComponent.Fire(this, OpenUIFormSuccessEventArgs.Create(e));
}
继续追:
//EventComponent.cs
public void Fire(object sender, GameEventArgs e)
{
m_EventManager.Fire(sender, e);
}
//EventManager.cs
public void Fire(object sender, GameEventArgs e)
{
m_EventPool.Fire(sender, e);
}
//EventPool.cs
public void Fire(object sender, T e)
{
if (e == null)
{
throw new GameFrameworkException("Event is invalid.");
}
Event eventNode = Event.Create(sender, e);
lock (m_Events)
{
//这里如队列
m_Events.Enqueue(eventNode);
}
}
public void Update(float elapseSeconds, float realElapseSeconds)
{
lock (m_Events)
{
while (m_Events.Count > 0)
{
Event eventNode = m_Events.Dequeue();
HandleEvent(eventNode.Sender, eventNode.EventArgs);
ReferencePool.Release(eventNode);
}
}
}
private void HandleEvent(object sender, T e)
{
bool noHandlerException = false;
GameFrameworkLinkedListRange> range = default(GameFrameworkLinkedListRange>);
//从集合中取出handler执行
if (m_EventHandlers.TryGetValue(e.Id, out range))
{
LinkedListNode> current = range.First;
while (current != null && current != range.Terminal)
{
m_CachedNodes[e] = current.Next != range.Terminal ? current.Next : null;
current.Value(sender, e);
current = m_CachedNodes[e];
}
m_CachedNodes.Remove(e);
}
else if (m_DefaultHandler != null)
{
m_DefaultHandler(sender, e);
}
else if ((m_EventPoolMode & EventPoolMode.AllowNoHandler) == 0)
{
noHandlerException = true;
}
ReferencePool.Release(e);
if (noHandlerException)
{
throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not allow no handler.", e.Id));
}
}
触发的时候从m_EventHandlers中取出之前添加的handler,调用执行
private void OnOpenUIFormSuccess(object sender, GameEventArgs e)
{
OpenUIFormSuccessEventArgs ne = (OpenUIFormSuccessEventArgs)e;
if (ne.UserData != this)
{
return;
}
m_MenuForm = (MenuForm)ne.UIForm.Logic;
}
在GF框架中的事件使用和OnOpenUIFormSuccess基本都差不多。
举个栗子:假设我们现在有n多个关卡,玩家可以在任意关卡之间切换,每一个关卡我们可以定义一个流程,比如 ProcedureLevelA, ProcedureLevelB, ProcedureLevelC...
可以给关卡的切换定义一个事件:
//必须继承自 GameEventArgs
public class ChangeProcedureEventArgs : GameEventArgs
{
public static readonly int EventId = typeof(ChangeProcedureEventArgs).GetHashCode();
//定义我们自己的参数
//这是我们要跳转到下一个关卡的参数
public SceneId sceneId { get; private set; }
public override int Id
{
get
{
return EventId;
}
}
public override void Clear()
{
sceneId = SceneId.None;
}
//创建事件,给自定义参数赋值
public static ChangeProcedureEventArgs Create(SceneId id)
{
ChangeProcedureEventArgs e = ReferencePool.Acquire();
e.sceneId = id;
return e;
}
在流程的OnEnter中注册事件,OnLeave中取消事件
public class ProcedureStart : ProcedureBase
{
private ProcedureOwner m_ProdureOwner;
public override bool UseNativeDialog
{
get
{
return false;
}
}
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
m_ProdureOwner = procedureOwner;
Log.Info("进入ProcedureStart================");
//注册事件
GameEntry.Event.Subscribe(ChangeProcedureEventArgs.EventId, RspChangeScene);
GameEntry.UI.OpenUIForm(UIFormId.SplashForm, this);
}
protected override void OnLeave(ProcedureOwner procedureOwner, bool isShutdown)
{
base.OnLeave(procedureOwner, isShutdown);
//取消事件
GameEntry.Event.Unsubscribe(ChangeProcedureEventArgs.EventId, RspChangeScene);
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
}
//事件处理函数
protected void RspChangeScene(object sender,GameEventArgs e)
{
if (m_ProdureOwner == null)
{
return;
}
ChangeProcedureEventArgs ne = e as ChangeProcedureEventArgs;
if (ne.sceneId == SceneId.None)
{
return;
}
//从事件参数中获取关卡ID,调用切换关卡的流程
m_ProdureOwner.SetData(Constant.ProcedureData.NextSceneId, (int)ne.sceneId);
ChangeState(m_ProdureOwner);
}
}
无论在任何地方,只要调用下面这行代码,传入要跳转的关卡名,就可以跳转关卡了
GameEntry.Event.Fire(this, ChangeProcedureEventArgs.Create(SceneId.MainMenu));