Scriptable Objects and UI. How to connect them. Best practices

Hello,

I have a small question about the best practices to implement something from my scriptable object that’s holding the relevant data to the UI that shows everything.

Imagine a small menu where you can update a few things of your Monster, Health, Damage, Attackspeed, Range, …

What I thought to do is:

  1. Create a SO for each existing upgrade that contains all the necessary information from the name to some parameters for the cost calculation
  2. Create an UI prefab how one Upgrade should look like (Small reactangle, with some name, an image, a small description, a button for the upgrade itself, …)
  3. Create some UpgradeComponents Script that holds a list of objects of the Upgrade-SOs and has an Init() method that initiates one of the UI prefabs for each assigned Upgrade-SO and put’s it under some parent-UI with a vertical-layout-component.

What I really like on that approach is, that I can later just create, update or delete an Upgrade-SO and done. Every change is taken over in the UI automatically. Nothing to do.

What i really hate is, that you always see this incomplete state of your UI in the Unity-editor as everything is auto generated during runtime.

So alternatively I could just do the whole UI and afterwards assign there my SOs one by one for each UI element or even completely trash the SOs and put the values directly on the corresponding UI element. Then the UI looks always nice like it would be in the game later, but apart from that I am losing the the seperation of the data from the implementation.

So what’s Unitys best practice to handle problems like that.

You can always have some editor-only means to preview faux data.

I do this a lot with UI toolkit, namely as a code-gen most of my UI. I just put a check in the constructor if it’s play mode or not, and if not, insert some faux information.

For example, for my player’s stats display, a little bit of code:

[UxmlElement]
public partial class PlayerStatsWindow : RetroWindow
{   
    public PlayerStatsWindow() : base("STATUS")
    {
        if (Application.isPlaying == false)
        {
            this.BuildMockDisplay();
        }
    }
   
    // the rest of the visual element

    private void BuildMockDisplay()
    {
        var healthStat = new PartsTreeStatsComponentHealth();
        healthStat.IncreaseStatCapacity(100);
        this.AddStatsComponentElement(healthStat);

        var energyStat = new PartsTreeStatsComponentEnergy();
        energyStat.IncreaseStatCapacity(100);
        this.AddStatsComponentElement(energyStat);

        var fuelStat = new PartsTreeStatsComponentFuel();
        fuelStat.IncreaseStatCapacity(100);
        this.AddStatsComponentElement(fuelStat);
    }
}

Let’s me preview what they look like in the UI Builder:
9642971--1371272--upload_2024-2-14_21-17-3.png

Not sure what the approach would be with uGUI, though, to be honest.

1 Like

I wouldn’t ever be so bold as to call it “best practices” or anything, but my Datasacks package does exactly this.


Datasacks is presently hosted at these locations:

https://bitbucket.org/kurtdekker/datasacks

If you are using any version of Unity later than Unity2021, it may be necessary to add this line to your Packages/manifest.json, or add it via the Package Mangler:

“com.unity.ugui”: “1.0.0”,

1 Like