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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Decouple UI elements

Discussion in 'Scripting' started by Flynn_Prime, Oct 7, 2019.

  1. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    383
    Just wondering how people go about decoupling their UIs from other code.

    Code (CSharp):
    1. public class SomeClass : MonoBehaviour
    2. {
    3.     public float someInt;
    4. }
    5.  
    6. Public class SomeUIClass : MonoBehaviour
    7. {
    8.     public Text someIntText;
    9. }
    With a system as above, I am struggling to understand how to decouple the two scripts. What if SomeClass is on multiple scenes, some of which won't have a SomeUIClass?

    I plan to use UnityEvents to actually update my UI elements, but other than constant null checks for SomeUIClass I'm not sure on the best way to go about it.
     
  2. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,887
    The UI class will need to access the data somehow. It can receive it in multiple ways.

    Most would just have events it listens to, or just reference the SomeClass directly. You could centralise the events to simplify the spaghetti, but then everything would be coupled to that.

    The only way to decouple would be using something like ScriptableObjects. Make a FloatReference type, then you can click-drag a ScriptableObject asset into the slot. The downside is that you can't make them dynamically, so they would have to be premade in the editor.
     
    Flynn_Prime likes this.
  3. Thibault-Potier

    Thibault-Potier

    Joined:
    Apr 10, 2015
    Posts:
    206
    I'm using delegate to throw event when i update a value.

    My UI scripts would subscribe to this event to do something about that value change. That way, i can have many -or zero - ui scripts that wants to know about this value without having to touch anything in my class holding the value.

    Of course all those hypothetical UI script need a reference to the class holding the value, but that's how it works ;)
     
    Flynn_Prime likes this.
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    This is a big topic and subject to a lot of debate, but here's my general philosophy on the subject.

    The single most important guiding principle is one-way dependency: The in-game object should never have any knowledge of the UI. If you imagine that you need to rebuild the UI from scratch (say, when porting from desktop to mobile/touch), you should be able to delete the entirety of your UI and your in-game objects will compile just fine with no changes whatsoever, and if there are different UIs or no UI in a given scene, the in-game object will carry on fine.

    However, the reverse does not need to be true - the UI can depend on the in-game object's code to exist, and (more or less) depend on their existence in the scene. (Though if it's possible for a given object to not exist, the UI should be designed to fail gracefully.)

    So applying this to games: Your "life meter" script can have a direct reference to the player, and should be responsible for setting its own value to the player's life. For values like this, this should be pretty straightforward.

    For events (say, the life bar flashing when the player receives damage), this is best handled by delegates and events. The player object has a OnDamageReceived event, and the UI can subscribe to that event. The player object still has no idea the UI exists, but it has all the hooks needed for that UI - or any UI - to plug into.
     
  5. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    383
    Thanks for the replies guys. Delegates is normally the way I would go, and if that's as decoupled as it can get than so be it.

    @StarManta I completely agree with that philosophy, and was hoping there was a way to have that "hook" outside of my player objects etc.

    Yet again, I will use C# events over UnityEvents. They seem so amazingly useful, but in my use cases they are always trumped by C# events and delegates!

    Thanks again guys.
     
  6. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    383
    On a second read, this point you made stood out to me. I would have thought you would change this value using your delegate/event. How would you update this value within your UI script, whilst keeping the player script unaware of the UIs existence, without using an event?
     
  7. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    Well, you can use an event here too, but (assuming it's not too performance-intensive) you can just have the life meter set itself to thePlayer.life each Update.
     
    KaOzz and Flynn_Prime like this.