最近在实习中,遇到了这样的问题,在与不同碰撞体碰撞时需要触发不同的事件,比如人物播放一段动画、播放一段语音,或者是切换摄像机,显示某个UI。这样看上去其实也不太难。但如果需要去关联的事件很多,就需要在类中去获取各种各样的对象,非常麻烦而且也会使程序耦合性变高,这时就需要使用到观察者模式。
观察者模式其本质就是通过委托将各个类中的方法都关联到一个固定的类上去,由该管理类去统一决定是否调用。由于很像是管理类去观察着各个类的情况,所以我们管这种设计模式叫做观察者模式,下面是观察者模式的简单实现。
事件中心类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class Event_: Singleton<Event_>
{
private static Dictionary<string, UnityAction<object>> eventDic = new Dictionary<string, UnityAction<object>>();
/// <summary>
/// 添加事件监听
/// </summary>
/// <param name="name"></param>
/// <param name="action"></param>
public static void AddEventListener1(string name, UnityAction<object> action)
{
if (eventDic.ContainsKey(name))
{
eventDic[name] += action;
}
else
{
eventDic.Add(name, action);
}
}
/// <summary>
/// 事件触发
/// </summary>
/// <param name="name"></param>
/// <param name="info"></param>
public static void EventTrigger(string name, object info)
{
if (eventDic.ContainsKey(name))
{
eventDic[name].Invoke(info);
}
}
/// <summary>
/// 移除事件
/// </summary>
/// <param name="name"></param>
/// <param name="action"></param>
public static void RemoveEventListener(string name,UnityAction<object> action)
{
if (eventDic.ContainsKey(name))
{
eventDic[name] -= action;
}
Debug.Log("RemoveEventListener");
}
}
事件中心里,首先是有一个eventDict事件容器 Dictionary<string,UnityAction>,用于存放各类事件,字典的键是事件的名字、值该事件委托。然后为了能够使在挂载事件的时候也能进行参数的传递,使用了泛型委托。然后中心对外提供三个方法,添加事件 AddEventListener、移除事件RemoveEventListener、事件触发器EventTrigge。整个程序流程基本是,发生了某件事情,然后事件中心告知各个类对这个事件的响应。所以需要让各个类与事件中心关联起来。
比如 在start周期中 挂件监听事件。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class chuanzhang : MonoBehaviour
{
public GameObject CzGo;
public Animation animationNAN;
// Start is called before the first frame update
void Start()
{
//try
//{
Event_.AddEventListener1("colliderevent", chuanzhangAni);
Debug.Log("监听已挂载");
// }
//catch(Exception e)
//{
// Debug.Log(e);
//}
}
private void chuanzhangAni(object info)
{
Debug.Log("船长触发动画 ——说话");
}
}
在碰撞函数中,设置事件触发
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ColliderEvent : MonoBehaviour
{
public int colliderevent = 1;
private void OnTriggerEnter()
{
Event_.EventTrigger("colliderevent", colliderevent);
Debug.Log("触发 船长动画 ");
}
}
在播放动画函数中 可以根据参数种类触发不同动画
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class chuanplayer : MonoBehaviour
{
private void Start()
{
Event_.AddEventListener1("colliderevent", player_ani);
}
private void player_ani(object info)
{
if (ColliderEvent.colliderevent == 1)
{
Debug.Log("播放动画");
}
}
private void OnDestroy()
{
Event_.RemoveEventListener("colliderevent", player_ani);
}
}
字典的好处
使用字典可以方便地将事件和其对应的委托存储在一个地方。当需要触发某个事件时,可以通过键值对的方式查找到对应的委托,并调用它来执行事件处理程序。同时,字典的查找操作非常快,因此可以高效地进行事件处理。
事件中心根据字典中的委托调用各个应该响应类中的函数。并且将ColliderEvent.colliderevent作为参数传递到各个函数中,让响应类能够针对type不同做出判断
请大家批评指正