0
\$\begingroup\$

I'm trying to understand the Send Message Function but I'm a bit confused. Currently I have a game object character and I want it to send a message to the main body if one of its children hits something.

On the Child object, I have:

void OnTriggerEnter2D(Collider2D col) {

    Destroy(col.gameObject);
    Debug.Log(this.name + " Hit!");
    gameObject.SendMessage("PlayerHitting", this.name);

On the Parent Object, I have

public void PlayerHitting(string Move) {
    Debug.Log("Player hits with " + Move);
}

The Destroy code and Debug.Log on the Child work, so it is colliding, but I keep getting an error that there is no reciever.

SendMessage PlayerHitting has no receiver!
UnityEngine.GameObject:SendMessage(String, Object, SendMessageOptions)

I don't quite understand how to use Send Message to send messages and notices between objects. Can someone explain?

\$\endgroup\$
2
  • \$\begingroup\$ If the message is meant to travel up the hierarchy from a child to a parent, did you want SendMessageUpwards? \$\endgroup\$
    – DMGregory
    Jan 1, 2020 at 11:45
  • \$\begingroup\$ I more want to be able to send a message to any object. I'm just using this parent child object in a test project to try and understand it because I'm a bit confused by the documentation. I did try SendMessageUpwards though and that didn't work either. Same error. \$\endgroup\$
    – JSparks
    Jan 2, 2020 at 3:41

1 Answer 1

1
\$\begingroup\$

I'm unable to reproduce this problem in a new project. I've set up a hierarchy like this:

  • Parent object, "Body"
    Rigidbody2D
    TriggerListener (implements the "PlayerHitting" method)

    • Child object, "Collision Rep"
      BoxCollider2D
      TriggerReporter (calls SendMessageUpwards("PlayerHitting", name) in OnTriggerEnter2D)

When I move this object over a trigger shape, the listener script on the parent correctly reports the message received from its child "Collision Rep" and I do not see any "no receiver" errors.

So I'm not sure why it's causing this error in your scene. It could be that my example differs from what you're doing in some way. Can you edit your question to include a minimal complete verifiable example, so users can reproduce the problem exactly as you're seeing it?


However, I think the best fix here is to not use SendMessage at all.

This is a legacy feature in Unity, and modern versions of the engine have much better tools for solving similar problems. Some of the issues with SendMessage:

  • It's slow, because it requires searching and reflection. Each time the message fires, Unity has to walk through each component on the object, check if it has a method matching this string, then invoke the method through reflection if so. For the "Upwards" version, it has to repeat this for every object up the hierarchy.

  • It's error-prone, because it makes your code "stringly typed": one typo in the name can make the message fail to find its recipient, in a way that's invisible to your compiler and refactoring tools. The message will just fail and you'll be scratching your head as to why (much as you are now), with no tool support to help you find the issue. If you later choose to rename the method, your IDE can automatically update all direct calls to it no problem, but will miss updating any SendMessage strings, breaking all your messaging.

  • It's difficult to debug, due to a combination of the two points above. Your control flow is now at the whims of the search routine and string comparisons, which tends to leave you with a very confusing call stack when you're trying to diagnose why a particular method is being called and where it's being called from.

For a situation like this, where there's a fixed relationship between the message-sender (the child) and the receiver (its parent), I'd recommend using Events.

First, I'd make an enum for your different move types, so these moves are strongly-typed instead of stringly-typed. This ensures any typos get caught at compile time, and they're faster to compare, switch over, map to arrays of outcomes/resistances, etc.

public enum MoveType {
    Punch,
    Kick,
    Headbutt
}

Now your trigger scripts can expose a dropdown field to select their move type, and an event to fire when their collider hits something:

using UnityEngine.Events;

public class MoveTrigger : MonoBehaviour {
    // Define a type of event that has a MoveType argument.
    [System.Serializable]
    public class MoveEvent : UnityEvent<MoveType> {}

    // Expose a MoveType drop-down in the inspector.
    public MoveType myMove;
    // Expose an event to fire when the trigger hits.
    public MoveEvent onTriggered;

    // When our trigger is activated, destroy what activated us and report to whoever is listening.
    void OnTriggerEnter2D(Collider2D other) {
        Destroy(other.gameObject);

        if(onTriggered != null)
            onTriggered.Invoke(myMove);
    }
}

Now in the inspector, you can wire up your MoveTrigger instances to call the parent's OnMoveTriggered handler method:

Example of inspector showing On Triggered event wired up to parent's OnMoveTriggered method

Advantages of this approach:

  • No runtime searching / reflection: you've told the engine exactly where to address the message, so it can send it there directly at very little cost.

  • No string matching: the references are explicit and visible to the compiler and refactoring tools, so they can catch typos before you run, and enforce consistency in naming as your project evolves and multiple contributors make changes.

  • Flexible: you don't need to re-write your scripts when you want to direct the trigger message to a different recipient, or select just one recipient out of several that happen to have a method of the same name. You can control this precisely through the list of addressees in the event, customized per-instance.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .