Search Unity

Question UI not tied to player or GameContoller

Discussion in 'Game Design' started by Dark_Seth, Aug 19, 2022.

  1. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    140
    Hi All. Hope you doing well.

    I am getting to a point as normal I feel where I have the usual. Health Script on the player, 2 Slots for weapons that the player switches between . GameController with a script that contains Resources the player has collected.

    I have done many prototypes and always UI design ( Not Look ) has not been the focus. Always bind a GUI element on the GameController etc. Move to a new scen and Null errors galore.

    I want to explore this by making a proper system I can re-use and expand on.

    What do you guys do and how did you implement this?

    Did read something about receiver and signals? But got no idea where to start.

    Thanks all! Would love to get some info, Videos articles!!
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,930
    There's a lot of patterns out there about sending and receiving signals. Publisher/Subscriber, Event Broker, etc. Most if not all of them make use of delegates at the end of the day, so I would look into learning about those.

    But in the context of Unity it's often easier to use Scriptable Objects.

    For example, you could have a scriptable object to hold onto your players stats:
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. [CreateAssetMenu(menuName = "Player/Stats Object")]
    5. public class PlayerStatsObject : ScriptableObject
    6. {
    7.     [Serializefield]
    8.     private int playerHealth;
    9.    
    10.     [SerializeField]
    11.     private int playerMana;
    12.    
    13.     public event Action<int> OnPlayerHealthChanged;
    14.    
    15.     public event Action<int> OnPlayerManaChanged;
    16.    
    17.     public void SetHealth(int health)
    18.     {
    19.         playerHealth = health;
    20.         OnPlayerHealthChanged?.Invoke(playerHealth);
    21.     }
    22.    
    23.     public void SetMana(int mana)
    24.     {
    25.         playerMana = mana;
    26.         OnPlayerManaChanged?.Invoke(playerMana);
    27.     }
    28. }
    Both the player and the UI can have a reference to an instance of this object without ever knowing the existence of one another. The UI can just subscribe to these delegates, and the player can call the methods. This also doesn't care about which scene the UI or player are in (ideally they would be in their own dedicated scenes), as the SO lives at the asset level.

    Notably anything else can listen to this object's delegates, but anything can also affect it's health, too, so, double edge sword there.

    Obviously it's just a quick example and a real world case would be much more robust. Just illustrating a possible solution.
     
    tmonestudio and Dark_Seth like this.
  3. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    140
    Ohh. Thank you. Give me some direction!
     
  4. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    Game Programming Patterns

    i'm always recommending this book, it has helped me a lot^

    just a couple threads down i was asking similar questions and there are pretty thorough and human-friendly answers which are giving an overview of composition design.

    the key thing I am focusing on as I am refactoring my nearly finished first game is consistency. If there is not consistent pattern to my code, it ends up feeling more complicated than it ought to be, and that causes fatigue when dealing with it.

    By consistency I mean things like:

    Does one class tell another what to do, or does a class make decisions for itself?
    In either case, why?

    I am trying to generalize different classes into types. There is types that listen, types that direct, types that report... I am trying to make sure that the way these different types communicate with each other is always following the same pattern.

    One general rule I've settled on is that just by default, if one class is going to communicate with another, it will be via an interface. This way I am not building up classes which hard-reference each other. So for each class which may need to be directed by another, or listen to another class, it subscribes to an interface accordingly.

    I am not a professional programmer so the way I am doing things might be a little unorthodox, but I am thinking about interfaces like radio channels. If somebody wants info about GameState for instance, they subscribe to the GameStateReportingChannel interface. If somebody wants to direct some UI panel to show/hide an element, they call on the UI_VisibilityChannel interface.

    That's just how I am getting some consistency in class->class communications.

    It is probably going to help if you can sketch out your general game loop and then try to fill in smaller details from that. Something like this helps me a lot:
    upload_2022-8-22_11-57-15.png
    if i try to jump into details without having broader picture pretty well mapped out, I find that I end up doing things in very inefficient ways.
    And of course, don't spend too long on initial plan because it won't take into accoutn all the things that you don't know yet, so I think its good to make a quick attempt once you have an inkling of a plan in mind, just to figure out the weird little details that you aren't planning for, then you can update the original plan and take a better shot on the second go.


    edit:
    this guy is showing some practical examples of design patterns in unity. I am working in unreal currently but i still use this as a reference sometimes because its hard to find good, plain language practical examples.
    Observer Pattern - Game Programming Patterns in Unity & C# - YouTube
     
    Last edited: Aug 22, 2022
    Dark_Seth likes this.
  5. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    140

    Real good info. Will take a read. What a post! Thank you!!!
     
  6. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    140
    Thanks all. Got a System that is working that goes well with my Scriptable objects .
     
    angrypenguin likes this.
  7. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Cool. What'd you end up doing, and what are its strengths and weaknesses? That kind of thing could be super useful for anyone who finds this thread in the future. :)
     
  8. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    140
    Used Signal System. Health etc is on scriptable objects. Have a script on UI that listenes for any signals ans then reads the Scribtable objects.
     
  9. emilian-s

    emilian-s

    Joined:
    May 11, 2014
    Posts:
    19
    Hello @spiney199
    You example is excelent, but I have a small issue with it.
    How can one broadcast `OnPlayerHealthChanged` when `playerHealth` is changed from the Inspector?
     
  10. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    If that's the only thing which is there to change, and/or there is no issue with it being called redundantly, then you can look at OnValidate().

    More generally, the only reliable way I'm aware of is to write your own custom Editor for that type. That could be as simple as a wrapper around the default Editor which stores the initial value of playerHealth, and raises the event afterwards if it is different.
     
    spiney199 likes this.
  11. emilian-s

    emilian-s

    Joined:
    May 11, 2014
    Posts:
    19
    But the custom Editor has access to the SerializedObject of PlayerStatsObject. Is it possible to call methods on SerializedObject?
     
  12. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
  13. emilian-s

    emilian-s

    Joined:
    May 11, 2014
    Posts:
    19
    I've read that, but didn't help, or didn't see the usage.
    Now that you pointed me to the same documenation, I've searched a bit more and I found that I can enforce the type on .targetObject, resulting in PlayerStatsObject.
    That was it, right? :(
     
  14. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Spot on. :)
     
    emilian-s likes this.