acting on other object during collision: SendMessage or GetComponent?

Common thing that happens in a million different games: A projectile hits something, and then does something to whatever it hits (damage, plays a sound, flashes color, etc). The reaction to the collision is different depending on the components attached to the objects involved, e.g. a piece of terrain wouldn’t take damage. From what I know, there are two ways to deal with this: SendMessage(), a bunch of GetComponent() calls, or checking tags followed by either method calls depending on the tag. All of these work, but also seem to have their own problems.

SendMessage() seems like by far the most elegant and simple. the sender doesn’t have to care what scripts the other object has, but if any of them have the method, it gets called. However this would result in a lot of SendMessage() calls that don’t do anything, plus I’ve heard that SendMessage() is very slow, so a high-action moment with dozens of collisions happening in a single frame could take a big performance hit.

A bunch of GetComponent() calls to achieve the same thing seems a lot less elegant, but might perform better. What I mean is something like

void OnCollisionEnter(Collision other)
{
    IDamageable damageable = other.gameObject.GetComponent<IDamageable>();
    if (damageable != null) {//do damage stuff} 

    IHitFlashable hitFlash = other.gameObject.GetComponent<IHitFlashable>();
    if (hitFlash != null) {//do hitflash stuff}

    //and so on for any other relevant scripts
}

This is a lot less elegant looking, but I understand that GetComponent<>() might be significantly faster than SendMessage(). However this still has the problem of making a bunch of unnecessary calls.

finally, there is the option of limiting either the SendMessage() or GetComponent<>() calls by checking the tag of the other object and making calls depending on what the tag is. However, then you run into the problem of needing a different tag for each different combination of relevant scripts which could be attached to the other object, and end up with a real messy pile of tags to keep track of.

SO my question is, which way do y’all prefer? or is there a pattern for this situation that I haven’t thought of or read about that trumps them all? I realize that the difference between each method might be so small that its better to leave to personal preference, but I’m the kind of person who likes to do everything the ‘best’ way :]

Avoid the tags mess. If you’re going to have a bunch of different special cases, then trying to use tags to solve this problem is going to become a nightmare.

I’d prefer the GetComponent approach for a few reasons

  1. Compile time checking (SendMessage won’t notice when you type “DamgeTargt”)
  2. Explicitly describes the code. SendMessage goes by method name - once your project grows to many thousands of lines of code, can you remember every component that has a method by that name? And when adding a new method to a class, you have to consider whether or not that method could be called in a SendMessage call. You might be able to remember this stuff while regularly working on the project, but good luck if you take a break for a few weeks.
  3. Performance. This really does depend on your game and the target platform, but GetComponent is going to be faster. I have seen wildly different results from others’ test results - some people say SendMessage is hundreds of times slower than GetComponent, others say it is only two or three times slower. Again, it comes down to platform and the specifics of your game.

The other option you might consider is having some kind EventRouter component inside of each gameObject. Then, your bullets collision code could be this:

void OnCollisionEnter(Collision other)
{
    var eventRouter = other.gameObject.GetComponent<EventRouter>();
    if (eventRouter == null)
        return;

    eventRouter.SendEvent<HitByBulletEvent>(new HitByBulletEvent(/*whatever data you need*/));
}

And the components on a gameObject can subscribe to events with the EventRouter, either through delegates or function pointers. I’d prefer function pointers

private Dictionary<Type, List<Action<Object>>>> m_EventSubscribers;

public void Subscribe<T>(Action<Object> action)
{
    if (!m_EventSubscribers.ContainsKey(typeof(T)))
        m_EventSubscribers.Add(typeof(T), new List<Action<Object>>());

    m_EventSubscribers[typeof(T)].Add(action);
}

public void SendEvent<T>(Object eventData)
{
    foreach(var action in m_EventSubscribers[typeof(T)])
    {
        m_EventSubscribers(eventData);
    }
}

The tradeoffs here are more memory and a bit more code, but you’ll get better performance than SendMessage without having to make a mess of all your collision code. Performance wise, I would not be surprised if this was competitive with GetComponent calls - it would depend on how GetComponent was implemented.

To make it faster, use an enum to keep track of event types, then you don’t have to call typeof constantly. But then you have maintenance of the enum.