Skip to main content
added additional example demonstrating the combination of static and dynamic data
Source Link
Kevin
  • 7k
  • 1
  • 12
  • 32

EDIT: If you need to display a combination of static and dynamic data, one way is to wrap the static data in a container:

public interface IWeapon {
    string DisplayName { get; }
    float Damage { get; }
}

public class Weapon : ScriptableObject, IWeapon {
    [SerializeField] private string displayName;
    [SerializeField] private float damage = 10;
    //etc

    public string DisplayName => displayName;
    public float Damage => damage;
    //etc
}

public class DamageScaledWeapon : IWeapon {
    private Weapon weapon;
    private float damageMultiplier = 1;

    public string DisplayName => weapon.DisplayName;
    public float Damage => weapon.Damage * damageMultiplier;
    public float DamageMultiplier { get => damageMultiplier; set => damageMultiplier = value; }
    //etc

    public DamageScaledWeapon(Weapon weapon) {
        this.weapon = weapon;
    }
}

//Can display a Weapon or a DamageScaledWeapon 
public class WeaponPresenter : MonoBehaviour {
    [SerializeField] private Text nameText;
    [SerializeField] private Text damageText;
    //etc    

    private IWeapon model;

    public IWeapon Model {
        get => model;
        set {
            model = value;
            Refresh();
        }
    }

    private void Refresh() {
        if (model != null) {
            nameText.text = model.DisplayName;
            damageText.text = model.Damage.ToString("N1");
            //etc
        }
    }
}

EDIT: If you need to display a combination of static and dynamic data, one way is to wrap the static data in a container:

public interface IWeapon {
    string DisplayName { get; }
    float Damage { get; }
}

public class Weapon : ScriptableObject, IWeapon {
    [SerializeField] private string displayName;
    [SerializeField] private float damage = 10;
    //etc

    public string DisplayName => displayName;
    public float Damage => damage;
    //etc
}

public class DamageScaledWeapon : IWeapon {
    private Weapon weapon;
    private float damageMultiplier = 1;

    public string DisplayName => weapon.DisplayName;
    public float Damage => weapon.Damage * damageMultiplier;
    public float DamageMultiplier { get => damageMultiplier; set => damageMultiplier = value; }
    //etc

    public DamageScaledWeapon(Weapon weapon) {
        this.weapon = weapon;
    }
}

//Can display a Weapon or a DamageScaledWeapon 
public class WeaponPresenter : MonoBehaviour {
    [SerializeField] private Text nameText;
    [SerializeField] private Text damageText;
    //etc    

    private IWeapon model;

    public IWeapon Model {
        get => model;
        set {
            model = value;
            Refresh();
        }
    }

    private void Refresh() {
        if (model != null) {
            nameText.text = model.DisplayName;
            damageText.text = model.Damage.ToString("N1");
            //etc
        }
    }
}
Source Link
Kevin
  • 7k
  • 1
  • 12
  • 32

I generally prefer ScriptableObjects for storing data that:

  1. You want to be editable with a GUI in the Unity Editor (as they can be edited in the Inspector much like the components on a GameObject)
  2. Is determined at design time and will not change at runtime
  3. You want to be able to assign to fields in the Unity Inspector

Here's the typical strategy I'll use for this:

//Data model
public class Ability : ScriptableObject {
    [SerializeField] private string displayName;
    [SerializeField] private string description;
    //etc

    public string DisplayName => displayName;
    public string Description => description;
    //etc
}

//Component for UI view
public class AbilityPresenter : MonoBehaviour {
    [SerializeField] private Text nameText;
    [SerializeField] private Text descriptionText;
    //etc

    private Ability model;

    public Ability Model {
        get => model;
        set {
            model = value;
            Refresh();
        }
    }

    private void Refresh() {
        if (model != null) {
            nameText.text = model.DisplayName;
            descriptionText.text = model.Description;
            //etc
        }
    }
}

This is probably most analogous to the Model-View-Presenter pattern (MVP), where the AbilityPresenter component would be the presenter, and a corresponding UI prefab would be the view.

If you'd also like to display your data in list form and are looking for a way to save some time, I sell a package on the Unity Asset Store that uses the above strategy for displaying sets of data in a list. In this package, you define a data model (which can be any struct or class) and view component very similar to the above, and a corresponding UI prefab. Then you can simply pass sets of data into the list controller and it will automatically instantiate view instances from the prefab and feed them the data models