@Lucas says they used reflection. See his answer’s here. and comments at the end of this blog.
If you’re not using SendMessage rapidly in performance-sensitive areas, it’s OK.
I don’t use it not because it’s slow, but because it makes my codes and designs ugly (string literals are a no-no for me…)
If you want a better approach sending messages, consider using delegates. If you want to globally send events, consider using some sort of an event manager, here’s what I used in my uFAction
using System;
using System.Linq;
using System.Reflection;
//using Fasterflect;
namespace uFAction
{
/// <summary>
/// A generic event system based on C#'s delegates
/// Subscribe (add), unsubscribe (remove) and raise (fire) GameEvents
/// Derive from GameEvent to create your own events
/// (There's no target differentiation here nor any other constraint, you could target anything)
/// </summary>
public static class EventManager
{
/// <summary>
/// Subscribe (add) a handler to the GameEvent specified by the generic argument `T`
/// </summary>
/// <typeparam name="T">The type of GameEvent to unsubscribe from</typeparam>
/// <param name="handler">The handler that wants to unsubscribe (the handler to be removed)</param>
public static void Subscribe<T>(Action<T> handler) where T : GameEvent
{
EventManagerInternal<T>.Subscribe(handler);
}
/// <summary>
/// Unubscribe (remove) a handler to the GameEvent specified by the generic argument `T`
/// </summary>
/// <typeparam name="T">The type of GameEvent to subscribe to</typeparam>
/// <param name="handler">The handler to subscribe (the handler to be added)</param>
public static void Unsubscribe<T>(Action<T> handler) where T : GameEvent
{
EventManagerInternal<T>.Unsubscribe(handler);
}
/// <summary>
/// Raises the specified event - Resolves the event type at runtime (uses faster flect cached reflection)
/// </summary>
public static void DynamicRaise(GameEvent e)
{
var type = typeof(EventManagerInternal<>);
var eventType = e.GetType();
var genType = type.MakeGenericType(eventType);
var raise = genType.GetMethod("Raise", BindingFlags.Public | BindingFlags.Static);
raise.Invoke(null, new object[] { e });
// if you have Fasterflect, you could call the method like this for better performance
//var raise = genType.DelegateForCallMethod("Raise", Flags.StaticPublic, eventType);
//raise(null, e);
}
/// <summary>
/// Raise (fire) the GameEvent specified by the generic argument `T`
/// </summary>
/// <typeparam name="T">The type of GameEvent to be raised</typeparam>
public static void Raise<T>(T e) where T : GameEvent
{
EventManagerInternal<T>.Raise(e);
}
/// <summary>
/// Clears the GameEvent delegate
/// </summary>
public static void Clear<T>() where T : GameEvent
{
EventManagerInternal<T>.Clear();
}
/// <summary>
/// Returns true if the specified handler is contained in the GameEvent's delegate invocation list
/// </summary>
public static bool Contains<T>(Action<T> handler) where T : GameEvent
{
return EventManagerInternal<T>.Contains(handler);
}
/// <summary>
/// Rasies the game event 'e' to all handlers except the ones specified
/// </summary>
public static void RaiseToAllExcept<T>(T e, params Action<T>[] handlers) where T : GameEvent
{
EventManagerInternal<T>.RaiseToAllExcept(e, handlers);
}
/// <summary>
/// Raises the game event 'e' only to the specified handlers
/// </summary>
public static void RaiseToOnly<T>(T e, params Action<T>[] handlers) where T : GameEvent
{
EventManagerInternal<T>.RaiseToOnly(e, handlers);
}
/// <summary>
/// The internal event manager class - do not be afraid, doing a:
/// EventManagerInternal`Event1`.Sub(...); and EventManagerInternal`Event2`.Sub(...);
/// the compiler will _not_ create two seperate classes - they will use the same place in memory!
/// If you don't know what I'm talking about, see Jamie King's: https://www.youtube.com/watch?v=9eZnUk0Gu7M
/// </summary>
private static class EventManagerInternal<T> where T : GameEvent
{
private static Action<T> action;
public static void Subscribe(Action<T> handler)
{
action += handler;
}
public static void Unsubscribe(Action<T> handler)
{
action -= handler;
}
public static void Raise(T e)
{
if (action != null)
action(e);
}
public static void RaiseToAllExcept(T e, params Action<T>[] handlers)
{
var toInvoke = from d in action.GetInvocationList()
where !handlers.Contains(d)
select (Action<T>)d;
foreach (var handler in toInvoke)
handler(e);
}
public static void RaiseToOnly(T e, params Action<T>[] handlers)
{
foreach (var h in handlers)
h(e);
}
public static void Clear()
{
action = null;
}
public static bool Contains(Action<T> handler)
{
if (action == null)
return false;
return action.GetInvocationList().Any(d => d.Equals(handler));
}
}
}
}
namespace uFAction
{
/// <summary>
/// An abstract base class for creating events - derive from it to create your own game events to use
/// in the generic event system, EventManager.
/// </summary>
public abstract class GameEvent
{
}
}
See the doc in the link ^ for usage examples.
You basically create an event:
public class OnPlayerDied : GameEvent
{
public GameObject Player { get; set; }
public MoreInfo info { get; set; }
}
Listen to that event:
public class GameManager : MonoBehaviour
{
void OnEnable() { EventManager.Subscribe<OnPlayerDied>(DeathHandler); }
void OnEnable() { EventManager.Unsubscribe<OnPlayerDied>(DeathHandler); }
void DeathHandler(OnPlayerDied arg)
{
print("Player + " arg.Player.name + " has died");
}
}
And raise the event somewhere:
public class Player : MonoBehaviour
{
private int health;
public int Health
{
get { return health; }
set {
health = value;
if (health <= 0)
EventManager.Raise(new OnPlayerDied { Player = gameObject, MoreInfo = new MoreInfo(...) });
}
}