How to send an interface message?

How to send an interface message?

I come from Unreal, still using it. There, how it works is like this: 
Let's say you as a player have a laser gun or something, and when shooting, you use raycast to see if you've shot anything, and if yes, you send an interface message (let's say Damage() function of IDamagable interface) to the hit result, if any.
Now, if the hit result implements that interface and has a Damage() function, it gets damaged. (And of course, if it doesn't implement the interface, nothing happens.)
But in Unity, I don't know how to send that interface message to the hit result (saying "if you have IDamagable implemented, call your Damage() function), but if it doesn't have it, that's fine either.
I've watched many videos on interfaces in Unity, but I still don't know how to achieve the same as in Unreal.

Solutions/Answers:

Answer 1:

Method 1 – Unity UI Event System

Thanks to Byte56 for pointing out that there’s a new approach to messaging in Unity, which is much more similar to what the asker describes in Unreal.

One quick heads-up: This approach is quite verbose compared to what I’m used
to. If you just need a quick way to call a method on another object,
without all the loosely-coupled robustness of the Event System, scroll
down and I’ll include some other options.

You’d start by creating a new C# script that defines an interface inheriting from IEventSystemHandler:

using UnityEngine.EventSystems;

public interface IDamagable : IEventSystemHandler
{
    void TakeDamage(DamageEventData damageData);
    void ToggleInvulnerability();
}

Each method you define in this interface becomes a message that you can send. If the message needs data, then we’ll also need to define our data object by extending BaseEventData:

using UnityEngine.EventSystems;

public class DamageEventData : BaseEventData
{
    // This doesn't have to be in this scope, but I didn't want to create
    // yet another script file for it, so this seemed the most logical place
    public static readonly ExecuteEvents.EventFunction<IDamagable> takeDamageDelegate
    = delegate (IDamagable handler, BaseEventData data)
    {
        var casted = ExecuteEvents.ValidateEventData<DamageEventData>(data);
        handler.TakeDamage(casted);
    };

    public float damageAmount;

    public DamageEventData(EventSystem eventSystem, 
                           float damageAmount,
                           ) : base(eventSystem)
    {
        this.damageAmount = damageAmount;
    }
}

What’s up with that delegate? Since our interfaces can contain multiple methods, Unity’s Event System uses a delegate to link up the right handler method and provide it the correct data type. You can do this inline, but having it defined statically somewhere is a bit friendlier to the garbage collector. (Example borrowed from this Event System walkthrough)

Next, ensure any script component you want to receive these messages implements your interface:

public Explodable : MonoBehaviour, IDamagable
{
      public Transform explosionPrefab;
      public float damageThreshold = 0.5f;

      public void TakeDamage(DamageEventData damageData)
      {
          if(damageData.damageAmount > damageThreshold)
          {
              Instantiate(explositionPrefab, transform.position, transform.rotation);
              Destroy(this);
          }
      }

      public void ToggleInvulnerability()
      {
          // No-Op: I declare by fiat that explodable objects are never invulnerable.
      }
}

Finally you can send this message to all scripts on a given GameObject that implement your interface:

if(Physics.Raycast(ray, out hit))
{
   DamageEventData data = new DamageEventData(
                                EventSystem.current,
                                damageAmount);

   ExecuteEvents.Execute<IDamagable>(
                                hit.transform.gameObject,
                                data,
                                DamageEventData.takeDamageDelegate);

   // If your message has no data - eg. the ToggleInvulnerability() method,        
   // you can replace that delegate in the last argument with:
   // (handler, data) => handler.ToggleInvulnerability
}

Whew, that was a bit of a marathon. You can use other methods of the ExecuteEvents class to search the hierarchy for a recipient of the message, or broadcast a message up the hierarchy. The docs say you can also recursively send a message down the hierarchy, but I haven’t found the method for that yet.

Method 2 – Just grab the component you need

If your message/interface describes a type of behaviour that maps well to a single component, then you might as well make it a component and address it directly:

var damagable = targetObject.GetComponent<Damagable>();
if(damagable != null)
    damagable.TakeDamage(damageAmount);

Strictly speaking this is more tightly coupled – but for a lot of gameplay we don’t need the industrial grade decoupling the Event System was introduced to handle (primarily for UI widgets). The separation of a Damagable component MonoBehaviour from the rest of an object/character’s behaviour logic scripts already decouples it enough for our purposes, I would claim.

You can also do this with interfaces, if you have multiple components that care about the same kinds of messages:

var damagables = targetObject.GetComponents<IDamagable>();
foreach(var damagable in damagables)
    damagable.TakeDamage(damageAmount); 

GetComponent has some performance cost, so try to cache these component references if you’re using them frequently. For things that happen only a handful of times per frame though, it’s still quite reasonable to use.

Method 3 – Old SendMessage approach

This is how it worked in Unity 4 & earlier. Because the Event System in Method 1 is described as a replacement for this, don’t expect this to stick around in future versions.

But if you just need something quick & dirty, it’s hard to get moreso than this:

targetObject.SendMessage(
                       "TakeDamage",
                       damageAmount,                     
                       SendMessageOptions.DontRequireReceiver);

Then in your recipient scripts, you’d include a method with the name TakeDamage:

public void TakeDamage(float damageAmount)
{
    hp -= damageAmount;
    if(hp <= 0)
      Die();
}

Note that this method doesn’t need to be public – Unity calls it using reflection with the name you pass, no interfaces or anything – but I like marking it that way to remind myself that it’s called externally.

Note that this reflection and string manipulation isn’t all that cheap in terms of performance, so definitely don’t use this for something that’s running many times every frame. This approach also tends to obfuscate the call stack when debugging, and needs to be manually refactored if you ever change the names of your methods. For these reasons it’s commonly considered an anti-pattern, so use it very cautiously if you use it at all.

References