I am making a game and I'm trying to create an way for objects to handle collisions with each other. I want to do something like:
//Imaginary C#
public SomethingThatCollides()
{
CollisionEvent<ObjectA> += CollisionWithObjA;
CollisionEvent<ObjectB> += CollisionWithObjB;
}
void CollisionWithObjA(ObjectA other)
{
//Do something
}
void CollisionWithObjB(ObjectB other)
{
//Do something else
}
When, say, CollisionEvent<ObjectA> is raised (perhaps by some collision checking code), CollisionWithObjA should get called. Same for CollisionWithObjB; when a collision with ObjectB is detected, it will raise the CollisionEvent<ObjectB> event which results in CollisionWithObjB getting called.
Is something like this possible?
Here is the thing, if class is generic and it has static field, it can work like a dictionary with key being type
public class Something {
public class EventsHolder<T>
{
static event Action<T> CollideEvent;
}
public void AddEvent<T>(Action<T> collisionEvent)
{
EventsHolder<T>.CollideEvent = collisionEvent;
}
public void RaiseCollision<T>(T Obj)
{
var Event = EventsHolder<T>.CollideEvent;
if (Event != null) Event(Obj);
}
}
Downside is that it uses static fields which can be inapropriate.
In this case you can use code #Daniel posted.
You can't really create a generic event like that. I suggest you create a special event arguments class that also encapsulates the collided object and check for its type in the event handler method:
public class CollisionEventArgs : EventArgs {
public object Object {
get; private set;
}
// ...
}
You'll need a special dispatcher method to use it:
class SomethingThatCollides {
public SomethingThatCollides(CollisionManager cm) {
cm.CollisionEvent += CollisionWithObj;
}
void CollisionWithObj(object sender, CollisionEventArgs args) {
if (args.Object is ObjectA) {
CollisionWithObjA((ObjectA)args.Object);
}
else if (args.Object is ObjectB) {
CollisionWithObjB((ObjectB)args.Object);
}
}
// ...
}
Or, you can try to solve this with double-dispatching, without using C# events. Look at wikipedia for a collision example.
That's uggly, but...You could have a dicionary of events by type:
Dictionary<Type, object> MyEventsByType;
event Action<A> CollisionEventA;
event Action<B> CollisionEventB;
event Action<C> COllisionEventC;
void Initialize()
{
MyEventsByType = new Dictionary<Type, object>();
MyEventsByType.Add(typeof(A), CollisionEventA);
MyEventsByType.Add(typeof(B), CollisionEventB);
MyEventsByType.Add(typeof(C), CollisionEventC);
}
void RaiseCollision<T>(T Obj)
{
Action<T> Event = (Action<T>)MyEventsByType[typeof(T)];
if (Event != null) Event(Obj);
}
Related
I have class "A", which will send event "a". Classes that are subscribing to event "a" will react to this event. Other classes can subscribe to event "a" without changing anything in class "A";
Now, what is the most reasonable way to do this in unity? Is there some messaging system that can already do that?
If not, should I make something like EventManager class that will store subscriber classes in array and call their methods?
There are probably many ways to do this.
Public static List
public class A : MonoBehaviour
{
public static List<A> AvailableAs = new List<A>();
private void Awake()
{
if(!AvailableAs.Contains(this)) AvailableAs.Add(this);
}
private void OnDestroy()
{
if(AvailableAs.Contains(this)) AvailableAs.Remove(this);
}
public void SomePublicMethod()
{
// magic
}
}
and use it e.g. like
public class B : MonoBehaviour
{
// run the method on all currently registered A instances
public void DoIt()
{
foreach(var a in A.AvailableAs)
{
a.SomePublicMethod();
}
}
}
Global EventHandler
Or if you rather want to go for encapsulation have as you mentioned a global event handler for all A's like
namespace ANamespace
{
public static class AEventHandler
{
internal static event Action OnInvokeA;
public static void InvokeAEvent()
{
if(OnInvokeA != null) OnInvokeA.Invoke();
}
}
}
and in A have
namespace ANamespace
{
public class A : MonoBehaviour {
private void Awake()
{
// it is save to remove a callback first even
// if it was not added yet. This makes sure it is
// added only once always
AEventHandler.OnIvokeA -= SomePrivateMethod;
AEventHandler.OnIvokeA += SomePrivateMethod;
}
private void OnDestroy()
{
AEventHandler.OnIvokeA -= SomePrivateMethod;
}
private void SomePrivateMethod()
{
// magic
}
}
}
Now in B you would rather simply do
// invoke the event and whoever is added as listener will do
// whatever he registered
// in this case all A instances execute their method
AEventHandler.InvokeAEvent();
Unity Event
If you have however only one class A which throws an event and you want others to react to it simply use a UnityEvent like
public class A : MonoBehaviour
{
public UnityEvent OnSomethingHappened = new UnityEvent();
private void SomeMethodIWillRun()
{
//...
OnSomethingHappened.Invoke();
}
}
Now you cann easily add callbacks to that event in the Unity Inspector by dragging in GameObjects/Components and select the method to call. (Exactly the same thing is used for the onClick event of the Button component btw. ;) )
And you could still add callbacks via script on runtime like
public class B : MonoBehaviour
{
public A referenceToA;
private void Start()
{
referenceToA.OnSomethingHappened.AddCallback(OnASomethingHappened);
}
private void OnDestroy()
{
referenceToA.OnSomethingHappened.RemoveCallback(OnASomethingHappened)
}
private void OnASomethingHappened()
{
//
}
}
Note: Typed on smartphone so no warranty but I hope the idea gets clear.
There is a video tutorial on unity official website called:
Events: Creating a simple messaging system.
They create a Event Manager or Messaging System.
I watch it and it was very helpful so I create that system on my game and now decided to instead of using UnityEvent and UnityAction use delegate and event which is better performance and good practice. So here is my code [StopListen() function not included yet]:
public class EventManager : MonoBehaviour
{
public delegate void GameEvents();
public event GameEvents onGameEvent;
private static EventManager _instance = null;
public static EventManager Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType(typeof(EventManager)) as EventManager;
}
if(!_instance)
{
Debug.LogError("Put a GameObject with this scrip attach to it on your scene.");
}
else
{
_instance.InitializeEventDictionary();
}
return _instance;
}
}
private Dictionary eventsDictionary;
void InitializeEventDictionary()
{
if (eventsDictionary == null)
eventsDictionary = new Dictionary();
}
public void Listen(string eventName, GameEvents gameEvent)
{
GameEvents thisEvent = null;
if (Instance.eventsDictionary.TryGetValue(eventName, out gameEvent))
{
thisEvent += gameEvent;
}
else
{
thisEvent = new GameEvents();
thisEvent += gameEvent;
Instance.eventsDictionary.Add(eventName, thisEvent);
}
}
public void TriggerEvent(string eventName)
{
GameEvents thisEvent;
if(Instance.eventsDictionary.TryGetValue(eventName, out thisEvent))
thisEvent.Invoke();
}
}
In my Listen() function this line thisEvent = new GameEvents(); gets me in trouble and I don't know how to fix it! (Help!) :-)
[PS] :
Is delegate and event have better performance then UnityEvent and UnityAction ?
What more should be or must be add to this code, to make it more efficient?
You must define what should invoked when the delegate is accessed, otherwise if nothing should be done the delegate is not necessary.
Like a lamda:
thisEvent = new GameEvents(() => { Console.WriteLine("TODO");});
Or a method:
thisEvent = new GameEvents(Target);
private void Target()
{
throw new NotImplementedException();
}
may be have a look at https://www.codeproject.com/Articles/624575/Delegate-Tutorial-for-Beginners
For execution time, I think best is to make a test and see what performs better.
I'm used to using delegate EventHandler for event callbacks but now I'm attempting to use event Action for invoking events. I couldn't find much info on how this can be used properly anywhere so I'm hoping someone can point me in the right direction.
I have an Action event handler that handles string objects. In my subscriber class I have public event Action<string> UploadProgress;. The event handler is invoked like this:
UploadProgress.Invoke(string.Format("sending file data {0:0.000}%", (bytesSoFar * 100.0f) / totalToUpload));
The listener class is subscribed to this event as below:
uploadFile.UploadProgress += uploadFile_UploadProgress;
void uploadFile_UploadProgress(string obj)
{
var prog = obj;
}
When the event is invoked, I get System.NullReferenceException: Object reference not set to an instance of an object. I'm not sure what I need to change in the subscriber class to avoid this error. Can someone tell me the proper way to use event Action or provide me the link to where I can read up on it? I know how to use the normal Action but confused about declaring it as an event. Any help is appreciated.
This way is much better, send bytesToUpload and totalToUpload through event, instead of the whole Action (right sample):
internal class Program
{
private static void Main(string[] args)
{
SomeClass someClass = new SomeClass();
someClass.UploadProgress += SomeClass_UploadProgress;
someClass.DoSomeUpload();
}
private static void SomeClass_UploadProgress(object sender, UploadEventArgs e)
{
string s = string.Format("sending file data {0:0.000}%", (e.BytesSoFar * 100.0f) / e.TotalToUpload);
Console.WriteLine(s);
}
}
public class UploadEventArgs : EventArgs
{
public float BytesSoFar { get; set; }
public float TotalToUpload { get; set; }
}
public class SomeClass
{
public event EventHandler<UploadEventArgs> UploadProgress;
public void DoSomeUpload()
{
if (UploadProgress != null)
{
UploadEventArgs e = new UploadEventArgs();
e.BytesSoFar = 123f;
e.TotalToUpload = 100000f;
UploadProgress.Invoke(this, e);
}
}
}
I was looking through some old code today and found an event handler that looked like this:
public void HandleEvent(EventClassA eventObj)
{
if(eventObj is EventSubClassA)
{
HandleEventSubClassA(eventObj as EventSubClassA);
}
else if(eventObj is EventSubClassB)
{
HandleEventSubClassB(eventObj as EventSubClassB);
}
else if(eventObj.GetType() == typeof(EventSubClassC))
{
HandleEventSubClassC(eventObj as EventSubClassC);
}
else if(eventObj is EventSubClassD)
{
HandleEventSubClassD(eventObj as EventSubClassD);
}
}
I thought this was kind of ugly. So I refactored it like this:
delegate void EventHandler(dynamic eventObj);
private static readonly Dictionary<Type, EventHandler> EVENT_MAP = new Dictionary<Type, EventHandler>()
{
{ typeof(EventSubClassA), HandleEventSubClassA },
{ typeof(EventSubClassB), HandleEventSubClassB },
{ typeof(EventSubClassC), HandleEventSubClassC },
{ typeof(EventSubClassD), HandleEventSubClassD }
};
public void HandleEvent(EventClassA eventObj)
{
EVENT_MAP[eventObj.GetType()](eventObj);
}
private void HandleEventSubClassA(dynamic evt)
{
var eventObj = evt as EventSubClassA;
}
I had a coworker review the code and there were concerns about the way this solution worked compared to the previous solution. I have a hard time believing that the previous solution is the best solution for this case, so I've turned to StackOverflow.
Is there a better way to build this type of class?
Is there a pattern I'm not aware of that is designed for this?
You can use generics to make your existing solution slightly safer:
private static Dictionary<Type, Delegate> handlers;
static HandlerClass()
{
handlers = new Dictionary<Type, Delegate>();
AddHandler<EventSubClassA>(HandleEventSubClassA);
AddHandler<EventSubClassB>(HandleEventSubClassB);
...
}
public static void AddHandler<T>(Action<T> handler) where T : EventClassA
{
handlers[typeof(T)] = handler;
}
public void HandleEvent(EventClassA #event)
{
Delegate handler;
if(handlers.TryGetValue(#event.GetType(), out handler))
{
handler.DynamicInvoke(#event);
}
}
Alternatively, if you can modify the classes in your event hierarchy you could implement the visitor pattern:
public interface IHandlers
{
void HandleSubClassA(EventSubClassA a);
void HandleSubClassB(EventSubClassB b);
...
}
public abstract class EventClassA
{
public abstract void Visit(IHandlers handlers);
}
public class EventSubClassA : EventClassA
{
public override void Visit(IHandlers handlers)
{
handlers.HandleSubClassA(this);
}
}
I feel like I'm missing something.
Wouldn't the best way to be to write overloads for each event type?
I have a class that goes like this:
public static class Messenger<T>
{
private static readonly Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();
public static void DoSomethingWithEventTable() //Somehow fills eventTable
public static void Clear()
{
eventTable.Clear();
}
}
Now, I called DoSomethingWithEventTable two times somewhere in my program, like this:
Messenger<int>.DoSomethingWithEventTable();
Messenger<float>.DoSomethingWithEventTable();
I want to clear eventTable for every Messenger<T>. How should I do it? Should I call Clear for every type that I have put in generics, like this:
Messenger<int>.Clear();
Messenger<float>.Clear();
Or will it be enough to do something silly like this once:
Messenger<string>.Clear();
UPD: Basic experiments show that I should clear the Messenger for every used T. Now could somebody come with better design for the classes?
The more detailed version of what I am using now:
static public class Messenger<T>
{
private static readonly Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();
static public void AddListener(string eventType, Callback<T> handler)
{
// Obtain a lock on the event table to keep this thread-safe.
lock (eventTable)
{
// Create an entry for this event type if it doesn't already exist.
if (!eventTable.ContainsKey(eventType))
{
eventTable.Add(eventType, null);
}
// Add the handler to the event.
eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler;
}
}
static public void RemoveListener(string eventType, Callback<T> handler)
{
// Obtain a lock on the event table to keep this thread-safe.
lock (eventTable)
{
// Only take action if this event type exists.
if (eventTable.ContainsKey(eventType))
{
// Remove the event handler from this event.
eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler;
// If there's nothing left then remove the event type from the event table.
if (eventTable[eventType] == null)
{
eventTable.Remove(eventType);
}
}
}
}
static public void Invoke(string eventType, T arg1)
{
Delegate d;
// Invoke the delegate only if the event type is in the dictionary.
if (eventTable.TryGetValue(eventType, out d))
{
// Take a local copy to prevent a race condition if another thread
// were to unsubscribe from this event.
Callback<T> callback = (Callback<T>)d;
// Invoke the delegate if it's not null.
if (callback != null)
{
callback(arg1);
}
}
}
static public void Clear()
{
eventTable.Clear();
}
}
It is also important that I have another classes Messenger (non-generic, yeah) and Messenger<T,M>, and maybe someday I would even need something like Messenger<T,M,N>, etc.
Each Messenger<T> type will have it's own copy of eventTable so you will need to call Clear() for every different T you have used.
As shown by this test:
[TestFixture]
public class Tests
{
static class MyClass<T>
{
public static List<int> Member = new List<int>();
}
[Test]
public void StaticTest()
{
var m1 = MyClass<int>.Member;
var m2 = MyClass<string>.Member;
Assert.AreNotSame(m1, m2);
}
}
Since
private static readonly Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();
does not depend on <T>, create a static "handler" for all event tables.
IE
public static class TableHandler {
ICollection<Dictionary<string, Delegate>> tables = new List<Dictionary<string, Delegate>>();
public void Add(Dictionary<string, Delegate> item)
{
tables.Add(item);
}
public void Clear()
{
foreach (var item in tables) item.Clear();
tables.Clear();
}
}
and ensure that DoSomethingWithEventTable() adds the event table to the TableHandler.
Might not be the best overall solution, but it helps you keep track of the tables with the current design.
EDIT:
I tried to google for a way to find all generic variants of a static class, but I did not find a way. Does anyone know of a way to do that?