Unity 自定义事件
EventId.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Assets.Scripts.Event
{
//事件ID
public enum EventId
{
event1,
event2,
Count,//必须写在最后 用于表示所有Event的个数以及存储时间数组长度
}
}
EventEatResponse.cs
using UnityEngine;
using System.Collections;
namespace Assets.Scripts.Event
{
public enum EventEatResponse
{
/// 继续向下一个已经注册该事件的游戏体传递当前事件
NotEaten,
/// 停止向下一个已经注册该事件的游戏体传递当前事件
Eaten,
}
}
EventObservers.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Assets.Scripts.DataStructures;
using Assets.Scripts.Core;
namespace Assets.Scripts.Event
{
/// <summary>
/// 事件监听列表
/// </summary>
public class EventObservers
{
public PriorityList<IEventObserver> List { get; private set; }
public MutableIterator Iter { get; private set; }
public EventObservers()
{
List = new PriorityList<IEventObserver>();
Iter = new MutableIterator();
}
}
}
EventPriority.cs
using UnityEngine;
using System.Collections;
namespace Assets.Scripts.Event
{
/// <summary>
/// 事件优先级
/// </summar>
public enum EventPriority
{
AfterDefault,
Default,
BeforeDefault,
Notification,
}
}
IEventObserver.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Assets.Scripts.Event
{
/// <summary>
/// 事件接口 所有要注册事件的类都要实现
/// </summary>
///
public interface IEventObserver
{
// 接到事件后会调用该接口
EventEatResponse OnEvent(EventId id, object cookie);
}
}
EventManager.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Assets.Scripts.Core;
using Assets.Scripts.DataStructures;
namespace Assets.Scripts.Event
{
/// <summary>
/// 事件管理类
/// </summary>
public class EventManager
{
//已经注册事件的游戏体列表 根据事件优先级分组
private EventObservers[] eventIdToObservers;
public EventManager()
{
Service.Set<EventManager>(this);
int count = (int)EventId.Count;
eventIdToObservers = new EventObservers[count];
// Fill with nulls until someone decides to listen for a given event.
// .Net does this for us. Just being explicit.
for (int i = 0; i < count; i++)
{
eventIdToObservers[i] = null;
}
}
// 注册默认优先级别的事件
public void RegisterObserver(IEventObserver observer, EventId id)
{
RegisterObserver(observer, id, EventPriority.Default);
}
// 根据优先级来注册事件
public void RegisterObserver(IEventObserver observer, EventId id, EventPriority priority)
{
if (observer == null)
{
return;
}
int index = (int)id;
EventObservers observers = eventIdToObservers[index];
if (observers == null)
{
observers = new EventObservers();
eventIdToObservers[index] = observers;
}
PriorityList<IEventObserver> list = observers.List;
if (list.IndexOf(observer) < 0)
{
list.Add(observer, (int)priority);
}
}
// 根据id接触已经注册的事件
public void UnregisterObserver(IEventObserver observer, EventId id)
{
int index = (int)id;
EventObservers observers = eventIdToObservers[index];
if (observers != null)
{
PriorityList<IEventObserver> list = observers.List;
MutableIterator miter = observers.Iter;
int i = list.IndexOf(observer);
if (i >= 0)
{
list.RemoveAt(i);
miter.OnRemove(i);
if (list.Count == 0)
{
eventIdToObservers[index] = null;
}
}
}
}
// 根据ID发送事件
public void SendEvent(EventId id, object cookie)
{
int index = (int)id;
EventObservers observers = eventIdToObservers[index];
if (observers != null)
{
PriorityList<IEventObserver> list = observers.List;
MutableIterator miter = observers.Iter;
for (miter.Init(list.Count); miter.Active(); miter.Next())
{
IEventObserver observer = list.GetElement(miter.Index);
if (observer.OnEvent(id, cookie) == EventEatResponse.Eaten)
{
break;
}
}
miter.Reset();
}
}
}
}
ElementPriorityPair.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Assets.Scripts.Event
{
public class ElementPriorityPair<T>
{
public T Element { get; set; }
public int Priority { get; set; }
public ElementPriorityPair(T element, int priority)
{
Element = element;
Priority = priority;
}
}
}
MutableIterator.cs
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
namespace Assets.Scripts.Event
{
public class MutableIterator
{
int index;
int count;
public MutableIterator()
{
Reset();
}
public void Reset()
{
index = 0;
count = 0;
}
public void Init(int count)
{
index = 0;
this.count = count;
}
public void Init(ICollection list)
{
index = 0;
this.count = list.Count;
}
public bool Active()
{
return index < count;
}
public void Next()
{
index++;
}
public int Index
{
get { return index; }
set
{
// Only allow manual index setting if we haven't yet started iterating.
if (index == 0)
{
index = value;
}
}
}
public int Count
{
get { return count; }
}
public void OnRemove(int i)
{
if (count > 0)
{
count--;
if (i <= index)
{
index--;
}
}
}
}
}
PriorityList.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Assets.Scripts.Event
{
public class PriorityList<T>
{
private List<ElementPriorityPair<T>> list;
public PriorityList()
{
list = new List<ElementPriorityPair<T>>();
}
// Just like List<T>.Count.
public int Count
{
get { return list.Count; }
}
// Similar to List<T>.Add() but inserts based on priority.
// Returns the index where the element was inserted.
// Returns -1 on failure.
public virtual int Add(T element, int priority)
{
// Prevent null from being added. It's just one of the features of this class.
if (element == null)
{
return -1;
}
for (int i = 0, count = list.Count; i < count; i++)
{
ElementPriorityPair<T> pair = list[i];
if (object.ReferenceEquals(pair.Element, element))
{
// Already added.
return -1;
}
// Keep the list sorted, higher priority first.
// For equal priorities, we'll add ourselves to the end of that range.
if (priority > pair.Priority)
{
// Found insertion point.
list.Insert(i, new ElementPriorityPair<T>(element, priority));
return i;
}
}
// Didn't find an insertion point. Add new currently-lowest-priority element.
list.Add(new ElementPriorityPair<T>(element, priority));
return list.Count - 1;
}
public ElementPriorityPair<T> Get(int i)
{
return list[i];
}
// Similar to List<T>[i].
public T GetElement(int i)
{
return list[i].Element;
}
// Get the associated prioriity of the i'th element.
public int GetPriority(int i)
{
return list[i].Priority;
}
// Alternative for when you want both the element and its associated prioiryt.
public void GetElementPriority(int i, out T element, out int priority)
{
ElementPriorityPair<T> pair = list[i];
element = pair.Element;
priority = pair.Priority;
}
// Just like List<T>.IndexOf(element).
public int IndexOf(T element)
{
for (int i = 0, count = list.Count; i < count; i++)
{
if (object.ReferenceEquals(list[i].Element, element))
{
return i;
}
}
return -1;
}
// No Remove(T t), to discourage misuse. Callers must know the index to remove.
// Remove(T t) can always be implemented is "if ((i = IndexOf(t)) >= 0) RemoveAt(i);".
public void RemoveAt(int i)
{
list.RemoveAt(i);
}
}
}
Service.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Assets.Scripts.Event
{
public static class Service
{
#if SERVER
[ThreadStatic]
#endif
private static List<IServiceWrapper> serviceWrapperList;
public static void Set<T>(T instance)
{
if (ServiceWrapper<T>.instance != null)
{
throw new Exception("An instance of this service class has already been set!");
}
ServiceWrapper<T>.instance = instance;
if (serviceWrapperList == null)
{
serviceWrapperList = new List<IServiceWrapper>();
}
serviceWrapperList.Add(new ServiceWrapper<T>());
}
public static T Get<T>()
{
return ServiceWrapper<T>.instance;
}
public static bool IsSet<T>()
{
return ServiceWrapper<T>.instance != null;
}
// Resets references to all services back to null so that they can go out of scope and
// be subjected to garbage collection.
// * Services that reference each other will be garbage collected.
// * AssetBundles should be manually unloaded by an asset manager.
// * GameObjects will be destroyed by the next level load done by the caller.
// * Any application statics should be reset by the caller as well.
// * If there are any unmanaged objects, those need to be released by the caller, too.
public static void ResetAll()
{
if (serviceWrapperList == null)
{
return;
}
// Unset in the reverse order in which services were set. Probably doesn't matter.
for (int i = serviceWrapperList.Count - 1; i >= 0; i--)
{
serviceWrapperList[i].Unset();
}
serviceWrapperList = null;
}
}
internal class ServiceWrapper<T> : IServiceWrapper
{
#if SERVER
[ThreadStatic]
#endif
public static T instance = default(T);
public void Unset()
{
ServiceWrapper<T>.instance = default(T);
}
}
internal interface IServiceWrapper
{
void Unset();
}
}
具体使用:
void Start () {
Debug.Log("MyCube Start");
//注册事件
Service.Get<EventManager>().RegisterObserver(this, EventId.event1,
EventPriority.Default);
Service.Get<EventManager>().RegisterObserver(this, EventId.event2,
EventPriority.Default);
}
发送事件:
Service.Get<EventManager>().SendEvent(EventId.event1, null);
发送事件后会根据优先级调用注册当前EventId的那个游戏体, 继而执行 相应的OnEvent()
例如给myCube分别注册事件1 和 事件2 mySphere只注册事件1
(如果在OnEvent当中返回的是Eaten)则不会继续向下一个注册改事件的游戏体传递这个事件)