A Clarification on ScriptableObjects

Hi,

I’m here to ask you some questions about ScriptableObjects that i couldn’t find an answer to anywhere.

Let’s say I’m making a Tower Defense Game, so there are several Towers with each their respective Stats. Towers are divided into categories:

  • Offensive Towers: yeah pretty self-explanatory lol;
  • Support Towers : Towers with no offensive capabilities, but that can boost other Tower’s stats, or make enemies weaker;
  • Misc Towers: Towers with none of the above. (Like a tower that makes you money, it doesnt shoot, and it doesnt boost/debuff anything);

And with this, my first thought was to make an Abstract MonoBehaviour, full of general variables that every turret has (for example how much it costs) to represent stats, and then make several other Derived MonoBehaviours for each Tower type that has their respective stats (an Offensive Tower will have his Attack Power, something a Support wouldn’t have).

This, at least in my head, worked fine, but nonetheless i wanted to research a little more on the matter and everything pointed me to these ScriptableObjects.

I’ve studied the basics of how these things work and i can in general understand what they are, why and where are useful… at least, i think??? But i cannot really understand how it links to my case. One of the most “popular(?)” example of their use was of Stats (that being of Enemies, Players, ecc…) which is literally what I’m after. This is somethings i understood:

  • Apparently ScriptableObjects are assets, and basically glorified data containers where you can put giant amount of data in it;
  • I saw basically everyone say that they are really good at “initializing data” and, if used right, at reducing the Project’s memory usage;
  • Someone mentioned that i shouldn’t absolutely change values in them on runtime (which makes sense being an assets);

Now, the problem is that, the stats of my Towers could change on runtime, that’s what Support Tower are made for, but if i cannot change the values of the ScriptableObject on runtime i cannot do that, so some people said that i should create a component that gets every variable from the ScriptableObject and store them there so that i can change them on runtime, but then if i do this i defeat the whole purpose of this and I’m back to square one… see where’s my problem?

I have no idea if I’m missing something really Important because, as of now, i cannot for the life of me understand what I’m supposed to do.

So… I’m asking y’all for a Clarification.

Thanks in advance to anyone that actually reads it.

Lets see if I can clear things up.

This is just one use of scriptable objects. Really you can do a lot of things with them so long as you can architect it right. Another popular use is as a form of service locator, where you use a shallow hierarchy to produce different scriptable objects with different logic and parameters (Basic AI is one example).

So, you can change the data on SO’s at run time. Though it’s important to note that the data will persist between play sessions unlike monobehaviour components. This is both a blessing and a curse depending on what you’re trying to do with them, so sometimes you’ll need safeguards to ensure data is reset properly. Again it depends on what you’re doing with the SO.

In your case SO’s would make a good source of immutable data for your towers. Having an intermediary between this data and what’s used in gameplay is perfectly normal. Rather than storing data, it can intercept reading the values and check for any form of modifiers before returning a value.

You could do prefabs and monobehaviours for this sort of thing, though I prefer scriptable objects as they’re generally a lot more stable than prefabs (especially if you’re doing any of your own serialisation). There’s a lot of editor tools out there that support SO’s more than prefabs too.

Sorry, could you explain this for me a little better? I dont know if i understood correctly.

Anyway, thanks a lot for the reply you already cleared some things up.

Sorry for the slow reply. Here’s some simple examples:

Lets say you have your attack turret scripable object class inheriting from your base turret class:

public class OffensiveTurretStats : TurretStatsBase
{
    public float TurretDamage;

    public float TurretFireSpeed;

    //more stats, etc
}

Then the actual turret monobehaviour could look like this:

public class OffensiveTurrentComponent : Monobehaviour
{
    [SerializeField]
    private OffensiveTurretStats offensiveTurretStats; //SO goes here.

    public TurrentDamage
    {
        get
        {
            float extraDamage = //check for any modifiers
            return offensiveTurretStats.TurretDamage + extraDamage;
        }
    }

    //same thing for TurretFireSpeed here
}

In the above example, rather than accessing the SO’s damage field, you instead expose a property that checks for any modifiers before returning the actual damage value. These modifiers could be additional components, a list of scriptable objects, etc.

OH ok now i understand. So, in this way, I directly pass the Damage with an eventual modifier without storing them anywhere. Even tho this is a pretty simple approach i haven’t thought about it, and for some reason i couldn’t even find someone online explaining something similar. Thanks a lot for the clarification!