I'm currently writing a game and want to encode everything that happens (GameObject has moved, hp changed, was created/destroyed, got/lost a component, ...) in Events that look like this:
class HpChangedEvent : IEvent
{
public int GameObjectId;
public int NewHp;
}
I hope that will make it easier to network the game since every change is encoded as a tuple of value types. Multi-threading is not needed for this code. The event listener can react on these events like this:
class HumanView
{
private int controlledGameObject;
public void Update()
{
if(Input.ForwardPressed)
{
Game.EventModule.QueueEvent(new ForceEvent(Vector3.Forward, controlledGameObject));
}
}
}
class PhysicModule
{
public PhysicModule()
{
Game.EventModule.AddDelegate(OnForceEvent);
}
void OnForceEvent(ForceEvent e)
{
var gameObject = Game.GetGameObjectFromId(e.GameObjectId);
gameObject.force += e.Value;
}
}
IEvent.cs
public interface IEvent
{
}
EventDelegate.cs
public delegate void EventDelegate<T>(T e) where T : IEvent;
EventModule.cs
public class EventModule
{
private class EventDelegateNode
{
public object EventDelegate { get; private set; }
public EventDelegate<IEvent> Callable { get; private set; }
private EventDelegateNode(object eventDelegate, EventDelegate<IEvent> callable)
{
EventDelegate = eventDelegate;
Callable = callable;
}
public static EventDelegateNode Create<T>(EventDelegate<T> eventDelegate) where T : IEvent
{
EventDelegate<IEvent> callable = delegate(IEvent e)
{
eventDelegate((T)e);
};
return new EventDelegateNode(eventDelegate, callable);
}
}
private Dictionary<Type, List<EventDelegateNode>> _EventDelegates = new Dictionary<Type, List<EventDelegateNode>>();
private Queue<IEvent> _EventQueue = new Queue<IEvent>();
public void AddDelegate<T>(EventDelegate<T> eventDelegate) where T : IEvent
{
if(eventDelegate == null)
{
throw new ArgumentNullException("eventDelegate");
}
var eventType = typeof(T);
var eventDelegatesList = default(List<EventDelegateNode>);
if (!_EventDelegates.TryGetValue(eventType, out eventDelegatesList))
{
eventDelegatesList = new List<EventDelegateNode>();
_EventDelegates.Add(eventType, eventDelegatesList);
}
if (!Contains(eventDelegatesList, eventDelegate))
{
eventDelegatesList.Add(EventDelegateNode.Create(eventDelegate));
}
}
public void RemoveDelegate<T>(EventDelegate<T> eventDelegate) where T : IEvent
{
if (eventDelegate == null)
{
throw new ArgumentNullException("eventDelegate");
}
var eventType = typeof(T);
var eventDelegatesList = default(List<EventDelegateNode>);
if (_EventDelegates.TryGetValue(eventType, out eventDelegatesList))
{
eventDelegatesList.RemoveAll(e => Object.ReferenceEquals(e.EventDelegate, eventDelegate));
if (!eventDelegatesList.Any())
{
_EventDelegates.Remove(eventType);
}
}
}
public void FireEvent(IEvent e)
{
if (e == null)
{
throw new ArgumentNullException("e");
}
var eventDelegatesList = default(List<EventDelegateNode>);
if (_EventDelegates.TryGetValue(e.GetType(), out eventDelegatesList))
{
// use ToArray so the list can be edited while looping
foreach (var eventListener in eventDelegatesList.ToArray())
{
eventListener.Callable(e);
}
}
// typeof(Event) is the wildcard listener
if (_EventDelegates.TryGetValue(typeof(IEvent), out eventDelegatesList))
{
// use ToArray so the list can be edited while looping
foreach (var eventListener in eventDelegatesList.ToArray())
{
eventListener.Callable(e);
}
}
}
public void QueueEvent(IEvent e)
{
if (e == null)
{
throw new ArgumentNullException("e");
}
_EventQueue.Enqueue(e);
}
public void Update()
{
// use ToArray so the list can be edited while looping
foreach (var e in _EventQueue.ToArray())
{
// remove event from real queue
_EventQueue.Dequeue();
FireEvent(e);
}
}
private static bool Contains<T>(List<EventDelegateNode> eventDelegatesList, EventDelegate<T> eventDelegate) where T : IEvent
{
foreach (var item in eventDelegatesList)
{
if (Object.ReferenceEquals(item.EventDelegate, eventDelegate))
{
return true;
}
}
return false;
}
}
event
,EventHandler<T>
andEventArgs
? \$\endgroup\$ – Mathieu Guindon Oct 17 '15 at 19:16public event EventHandler<HpChangedEvent> OnHpChange;
would live and correspondingpublic void FireEvent(HpChangedEvent e) {...}
otherwise every listener needs to know every sender which would introduce unnecessary coupling. Additionally I like that the event registration looks the same for every EventEventModule.AddDelegate(DoSomething);
instead ofEventModule.OnEvent1 += OnEvent1Handler;
. The only benefit with theEventHandler<>
would be thatEventArgs
can be used for more then one Event. \$\endgroup\$ – prydain Oct 17 '15 at 20:13