Alternative to enumerators in AI - c#

I'm working on a server for a multi-player game that has to control a few thousand creatures, running around in the world. Every creature has an AI with a heartbeat method that is called every few ms/s, if a player is nearby, so they can react.
Currently the AI uses enumerators as "routines", e.g.
IEnumerable WanderAround(int radius)
{
// Do something
}
which are called from "state methods", which are called in foreachs, yielding in the heartbeat so you get back to the same spot on every tick.
void OnHeartbeat()
{
// Do checks, maybe select a new state method...
// Then continue the current sequence
currentState.MoveNext();
}
Naturally the routines have to be called in a loop as well, because they wouldn't execute otherwise. But since I'm not the one writing those AIs, but newbies who aren't necessarily programmers, I'm pre-compiling the AIs (simple .cs files) before compiling them on server start. This gives me AI scripts that look like this:
override IEnumerable Idle()
{
Do(WanderAround(400));
Do(Wait(3000));
}
override IEnumerable Aggro()
{
Do(Attack());
Do(Wait(3000));
}
with Do being replaced by a foreach that iterates over the routine call.
I really like this design because the AIs are easy to understand, yet powerful. It's not simple states but it's not a hard to understand/write behavior tree either.
Now to my actual "problem", I don't like the Do wrapper, I don't like having to pre-compile my scripts. But I just can't think of any other way to implement this without the loops, that I want to hide because of verbosity and the skill level of the people who're gonna write these scripts.
foreach(var r in Attack()) yield return r;
I'd wish there'd be a way to call the routines without an explicit loop, but that's not possible because I have to yield from the state method.
And I can't use async/await because it doesn't fit the tick design that I depend on (the AIs can be quite complex and I honestly don't know how I would implement that using async). Also I'd just trade Do() against await, not that much of an improvement.
So my question is: Can anyone think of a way to get rid of that loop wrapper? I'd be open to using other .NET languages that I can use as scripts (compiling them on server start) if there's one that supports this somehow.

Every creature has an AI with a heartbeat method that is called every few ms/s,
Why not go full SkyNet and have each creature responsible for its own heartbeat?
Such as creating each creature with a timer (the heart so to speak with a specific heartbeat). When each timer beats it does what it was designed to do, but also checks with the game as to whether it needs to shut-down, be idle, wander or other items.
By decentralizing the loop, you have gotten rid of the loop and you simply have a broadcast to subscribers (the creatures) on what to do on a global/basic level. That code is not accessible to the newbies, but it is understood what it does on a conceptual level.

You could try turning to the .NET framework for help by using events in your server and having the individual AIs subscribe to them. This works if the Server is maintaining the heartbeat.
Server
The server advertises the events that the AIs can subscribe to. In the heartbeat method you would call the OnIdle and OnAggro methods to raise the Idle and Aggro events.
public class GameServer
{
// You can change the type of these if you need to pass arguments to the handlers.
public event EventHandler Idle;
public event EventHandler Aggro;
void OnIdle()
{
EventHandler RaiseIdleEvent = Idle;
if (null != RaiseIdleEvent)
{
// Change the EventArgs.Empty to an appropriate value to pass arguments to the handlers
RaiseIdleEvent(this, EventArgs.Empty);
}
}
void OnAggro()
{
EventHandler RaiseAggroEvent = Aggro;
if (null != RaiseAggroEvent)
{
// Change the EventArgs.Empty to an appropriate value to pass arguments to the handlers
RaiseAggroEvent(this, EventArgs.Empty);
}
}
}
Generic CreatureAI
All of your developers will implement their creature AIs based on this class. The constructor takes a GameServer reference parameter to allow the events to be hooked. This is a simplified example where the reference is not saved. In practice you would save the reference and allow the AI implementors to subscribe and unsubsrcibe from the events depending on what state their AI is in. For example subscribe to the Aggro event only when a player tries to steal your chicken's eggs.
public abstract class CreatureAI
{
// For the specific derived class AI to implement
protected abstract void IdleEventHandler(object theServer, EventArgs args);
protected abstract void AggroEventHandler(object theServer, EventArgs args);
// Prevent default construction
private CreatureAI() { }
// The derived classes should call this
protected CreatureAI(GameServer theServer)
{
// Subscribe to the Idle AND Aggro events.
// You probably won't want to do this, but it shows how.
theServer.Idle += this.IdleEventHandler;
theServer.Aggro += this.AggroEventHandler;
}
// You might put in methods to subscribe to the event handlers to prevent a
//single instance of a creature from being subscribe to more than one event at once.
}
The AIs themselves
These derive from the generic CreatureAI base class and implement the creture-specific event handlers.
public class ChickenAI : CreatureAI
{
public ChickenAI(GameServer theServer) :
base(theServer)
{
// Do ChickenAI construction
}
protected override void IdleEventHandler(object theServer, EventArgs args)
{
// Do ChickenAI Idle actions
}
protected override void AggroEventHandler(object theServer, EventArgs args)
{
// Do ChickenAI Aggro actions
}
}

Related

C# Unity: What's the difference between calling another class's function directly vs. calling the function by subscribing to an event?

In game development (Unity C#) there was always a question in my head about the best method or best approach to call a function from another class, there are two methods that i use here:
METHOD 1
Making the function DoSomething public in class 1 and call it from
class 2
Class 1:
public class Class1 : MonoBehaviour{
public static Class1 instance;
private void Awake()
{
instance = this;
}
//The function we want to call
public function DoSomething(){
Debug.Log("Done something!");
}
}
Class 2:
public class Class2 : MonoBehaviour{
private Class1 _class1;
private void start(){
_class1 = Class1.instance;
}
public function SomeFunction(){
//Calling the function
_class1.DoSomething();
}
}
METHOD 2
Creating an event in class 2 and subscribing to this event in class 1, this way the function will get called when we trigger the event in class 2
Class 1:
public class Class1 : MonoBehaviour{
private void OnEnable()
{
Class2.OnSomeEvent += DoSomething;
}
private void OnDisable()
{
Class2.OnSomeEvent -= DoSomething;
}
//The function we want to call
private function DoSomething(){
Debug.Log("Done something!");
}
}
Class 2:
public class Class2 : MonoBehaviour{
public static event Action OnSomeEvent = delegate {};
public function SomeFunction(){
OnSomeEvent();
}
}
What's the difference between the two methods and which one has the best functionality and performance?
The performance depends on the use case. Let's say you want to run some method on a class once the player health is below a certain value. One option is to check on every Update whether it's time to do that. That however would be inefficient. A better option would be to subscribe to an event which is triggered only when the health is changed and do the health check there instead.
Besides that, it's an extra tool for developing project architecture. Instead of mixing code together, you can have one class that deals with informing others when some X thing happens and others can listen to that event.
In your second solution with the event, Class2 doesn't need to know anything about Class1. Maybe even several other objects may subscribe this event or maybe none. Class2 presenting the event, may even belong to a library written at a time when the subscriber Class1 didn't exist.
Example: You are creating a control MySuperButton which looks much better than the standard buttons. This button will be used in different places and call different click-handlers. So, when writing the button code, you don't know the methods to be called when the button will be clicked. You just raise a Click-event and you are done.
Performance differences can be neglected in most cases.
The C# Programming guide says:
Events enable a class or object to notify other classes or objects when something of interest occurs.
The publisher determines when an event is raised; the subscribers determine what action is taken in response to the event.
So basically, you get a better decoupling of concerns and also an inversion of the dependencies.
There isn't any performance differences. Using the Event system can be much more code efficient/convenient. Suppose you want something in the program happens where you want DoSomething to stop firing, simply unsubscribe to the event.
Class2.OnSomeEvent -= DoSomething;
Another scenario, suppose that your senior dev wants Class3.SomethingMore and Class4.AnotherThing to happen too. Rather than chaining them to the DoSomething method they also can just subscribe to the event with multicasting.
// in Class 3 OnEnable
Class2.OnSomeEvent += SomethingMore;
// in Class 4 OnEnable
Class2.OnSomeEvent += AnotherThing;
Now OnSomeEvent will fire all three events. The delegate system makes your code much more readable, modular and easier to debug as your program becomes larger and complex. You can subscribe methods to important events in your game rather than making long, hard-to-read method call chains that are difficult to go back and make changes to. Generally, Unity is designed to be as modular as possible with its Component system, and a delegate event system is a way to also make method calls modular. You could also say there is an added bonus of classes not needing references to other classes and permissions for their methods when the other classes subscribe to the event.
To add one more example, lets say you didn't use the event system and right now you had different classes that periodically called.
Class1.DoSomething();
Your senior dev tells you that whenever DoSomething() is called, SomethingMore() and AnotherThing() need to be called. You can look through the code and try to find every place where DoSomething() is called and add two lines after SomethingMore() and AnotherThing() and make sure the classes calling have references to Class3 and Class4. Another option is to add references to these classes in Class1 and in the end of the DoSomething() method add SomethingMore() and AnotherThing(). The issue with this if later on your team figures out that DoSomething() should be called in certain cases by itself without SomethingMore() and AnotherThing(), now your code is going to start getting ugly.
Finally, lets say you are instantiating 'Bug' enemies that will Swarm() when the queen commands them to. There is no consistent amount of 'Bug' enemies as some spawn randomly and are destroyed by the player. Its much easier in the 'Bug' script for each bug to subscribe to the 'SwarmCommand' event of the queen than it is in the Queen script when 'SwarmCommand' is called to find references to each and every bug and call the bug 'Swarm()' (You would need to call FindObjectsWithTag or keep an up-to-date array of all the Bug GameObjects which would have some costs attached).

connect systems with events

Using the Entity-Component-System pattern I want to connect some systems with events. So some systems shouldn't run in a loop, they should just run on demand.
Given the example of a Health system a Death system should only run when a component gets below 1 health.
I thought about having two types of systems. The first type is a periodic system. This runs once per frame, for example a Render or Movement System. The other type is an event based system. As mentioned before a connection between Health and Death.
First I created a basic interface used by both system types.
internal interface ISystem
{
List<Guid> EntityCache { get; } // Only relevant entities get stored in there
ComponentRequirements ComponentRequirements { get; } // the required components for this system
void InitComponentRequirements();
void InitComponentPools(EntityManager entityManager);
void UpdateCacheEntities(); // update all entities from the cache
void UpdateCacheEntity(Guid cacheEntityId); // update a single entity from the cache
}
Further I created the interfaces
internal interface IReactiveSystem : ISystem
{
// event based
}
and
internal interface IPeriodicSystem : ISystem
{
// runs in a loop
}
but I'm not sure if they will be necessary. There is no problem using
foreach (ISystem system in entityManager.Systems)
{
system.UpdateCacheEntities();
}
but I don't want to run a system if not needed.
There are two types of Events, a ChangeEvent and a ExecuteEvent. The first gets triggered when a value from a component has changed. The second one gets triggered when something should be done with a specific entity.
If you Need or want to you can have a look at the EntityManager
https://pastebin.com/NnfBc0N9
the ComponentRequirements
https://pastebin.com/xt3YGVSv
and the usage of the ECS
https://pastebin.com/Yuze72xf
An example System would be something like this
internal class HealthSystem : IReactiveSystem
{
public HealthSystem(EntityManager entityManager)
{
InitComponentRequirements();
InitComponentPools(entityManager);
}
private Dictionary<Guid, HealthComponent> healthComponentPool;
public List<Guid> EntityCache { get; } = new List<Guid>();
public ComponentRequirements ComponentRequirements { get; } = new ComponentRequirements();
public void InitComponentRequirements()
{
ComponentRequirements.AddRequiredType<HealthComponent>();
}
public void InitComponentPools(EntityManager entityManager)
{
healthComponentPool = entityManager.GetComponentPoolByType<HealthComponent>();
}
public void UpdateCacheEntities()
{
for (int i = 0; i < EntityCache.Count; i++)
{
UpdateCacheEntity(EntityCache[i]);
}
}
public void UpdateCacheEntity(Guid cacheEntityId)
{
Health healthComponent = healthComponentPool[cacheEntityId];
healthComponent.Value += 10; // just some tests
// update UI
}
}
How can I create ChangeEvents and ExecuteEvents for the different systems?
EDIT
Is there a way to add event delegates to the components to run a specific system for this entity on change if a change event is listening or on demand if an execute event is listening?
By mentioning ChangeEvent and ExecuteEvent I just mean event delegates.
Currently I could do something like this
internal class HealthSystem : IReactiveSystem
{
//… other stuff
IReactiveSystem deathSystem = entityManager.GetSystem<Death>(); // Get a system by its type
public void UpdateCacheEntity(Guid cacheEntityId)
{
// Change Health component
// Update UI
if(currentHealth < 1) // call the death system if the entity will be dead
{
deathSystem.UpdateCacheEntity(cacheEntityId);
}
}
}
But I was hoping to achieve a better architecture by using event delegates to make systems communicate and share data between each other.
I am not an expert on this design pattern but I read something on it and my advice is: try not to forget the real purpose of this pattern. This time I found the article on Wikipedia really interesting.
It is basically saying (at least it is what I understood) that this pattern has been "designed" to avoid creating too many dependencies, losing the decoupling. Here an example I took from the article:
Suppose there is a drawing function. This would be a "System" that
iterates through all entities that have both a physical and a visible
component, and draws them. The visible component could typically have
some information about how an entity should look (e.g. human, monster,
sparks flying around, flying arrow), and use the physical component to
know where to draw it. Another system could be collision detection. It
would iterate through all entities that have a physical component, as
it would not care how the entity is drawn. This system would then, for
instance, detect arrows that collide with monsters, and generate an
event when that happens. It should not need to understand what an
arrow is, and what it means when another object is hit by an arrow.
Yet another component could be health data, and a system that manages
health. Health components would be attached to the human and monster
entities, but not to arrow entities. The health management system
would subscribe to the event generated from collisions and update
health accordingly. This system could also now and then iterate
through all entities with the health component, and regenerate health.
I think that you overcomplicated your architecture, losing the advantages that this pattern can give you.
First of all: why do you need the EntityManager? I quote again:
The ECS architecture handles dependencies in a very safe and simple
way. Since components are simple data buckets, they have no
dependencies.
Instead your components are constructed with the EntityManager dependency injected:
entityManager.AddSystem(new Movement(entityManager));
The outcome is a relatively complex internal structure to store entities and the associated components.
After fixing this, the question is: how can you "communicate" with the ISystems?
Again, answer is in the article: Observer Pattern. Essentially each component has a set of attached systems, which are notified every time a certain action occurs.
by what im getting at this, you want to have a repetitive, once every tick type event alongside a once in a year type event (exaggerated but clear), you can do this with a delegate call back function IE:
public delegate void Event(object Sender, EventType Type, object EventData);
public event Event OnDeath;
public event Event OnMove;
public void TakeDamage(int a)
{
Health-=a;
if(Health<1)
OnDeath?.Invoke(this,EventType.PlayerDeath,null);
}
public void ThreadedMovementFunction()
{
while(true)
{
int x,y;
(x,y) = GetMovementDirection();
if(x!=0||y!=0)
OnMove?.Invoke(this,EventType.PlayerMove,(x,y));
}
}
you can implement this into an interface, and then store the object class and only access the needed stuff like events and so on. but tbh i don't quite understand what you're looking for, so if you could elaborate on the exact issue or thing you need to solve, that would be greatly appreciated!

What is the most optimal way of communication between scripts in Unity

Say, for example, that I have a GameObject A with a Manager script attached, which on start spawns in x amount of GameObjects with B script attached.
A different GameObject with script C is supposed to do something when the GameObject with script B says so.
So the questions is, what would be the best way for these three to communicate?
Obviously, Script B could just call Script C, however I feel like this method lacks structure and organisation.
Script A could also have reference to script C, and script B could tell the Script A to act on Script C.
I feel like there is some sort of rule I am supposed to follow, however I haven't come across it yet. Any help is much appreciated!
Obviously, Script B could just call Script C, however I feel like this
method lacks structure and organisation.
True. This is what the GameObject.SendMessage function is used for. Unfortunately, it is slow and I wouldn't recommend it but it's worth mentioning.
If you have many objects that will need to communicate with other objects, implement an event manager with event and delegate. This is the proper way to do it. You can find full EventManager implementation here.
With it, you can register any amount of function to an event with:
EventManager.StartListening("jump", someFunction);
Un-register any function from an event with:
EventManager.StopListening("jump", someFunction);
From there, you can invoke the event on any object listening to it:
EventManager.TriggerEvent("jump");
If A already has a reference to script C, it can pass on this reference to B, when it is created. Thus, B can communicate with C, without going through A.
i.e
Script A:
// variables
public ScriptC c;
// methods
void SpawnB(){
// spawn B
B.setC(c); // B's variable for script C is passed in from A
}
Script B:
// variables
ScriptC c;
// methods
void setC(ScriptC v){
c = v;
}
Something along those lines.
You could also use the not Unity specific Action delegates. I like to use a static class for that but you could as well implement it in one of your existing classes (as long as you use static members and methods)
E.g.
public static class MyEvents
{
public static event Action SomeEvent;
public static void InvokeSomeEvent()
{
// Make sure event is only invoked if someone is listening
if (SomeEvent == null) return;
SomeEvent.Invoke();
}
}
This makes your classes completely independent (well, ok they share the MyEvents class) and easy to modularize.
In script C add a "listener" e.g.
private void Start()
{
// It is save to remove a listener also if it wasn't there yet
// This makes sure you are not listening twice by accident
MyEvents.SomeEvent -= OnSomeEvent;
// Add the listener for that event
MyEvents.SomeEvent += OnSomeEvent;
}
private void OnSomeEvent ()
{
// Do something if SomeEvent is invoked
}
Then somewhere in script B just call
MyEvents.InvokeSomeEvent();
So class B doesn't have to know or care who listens for that event; it just invokes it and cares for it's own business.
On the other side C or (any other class where you add a listener for the event) doesn't have to know/cares where the invoke came from; it just handles it and does its stuff.
Note however, that this also makes debugging a little bit harder since it is not that easy anymore to tell where the invoke came from ;)
Note: You can also add parameters to an Action e.g.
public static event Action<int> SomeParameterEvent;
In this case ofcourse all methods have to also implement that parameter
public static InvokeSomeParameterEvent(int value)
{
if(SomeParameterAction == null) return;
SomeParameterEvent.Invoke(value);
}
In C (the listener) you also have to receive the parameters
// name can be changed
private void OnSomeParameterEvent(int value)
{
//...
}
And ofcourse also call it with the parameter in B
MyEvents.InvokeSomeParameterEvent(someInt);
And than you can take it even on step further and instead of a value or a reference pass a complete delegate method as parameter. See examples here
Delegates and Events are mostly used for comunication as Programmer wrote.
For better structure and organization I would suggest using a MVC pattern or any other design pattern you like. Here you can find a great example of MVC implementation for Unity3D with simple yet powerful notification system:
Unity with MVC by Eduardo Dias da Costa
In that example you don't need to use delegates/events for communication and you keep everything well organized.
Some of the communication functions used in quoted tutorial in case the link gets deprecated:
1.
// Iterates all Controllers and delegates the notification data
// This method can easily be found because every class is “BounceElement” and has an “app”
// instance.
public void Notify(string p_event_path, Object p_target, params object[] p_data)
{
BounceController[] controller_list = GetAllControllers();
foreach(BounceController c in controller_list)
{
c.OnNotification(p_event_path,p_target,p_data);
}
}
// Fetches all scene Controllers.
public BounceController[] GetAllControllers() { /* ... */ }
2.
// This class will give static access to the events strings.
class BounceNotification
{
static public string BallHitGround = “ball.hit.ground”;
static public string GameComplete = “game.complete”;
/* ... */
static public string GameStart = “game.start”;
static public string SceneLoad = “scene.load”;
/* ... */
}
3.
// Handles the ball hit event
public void OnNotification(string p_event_path,Object p_target,params object[] p_data)
{
switch(p_event_path)
{
case BounceNotification.BallHitGround:
app.model.bounces++;
Debug.Log(“Bounce ”+app.model.bounce);
if(app.model.bounces >= app.model.winCondition)
{
app.view.ball.enabled = false;
app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball
// Notify itself and other controllers possibly interested in the event
app.Notify(BounceNotification.GameComplete,this);
}
break;
case BounceNotification.GameComplete:
Debug.Log(“Victory!!”);
break;
}
}
4.
// Callback called upon collision.
void OnCollisionEnter() { app.Notify(BounceNotification.BallHitGround,this); }
Of course you can still implement MVC and use Delegates and Events. It is just to show another way of doing things.
Check out this really good article on communicating through Unity's scriptable objects. The removes the need for scripts to have references to each other, facilitating much more modular code. Within that link I shared, I really recommend the links there about Richard Fine and Ryan Hipple's presentations too.

Create a special dictionary<T, EventDelegate<T>> with generic delegate EventDelegate<T>(T e) where T : GameEventBase

I have a game with many classes that need to listen to events. But under certain circumstances, those classes are destroyed or disabled. When that happens, I need to remove their listening methods from the events manager delegate table.
I don't want to modify the EventsManager and I would like to each class that adds any events to it to know which events it added.
I'm currently using something like this do add and remove the events in each class:
void AddEventsListeners() {
EventsManager.AddListener<OnClickDown>(OnClickDownHandler);
EventsManager.AddListener<OnClickUp>(OnClickUpHandler);
EventsManager.AddListener<OnClick>(OnClickHandler);
}
void RemoveEventsListeners() {
EventsManager.RemoveListener<OnClickDown>(OnClickDownHandler);
EventsManager.RemoveListener<OnClickUp>(OnClickUpHandler);
EventsManager.RemoveListener<OnClick>(OnClickHandler);
}
Those OnClick are all derived from GameEventBase, and the OnClickHandler are methods declared as
void OnClickDown(OnClickHandler e) {}
to match the delegate that is used in the EventsManager, which is declared as
delegate void EventDelegate<T>(T e) where T : GameEventBase;
I want to be able to fill a special hash table named, say, events, that has keyvalue pairs like
<T, EventDelegate<T>> where T: GameEventBase
That is, I want to be able to do events.add(OnClick, OnClickHandler), where OnClickHandler is declared as
OnClickHandler(OnClick e) {}
And I want adding to fail if OnClickHandler where defined, for example, as
OnClickHandler(OtherGameEventBaseDerivedEvent e) {}
That requirement translates to me wanting type safety in that special dictionary.
One of my attempts involved not a dictionary, but a way to decide which method to call, between the AddListener and RemoveListener
I didn't like it because it introduces a parameter to the method and the code reads really weird with it. It does work, and does reduce the repetition, but is too ugly.
I create a AddOrRemoveAllListeners(AddOrRemove addOrRemove), which I populated with calls to AddOrRemoveListener for each event.
Now all I had to do is AddOrRemoveAllListeners(AddOrRemove.Remove) or AddOrRemoveAllListeners(AddOrRemove.Add), to add or remove my events.
enum AddOrRemove {
Remove,
Add
}
void AddOrRemoveListener<T>(EventsManager.EventDelegate<T> del, AddOrRemove addOrRemove)
where T : GameEventBase {
switch (addOrRemove) {
case AddOrRemove.Remove:
EvMan.RemoveListener<T>(del);
break;
case AddOrRemove.Add:
EvMan.AddListener<T>(del);
break;
}
}
Another attempt involved creating the type
class EventsDictionary<T> : Dictionary<T, EventsManager.EventDelegate<T>> where T : GameEventBase { }
And using it like this:
EventsDictionary<GameEventBase> events = new MyCustomDictionary<GameEventBase>();
void AddEventHandlerPairToEventsDictionary<T>(T e, EventsManager.EventDelegate<T> handler) where T : GameEventBase {
if (!events.ContainsKey(e)) {
events.Add(e, handler);
}
}
But the events.Add(e, handler) fails and forces me to declare the handler as
EventsManager.EventDelegate<GameEventBase>
instead of
EventsManager.EventDelegate<T>
If I do that, I could add keyvalue pairs that don't make sense in that events type, i.e., I lose the event handling type safety.
I want to have such a structure because I don't like all those repetitions. It would be really bad if someone forgot to remove an event in the RemoveEventsListeners().
Having such a dictionary, I could use a foreach loop to add/remove the handlers to the EventsManager, which would be really nice.
As for performance, this is for a game and it needs to have good performance. Those adding/removing of events can happen a lot (sometimes hundreds of times per frame) because a lot of objects are destroyed (can't leave null handlers in the EventsManager) or disabled (need to stop listening to everything until enabled again) all the time. This means reflection and lots of casting/boxing or anything that creates lots of garbage collected objects is out.
I'm, of course, open to suggestions as to other ways to approach this.
Thanks for your assistance!
I'm attaching the relevant parts of the EventsManager being used (The RemoveListener() is analogous to the AddListener). The GameEventBase is just an empty shell. It isn't a .NET event nor uses EventArgs.
public class EventsManager : ManagedBase {
public delegate void EventDelegate<T>(T e) where T : GameEventBase;
private delegate void EventDelegate(GameEventBase e);
private readonly Dictionary<Type, EventDelegate> delegates = new Dictionary<Type, EventDelegate>();
private readonly Dictionary<Delegate, EventDelegate> delegateLookup = new Dictionary<Delegate, EventDelegate>();
public void AddListener<T>(EventDelegate<T> del) where T : GameEventBase {
// Early-out if we've already registered this delegate
if (delegateLookup.ContainsKey(del)) {
return;
}
// Create a new non-generic delegate which calls our generic one.
// This is the delegate we actually invoke.
EventDelegate internalDelegate = (e) => del((T) e);
delegateLookup[del] = internalDelegate;
EventDelegate tempDel;
if (delegates.TryGetValue(typeof (T), out tempDel)) {
delegates[typeof (T)] = tempDel + internalDelegate;
}
else {
delegates[typeof (T)] = internalDelegate;
}
}
public void Raise(GameEventBase e) {
EventDelegate del;
if (delegates.TryGetValue(e.GetType(), out del)) {
del.Invoke(e);
}
}
}
Your problems seem to be solved if you use the EventAggregator pattern.
There is a short description of it by Martin Fowler
Some very good implementations of it already exist, for example in caliburn micro and
Microsoft Prism
The general idea is that you simplify event registration and deregistration and have a single source of events for many objects.
I never had performance issues with it. You simply put a _eventAggregator.Subscribe(this) when you want to start listening to events for an object and Unsubscribe if you want to stop. Whereever you want to fire an event, just publish it, EventAggregator does the routing.
This once again looks like an XY problem. OP seems to want to have a central place to handle event handlers, registration and disposal. The OP has gone down the route of trying to create a pattern that deal with this in a generic way, but has not looked into the state of the art regarding how this problem is typically solved. He has now come up against a problem in his design and is now asking for a solution to THAT problem, rather than the original problem of event handlers.
There are two good solutions to event handler registration lifecycle management that I know of in .net.
Weak Event Handler
You state that "It would be really bad if someone forgot to remove an event in the RemoveEventsListeners()." Yet do not actually mention WHY it is bad. Typically the only reason for this being bad is that the event handler will now keep an object in reference, that should be collected. With weak reference event handlers, the GC will still be able to collect your object, even when it subscribes to an object that is still alive.
Rx.Net
Rx.Net abstracts event registrations into IDisposables, which you can tie to the object's lifetime, assuming of course you want to control the lifetime of the registrations.
However I actually find the IObservable pattern much nicer to work with than event handler pattern, mostly because C# lacks first class support for event handlers (this is not the case with F#).
F#
Most of your problems will have stemmed from the short sighted design of events keyword handling in C# (specifically not making events a first class construct). F# however does support first class events, and thus should be able to support the pattern you are trying to construct.
Thus with this option you should scrap your code base and rewrite it in F#.
*EDIT added tongue in cheek option of rewriting in F#.

Fastest way to hold and parse through a collection of Enums?

In the program I am creating, I have a large amount of enums
enum kEvents
{
Start = 0,
End,
EntityCreated,
}
In this program, there is a huge list of Entities.
Each entity holds a list of "kEvents" that it is "Registered to"
Every time an object says "Call the Start event" I have to iterate through every single entity in the game, and find out if they are "listening" for that event.
Note: I know that c# has event handlers, but I was hoping to create this enum system from the ground up instead.
This being the case, what is the best way to:
Hold the collection of enums in each entity object
Check if the entity holds the triggered enum
I am wondering if Enums, being basically numbers, have a lower level, quicker way to handle this than a List<T> of objects.
Every time an object says "Call the Start event" I have to iterate through every single entity in the game, and find out if they are "listening" for that event.
You're doing it wrong! Iterating over every single object and checking if they're registered for an event is very inefficient! This is the typical Observer design pattern and there are several ways to go about this, here are a couple:
Have a single event which gets raised and has an enum parameter for event type (every subscriber gets it).
Have a dictionary of enums and corresponding events.
Here is what option 1 might look like:
delegate void OnEventDelegate(kEvents anEvent);
public class MyEventObservable
{
public event OnEventDelegate OnEvent;
}
public class MyEventObserver
{
// Constructors and all
// ...
public void OnEventReceived(kEvents anEvent)
{
switch(anEvent)
{
// switch through all the events and handle the ones that you need
}
}
}
MyEventObserver observer = new MyEventObserver();
MyEventObservable observable = new MyEventObservable();
observable.OnEvent += new OnEventDelegate(observer.OnEventReceived);
Here is option 2:
public class MyEventObservable
{
private Dictionary<kEvents, List<IObserver>> _observers;
MyEventObservable()
{
// Initialize the dictionary with all the events
}
public void RegisterObserver(kEvents event, IObserver observer)
{
_observers[event].Add(observer);
}
}
interface class IObserver
{
void Notify(kEvents anEvent);
}
public MyEventObserver: IObserver
{
// Constructors and all
// ...
// Override the IObserver
public void Notify(kEvents anEvent)
{
switch(anEvent)
{
// switch through all the events and handle the ones that you need
}
}
}
MyEventObserver observer = new MyEventObserver();
MyEventObservable observable = new MyEventObservable();
observable.RegisterObserver(kEvents.Start, observer);
Option two will reduce the number of events each observer has to handle, but it comes at the cost of having to register for every event separately (which adds coding complexity). This means that option 2 will work faster because there are fewer function calls to make. Depending on how "crazy" you want to get with the performance, there might be other options that can help you to speed it up but this should set you on the right track.
P.S. I have not compiled any of the code, but it should give you a general idea of how things ought to work.
It seems to me that you want to implement some kind of publish/subscribe scheme, some centralized event sink that the Entities can subscribe to events and provide a callback when that event happens, then other Entities would Rise the event and the event sink would call all the Entities that are subscribed to that event.... but I may have not understood the question :)
Something like:
How to access controls in other class
Every time an object says "Call the Start event" I have to iterate
through every single entity in the game, and find out if they are
"listening" for that event.
If I have correctly understood what you are doing then you are doing it backwards. the knowledge of which events an entity is listening to cannot be (only) in the event itself. There must be another data structure that holds this information.
That other data structure could be something as simple as a Map from the enum constant to a list of entities.

Categories

Resources