Issues in implementing the observer pattern in Unity?

Hey everyone,

I haven’t studied programming in school so this question might seem a little strange to some people here, but I need your help understanding what I’m doing wrong and since I can’t ask a question on stackoverflow because my questions always get down voted for some reason but I’ll try my best to explain everything.

So I’m trying to use the observer pattern in my game since it decouples the scripts from each other and generally make the code more efficient and less prone to errors so…

I have 2 scripts the first one is called Scanner and the second is one is called Product.

Scanner script:

using UnityEngine;
using System;

public class Scanner : MonoBehaviour
{
    public static Action<Color> OnProductHit;

    private Color scannerColor = Color.yellow;

    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Product"))
        {
            OnProductHit?.Invoke(scannerColor);
        }
    }
}

Product code:

using UnityEngine;

public class Product : MonoBehaviour
{
    private Color productColor = Color.yellow;

    private void OnEnable()
    {
        Scanner.OnProductHit += ScanColor;
    }

    private void OnDisable()
    {
        Scanner.OnProductHit -= ScanColor;
    }

    private void ScanColor(Color _scanColor)
    {
        if (productColor == _scanColor)
        {
            Debug.Log("Colors match");
        }
        else
        {
            Debug.Log("Colors don't match");
        }
    }
}

As you can see from the Scanner script I’m calling the event OnProductHit from the OnTriggerEnter method, and the Products scripts subscribe to this event in the OnEnable and in the OnDisable methods but since this event OnProductHit is STATIC which means if I have more than one scanner object in the scene the method ScanColor() will be called as many times as how there are scanner objects in the game.

I watched this cool Observer pattern video on youtube and I asked him the same question and I got this reply from him.

I think his solution might work but I don’t think its good enough since all the events will still run and then check for the condition whether is true or false, like what if I have 15 objects in the scene the again all of the static events in these objects will still run 15 times and check for the condition inside of them making the code extremely inefficient.

I know that there is something that I don’t understand or better solutions using the observer pattern, so if anyone have an idea this question please do share it here.

Thank you.

That’s the idea of an event. You’re basically saying “to whom it may concern: Do this”. And anyone who is subscribe to the event will reply. If you want to be more precise, you’ll need to get the exact script on the exact object you want to run.

In your case, you could do so, though your decoupling would be gone, of course. Simply get the GameObject the Collider from the Trigger method is attached to and GetComponent the script you want to run. Depending on what you are planning to do with this, the decoupling might not actually be important at this stage, so you’d be fine.

Then again, 15 objects isn’t that much either. Might seem like a waste, but I don’t think it’s that bad.

Edit:
Also, this is another way to get only the right object to respond:

It seems to me that your setup is reversed. I believe what you should (and probably wanted to) implement is this:

  • If a product is hit, scan the product that was hit

What you actually implemented is

  • If any product is hit, scan all products (scanner invokes scanColor on all subscribed products)

An observer pattern may not be the best approach for this particular problem (if you wanted to do what is described under the first bullet).

I would tend to agree that the observer pattern may not be needed here. By using OnTriggerEnter you already have a reference to the product that was scanned via the collider input parameter. Which makes it easy to call a function on that object and (I think) more straight forward than invoking a static event that then has to get sorted through by every product.

I don’t think in this case the coupling/decoupling is something to worry about. The scanner gets a reference to the product in a very neat and tidy way. That reference is then lost after the OnTriggerEnter function does its thing.

Edit: One more thought. You may want the product itself to then invoke an event “IGotScanned” which can send a reference of itself. This could allow UI elements (or other systems) to know that a particular product was scanned. This would be a good use of the observer pattern.