Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Observer pattern or conditional logic in Update method for controlling UX visibility

Discussion in 'Scripting' started by Raidenwins, Mar 22, 2020.

  1. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    So i have a bit of a conundrum at the moment. I have a piece of UX (in a Canvas panel) that needs to be shown or hidden, depending on some condition in gameplay logic. Up until now I've been doing this with conditional logic in the Update method of a MonoBehaviour, i.e. something like this:

    Code (CSharp):
    1. public GameObject uxPanel;
    2.  
    3. void Update()
    4. {
    5.     if (uxShowCondition)
    6.     {
    7.         uxPanel.gameObject.SetActive(true);
    8.     }
    9.     else
    10.     {
    11.         uxPanel.gameObject.SetActive(false);
    12.     }
    13. }
    Simple and it works. The problem is that this conditional logic gets called every single frame. I know Unity is optimized for running these Update methods every frame and all that, but still, it seems like an overhead.

    An alternative to the above, that I discovered recently, is to trigger an event in the gameplay logic and then show/hide the UX in question in a class that handles said event, using the Observer pattern. That would look like this:

    An abstract subject class, i.e. a template for something that is being observed:
    Code (CSharp):
    1. public abstract class Subject
    2. {
    3.     public event Action OnGameplayEvent;
    4.  
    5.     public virtual void Trigger()
    6.     {
    7.         OnGameplayEvent?.Invoke();
    8.     }
    9. }
    A concrete implementation of the abstract class above:
    Code (CSharp):
    1. public class MySubject : Subject
    2. {
    3.     public override void Trigger()
    4.     {
    5.         base.Trigger();
    6.     }
    7. }
    An observer class that listens for events from a subject class:
    Code (CSharp):
    1. public class MyObserver : MonoBehaviour
    2. {
    3.     private Subject mySubject;
    4.  
    5.     void Start()
    6.     {
    7.         mySubject.OnGameplayEvent += Subject_OnGameplayEvent;
    8.     }
    9.  
    10.     private void Subject_OnGameplayEvent()
    11.     {
    12.         // Do something here to show or hide the UX panel
    13.     }
    14. }
    And this is the gameplay class that triggers the event. I am skipping constructors and initializers for the sake of brevity:
    Code (CSharp):
    1. public class MyGameplayClass
    2. {
    3.     Subject mySubject;
    4.  
    5.     void GameplayLogic()
    6.     {
    7.         ...............
    8.         ...............
    9.  
    10.         mySubject.Trigger();
    11.     }
    12. }
    So my question is, which of the two methods above is better for showing and hiding a piece of UX in a Canvas panel? The first one is definitely simpler, but it runs every frame, which may or may not be a problem. I am not sure about that. The second is more complex, but it only runs one time, i.e. when the gameplay logic triggers the event.
     
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Definitely the first one.
    But there is no need to make such if blocks, what you need is simply
    Code (csharp):
    1. uxPanel.gameObject.SetActive(uxShowCondition);
    However, you have to be mindful about the amount of objects whose Updates are called per frame.
    If you have dozens or hundreds of Updates running all at once (benchmark this anyway, you want to stay on top of things, instead of becoming superstitious of any numbers), consider a smarter approach like you already described.

    The actual amount of calls is your only concern. It will hurt your game if it accumulates past a certain point.

    It doesn't hurt if it's redundant here and there, just imagine that your screen is constantly refreshing dozens or maybe hundreds of times per second, mostly needlessly right? So don't overdo simple things. Everything's on a clock, vibrating anyway.

    edit:
    Oh, and depending on what exactly goes under the hub, you might want to introduce a safety switch in between by comparing the previous uxShowCondition state with the current, and update the API only when necessary. This will double your memory requirement, but might help Unity (or even a compiler) cache some of these values and proactively using this cache instead of missing all the time, we don't know really what else might be going on. << my final recommendation, unless you need a dedicated system to cull unnecessary Updates

    Also if you're doing this on a MB (and you do, since you call Update), and this var is serialized, you can rely on OnValidate() and do it like that instead, which is probably the best thing in your case.
    Code (csharp):
    1. void OnValidate() {
    2.   uxPanel.gameObject.SetActive(uxShowCondition);
    3. }
    edit2:
    Nope, sorry, just checked, OnValidate works in editor only.

    edit3:
    In any case, do the simplest thing if this is a lone culprit, but I'm all in for the more elegant solution if you're about to use it throughout your entire project. If you're not lazy it'll help you stay in control in the long run.
     
    Last edited: Mar 22, 2020
  3. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Or, frankly, you can sort this out by checking this immediately, so no need for another var.
    This way you simply minimize needless tinkering with the API values, and the check itself is extremely fast.
    Code (csharp):
    1. if(uxShowCondition != uxPanel.gameObject.activeSelf) {
    2.   uxPanel.gameObject.SetActive(uxShowCondition);
    3. }
     
  4. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    I am already using the more elegant solution in another part of the UX, so this is not the lone culprit. Also, my code base has grown to a significant size so I do probably have at least a couple dozen Updates running in the particular scene where this UX is.
     
  5. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    In that case, make it smart all the way. Reduce your Update count as much as you can.
    I'm glad you're knowledgeable about these things, so that we can keep this conversation short.

    In essence, you do want to reduce the Unity messages (that's how they call Start, Update, and such) being tied with the core system whenever you can. But they do not impose drastic penalties in humble and/or typical scenarios. You could, in theory, make a really big game without paying any attention to this cost whatsoever.

    However, if you can, circumvent the system completely. It will only be easier for you if you start implementing monstrous UIs with many moving parts (in terms of states), and your game will only behave better for not having to grind through so many Updates per frame. It's hard to say how much though, but if you're already capable of doing it, better be safe than sorry.