How do you understand the Observer Pattern?

Did this lecture make sense to you? What new knowledge did you take away?

I understand it as a very flexible and expandable way to let classes react to certain things without the need of tight dependencies due to the fact that I can broadcast any event and let anyone who is interested in this event subscribe to it. The beauty is that the broadcaster does not care about it’s subscribers, which makes the system maintainable and expandable and easy to refactor. I use it a lot for bossfights for example. When a boss hits a certain life threshold, he broadcasts an event and I can try out different things and see what I want to do with it. Like having enemy spawners that start to spawn adds on event call or make rocks falling from the top of the level.

1 Like

I haven’t used the observer pattern much before, but I definitely see how useful it can be!
I had to pause the video a few times to think about how it was all working together, but I think you did a great job of explaining it all (and doing the FakePlayableDirector to help us see what’s going on behind the scenes).

1 Like

@sampattuzzi The link seems wrong to me (it point me to the course homepage), anyway I want to suggest a better implementation (in terms of design) that’s the publisher/subscriber pattern (event bus in other disguise)

to see differences between observer and pub/sub there is a nice article here that explain it without going into implementation details: https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c

Giving my background (C# dev since inception and Java knowledge) this pattern (pub/sub) is really useful in my daily job (unluckily it’s not gamedev heh) to decouple completly subsribers from publisher, so a suscriber just have to publish in a “channel” that something has appened and then any other component aware of that specific event (without knowing who’s the publisher) can consume that event.

I don’t know unity implications in terms of performance, but about design this is a big relief, any component that have to consume any event has just to have a reference to the broker class (e.g. EventBus)

In my implementation any publishable event is a type itself (a class) and the consumer has just to know that it exists to be able to subscribe and receive it whenever anyone publish that event type.
No need to declare events anywhere, just declare a type and then you are able to publish it anywhere.

I leave here just an example of a simple eventbus implementation I did for a project I’m collaborating with to see how it can be used/implemented

My Unity knowledge isn’t high, maybe unity has its own message bus (I think I remember about unity messaging in the past, but it was just a string, not typed and not as powerful as pub/sub) anyway would be nice to know how an event bus works in Unity

2 Likes

I was looking at that article indeed as I prepared this lecture. We are definitely using the observer pattern but sometimes the eventbus is a good way to go. But I think it can be overkill for situations where the observer pattern would do.

Loved the content of this lecture. Very informative and easy to follow. Creating FakePlayableDirector first was very helpful.

1 Like

Hey Sam, I found this lecture to be a whole lot more challenging than any of the previous ones. I’m reading up on delegates, observer pattern, etc. using the links you provided.

I’m wondering about this line though:
GetComponent().onFinish += EnableControl

What datatype is “EnableControl”? If this were a string it would make more sense to me. I can’t grasp how the computer is going to use this unless it’s being used as a string to call it directly, kind of like how you would use SetFloat(“something”, 1f). That accesses the property “something” on whatever you’re trying to change.

Is a function itself a datatype? Is it a different datatype if it has () after it? I’m not sure if I’m even asking the right questions, but this just seems really odd to me. Any help would be appreciated.

Function signatures can match the expectation for event/delegate data types, I don’t know if they are having a type themselves, or even if that’s really important.

You can reference the event/delegate definition (when you are creating them) to see what they expect.

In the case of a delegate you have something like:

public delegate void SomeFunc();

From this you can create instances of SomeFunc that are assigned a method with a matching signature. C# will refer to the type of that instance as the delegate’s name, so create/assign a SomeFunc instance, and it’s type will be SomeFunc.

In this lecture (took a while to hunt it down btw ! that link in the original post does not take you there anymore), they are using events with an Action delegate. Like this:

public static event Action<float> SomeEvent;

Which you later see being assigned with the += accumulation syntax. These too have to meet a specific signature format, but they are declared and typed differently, partly because of generics and partly because Actions are provided by the language instead of yourself. So in this case, it has the type: System.Action`1[System.Single], which if I recall equates to a “System.Action with 1 parameter, that is a float/single”.

Try it: https://dotnetfiddle.net/bjFwX7

1 Like

EnableControl in this case is a references to the method EnableControl that is elsewhere in the script…

public void EnableControl()
{
    ///enable the controls
}

So the onFinish+= EnableControl is simply passing the address of the method EnableControl to the onFinish event. It can get confusing, since we don’t use the () or strange pointer types like ^ or * (C++). In C#, all that is require/allowed is the name of the method. The signature of the method must match the signature of the event, but the compiler handles that.

1 Like

I’m starting to understand this pattern better after going through part 2 and using it for updating the UI. I’m thinking I would be able to change the buff/debuff system I have in place to use the observer pattern. Currently, each time a stat like Attack or MaxHealth is needed, the code runs recalculates from BaseStats and runs through all of the modifiers.

With the observer pattern, when an effect is started/ended, it would notify the scripts that care about stats and they’d be able to update themself. Given that scripts like HealthBar are already checking BaseStats every frame, I’d hope this doesn’t introduce any stutter as long as there’s only a few scripts subscribed.

I think this would be very similar to how Equipment and Inventory invoke their action when they’ve been updated.

Edit: I think I got it sorted. One of the Discord users had posted a video of using a Sciptable Object for Unity Events, so I went with that method so that I wouldn’t need to tangle the namespaces from Health to Combat and Inventory. Reading through the article posted earlier in this thread, it seems like a pub-sub pattern instead of a simple Observer? Either way, now whenever a Stat Modifier script makes a change, it raises an event that has a receiver to call the relevant recalculations:

Going back through also let me streamline how the combat system drops buffs, so it was worthwhile all around.

1 Like

Real life analogies?

Pub/sub:

Tuning into a radio station to listen to it. The radio station doesn’t know (except via popularity polls after the effect) how many listeners there are - maybe there are none and the DJ is screaming into the void - but it broadcasts anyway, in case someone should want to listen and pay attention to it.

Observer:

Subscribing for email notifications. The subscriber often (hopefully) gets to choose what types of topics they will receive updates on, and the company/service sending out the notifications is now obligated to only contact those email subscribers it knows about and upon topics on which they are interested. **

.

** A little idealistically perhaps given we know how spammers operate with no regard to subscription opt-in choices. I’m referring to a legit business you would subscribe to such as your bank, a game dev education company, etc., not spammers.