Hey all, just wanted to share my approach to messaging since it’s a bit different from what Ben shows us in the course with SendMessage().
Just a quick warning: this might be a bit more advanced for those that aren’t very familiar with C#. This is well beyond the scope of what was introduced at this point in the course, but I know there have been a few questions/comments about using standard C# events instead of the SendMessage() method so I thought I would make a quick post about how I tackled it.
My CollisionHandler script:
public class CollisionHandler : MonoBehaviour
{
public delegate void CollisionEvent(CollisionHandler sender, Collider collider);
public event CollisionEvent OnCollision;
public void OnTriggerEnter(Collider other)
{
if (OnCollision == null)
{
return;
}
OnCollision(this, other);
}
}
And the relevant parts of the PlayerController:
public class PlayerController : MonoBehaviour
{
private CollisionHandler collisionHandler;
public void Start()
{
collisionHandler = GetComponent<CollisionHandler>();
if (collisionHandler != null)
{
collisionHandler.OnCollision += new CollisionHandler.CollisionEvent(OnPlayerCollision);
}
}
private void OnPlayerCollision(CollisionHandler sender, Collider collider)
{
print("Collision happened!");
}
}
Since I know there will be some questions, here are a few preemptive answers:
What’s going on here?
In essence, the CollisionHandler becomes an event (or message) provider that allows other classes to register receivers for those events that are provided. In this case specifically, the PlayerController script registers the OnPlayerCollision method as an event receiver for the OnCollision events that are published by the CollisionHandler script. It’s then up to the provider to call those registered receivers to send an event (message) by simply calling OnCollision.
For more information I’d highly encourage referencing this Microsoft/C# article as it has some great information: https://docs.microsoft.com/en-us/dotnet/standard/events/
Why?
Well, to start, I have a personal vendetta against hard-coded strings as I’ve experienced a number of problems with them after refactoring old code. They also make it incredibly difficult to find code that is dependent on that particular string, short of a straight-up file search (which often yields more results that aren’t relevant than results that are).
In addition to now being tied in code and having a direct reference, this implementation also give me the ability to pass the OnPlayerCollision method references to the CollisionHandler script as well as the Collider that triggered the event originally. This isn’t all that useful at this point, but in the future I could use it to get information about the Collider and make decisions or perform actions based on that information.
Lastly, the traditional C# event system is more flexible and more robust. In theory, it should also have better performance than using SendMessage(), although the difference would probably not be noticeable in this course or in a game this size.
That’s great, but why use a custom delegate and not System.EventHandler?
In traditional C# code System.EventHandler is great as it provides a very generic delegate and the ability to pass back information as a custom EventArgs object. In this case, I didn’t want to have to create that custom object just to pass back my Collider, and I also didn’t want to have to cast my sender (CollisionHandler script) from a generic object in the event that I wanted to use it. The custom delegate allowed me to avoid both of those cases by passing back exactly what I wanted.
Why would SendMessage() be less performant? Shouldn’t Unity’s standard approach be better?
I couldn’t find the exact implementation of SendMessage(), but it looks like it was implemented using reflection (this is where my “in theory” caveat comes in). Assuming it is, it would be reflectively checking every script on the game object it’s attached to for anything with a public method that matches the string passed as the event name to SendMessage(). For each one it finds, it would then execute those methods accordingly.
Alternatively, with a traditional event, the class will have direct references to any event handlers that are tied to the OnCollision event. This means that instead of searching through what could be a large amount of code behind the scenes, the engine simply iterates through a collection of methods designated to handle that event.
I believe SendMessage() was likely created to make this process easier. There is quite a bit of boilerplate code when using delegates/event handlers.
Are you implying that SendMessage() is inferior and shouldn’t be used at all?
No! SendMessage() is great for a number of different reasons, the primary being that it is easier to use, especially for those less familiar to C#. My only intention with this post was to show an alternative method of messaging between scripts and to demonstrate that regular C# events work just as they would be expected to for those that are familiar with them.