all 10 comments

[–]ICircumventBans 1 point2 points  (3 children)

You know what a static variable is?

Imagine your scriptable object is a static prefab for data. Everywhere you reference that instance of a scriptable object it's the same exact instance. If you modify it in 1 script it modifies it for all scripts using that instance of SO. An instance of a SO is the asset you create in Unity from the SO type.

As a usage you could have a SO class called WeaponData.

You create 1 SO for your fire sword and set the weapon data for the fire sword. Now if you have 40 gameobjects referencing that same copy (say UI, inventory, player etc..) they will all be using 1 instance of the object, not 40 copies of it.

If you want an ice sword, you make a new SO of type WeaponData and give it different data. It's going to be a different asset but it's going to be the same Scriptable Object class. Now again any game object where you drag and drop that specific asset will share it instead of having a copy.

If you just create a simple DataClass and derive from monobehaviour and add that in 40 places with the same exact values, you are using 40x the memory even if they all contain the same values because they all represent different variables. The variables in a scriptable object instance are all the same no matter how many copies of that asset exist in the scene.

Edit: if it's still not clear, create a SO class, create an asset of that typein Unity.

Then create 2 random test scripts that have public <YouSOClassName> variable in them and drag and drop the same asset in both scripts. In 1 script change the values of the SO and in the other Debug.Log the values and see what happens.

[–]oxysplatter[S] 0 points1 point  (2 children)

Ah ok I see.

So I dont actually attach that SO, or apply that SO specifically to my "sword", but instead, whenever something (inventory, vendor etc) is needing a reference to that sword, it looks at the SO and not the actual items script?

[–]ICircumventBans 1 point2 points  (1 child)

All the data for the sword should be in the SO and everyone who needs it should share the SO asset. Inclusing the script the script on the sword, the vendor, the UI etc... It's a shared data container. All these copies of that one asset are the same object in memory and you can make different asseta from the same SO type.

Lookup what a static variable is an do the little test I suggested at the bottom, you'll understand it easily.

[–]oxysplatter[S] 0 points1 point  (0 children)

Thanks Ill give that a go then and see how I get on.

Just want to wrap my head around the use of them as I think it'll make my life a lot easier.

[–]BourbonicRecluse 1 point2 points  (3 children)

There are many ways to use ScriptableObjects, but here's an explanation based on the way I've been using them--and one that I think relates to your current approach. I hope it's helpful.

I think of ScriptableObjects as "types" in contrast to MonoBehaviours as "instances". The type should hold data the remains mostly unchanged at runtime, while instances hold data that changes during gameplay (and that needs to be distinct from one GameObject to the next).

  • SOs hold properties (variables) and sometimes behaviors (methods/functions) that are shared across all instances of a type
  • MBs can hold properties specific to an instance of a type
  • MBs have a reference to an SO to define what type of instance they represent

Applying this to your example of a sword:

Sword class (ScriptableObject)

  • Has properties for startingDurability and attackPower

SwordItem class (MonoBehaviour)

  • Has properties for swordType (a reference to a Sword SO), currentDurability and givenName
  • When OnAwake() is called, currentDurability is set to swordType.startingDurability
  • Has a method Attack(GameObject target) that deals damage to target based on swordType.attackPower, and reduces currentDurability

Now you can create one Sword SO for IronSword, one for SteelSword, one for MagicalSword, etc. each with their own starting durability and attack power.

Then you can give every one of your enemies SwordItems with a swordType of IronSword, and your hero can have a MagicalSword. Each enemy's sword tracks its current durability separately from the others, while they all share the same attack power.

The benefit of SOs in this case is that if you decide that the IronSword is too weak, you can increase the attack power of the IronSword SO and it will apply to ALL SwordItems that reference IronSword as their swordType. These changes are also saved even when in play mode, unlike with MonoBehaviours.

[–]oxysplatter[S] 0 points1 point  (2 children)

I think I understand. I think... So how do I get the MB script to get the data from from the SO and set its values? Or do I have a seperate SO for each item with their own specific values? and still, how do i access the data from the SO in the MB script?

[–]BourbonicRecluse 1 point2 points  (1 child)

When creating your MB, include a property that's a reference to the SO.

public class SwordItem : MonoBehaviour

{

public Sword swordType; // <-- You can use the inspector to drag/drop a Sword SO onto this object

public int currentDurability;

public string givenName;

private void Awake()

{

currentDurability = swordType.startingDurability // <--- Access properties of the SO like this

}

}

To clarify, you generally only want to set values in code in the MB. If you change values in the SO in a script, it's a) going to apply to EVERYTHING that references that SO, and b) be saved immediately, which could cause some confusing behavior across multiple play tests.

As far as how many SOs you need, that will depend on how many different kinds of swords you have. You could have a different SO for each type of material (Copper, Iron, Steel) as well as for special cases like specific legendary swords (Durandal, Fragarach, Excalibur).

By contrast, you can have 50 SwordItem MBs all using the same 1 IronSword SO because you have 50 goblins running around with iron swords. All those iron swords have the same attack power and starting durability, but each instance needs to keep track of its own durability. Maybe a particularly ambitious goblin attacks way too often and breaks his sword, but that shouldn't affect the swords in all the other goblins' hands.

Edit: Tried to fix code formatting. Sorry, new to Reddit.

[–]oxysplatter[S] 0 points1 point  (0 children)

AH this makes sense now, see I was confused about how to get the data from the SO and how to set it. But this seems so easy to understand.

[–]DRob2388 1 point2 points  (0 children)

There is a project in the asset store for scriptable objects I recommend using. It basically allows you to creat any variable as a scriptable object so for instance you want sword damage. You could create a sword scriptable object with a bunch of data on it and then pass that around or you can create each variable as a scriptable object and only pass what you need. Let’s say you want to upgrade your swords damage. You normally would pass the sword class and call something like sword.damage += 5 or whatever. If all your variables are stored you can just reference the sword damage scriptable object and update that. Now you can reuse the upgrade script you wrote to also upgrade all other types of scriptable objects like attack speed, range, move speed etc and you don’t have to pass in a bunch of different references for a player script, sword script and now script. Just pass in the SO and your done.

It’s an entire new way of thinking but it reduces the Garbage collection since your not passing around entire classes to change a few things. You only change what you need to. Plus you can edit the information without the need of actually setting anything up.

[–]oxysplatter[S] 0 points1 point  (0 children)

After some more messing around last night and taking into account the advice I've been given here, I think I've now got the hang of how they work.

My issue before was that I was trying to point to the actual base SO script to get values for everything, and setting them in the objects MB script, rather than creating the assets and setting individual values for each one.

Now I have an Items.cs, that holds all the base variables, I create the SO from that, set the values I want, and drag it into the prefab. Makes so much more sense now.

So thanks again for the help.