Hello all!
A Brief Overview
I have been trying to create a flexible system for creating items via ScriptableObjects. Here is my simple code for creating an item:
[CreateAssetMenu(menuName="New Item")]
public class Item : ScriptableObject {
[SerializeField] private string m_name;
[SerializeField] private string m_description;
}
As you can see, all this code does is create a new .asset file which will contain a name and description variables. I can set these variables via the inspector. So to create an Apple item my steps would look like this:
- Assets > Create > New Item
- A new .asset file appears in my assets
- I name this Apple.asset
- Set the name variable to Apple via the inspector
- Set the description variable to Delicious! via the inspector
I now have an Apple item asset. Creating a Cake item or a Recycled Boot is just as easy. Fantastic!
The Problem
From what I understand, this Apple.asset exists in my assets folder and can be referenced via references. That seems to be what ScriptableObjects are for: storing some data which can be referenced; Shared is the keyword here. The issue here is that items should be uniquely identifiable. If I have some Inventory database which holds two Apples, it is only holding two references to the same Apple asset. What if I wish to introduce unique attributes to these Items? e.g. Items now have a m_numberOfTimesUsed property which increments every time it is used. I cannot share this property value between items.
As far as I understand you can’t create an instance of a ScriptableObject asset. It would be great if I could create an instance of my Apple.asset but that just doesn’t seem to be the way ScriptableObjects are intended to be used.
Some Solutions
- Create a UniqueItem class which holds a reference to the ScriptableObject Item asset:
public class UniqueItem {
public readonly Item item;
public UniqueItem(Item item) {
this.item = item;
}
}
This would allow me to create new items by calling new UniqueItem(item) where item is a reference to a ScriptableObject Item asset. I can then compare two UniqueItem objects to see if they are the same instance or not (Going back to my Inventory database example, the two Apples would now be unique objects and have a reference to the Apple asset). The con of this approach is that I cannot easily create the m_numberOfTimesUsed property without adding this property to the UniqueItem class.
- Forget about ScriptableObjects. This means I lose support for Unity’s inbuilt serialization. I also lose the benefits of easy item creation with the editor. Every new item must be created via a new class and must be constructed via scripting. If I’m specifying the item’s sprite, I’ll need to set the sprite path explicitly. The creation process becomes difficult and not very user friendly.
Am I missing something here, or just misunderstanding the use of a ScriptableObject? I seem to be wanting to use ScriptableObject assets as a ‘blueprint’ for item instantiation. As I say that it sounds just like I am describing a class and instantiating it via new.
Any clarity on this situation is greatly appreciated.
Thank you,
-Pzula
EDIT: Solved
Thanks to LaneFox’s answer, creating a unique instance of a .asset ScriptableObject is as easy as: Item newUniqueItem = Object.Instantiate(appleAsset);
or a more generic implementation:
MyScriptableObject newUniqueSO = Object.Instantiate(scriptableObjectAsset);
where MyScriptableObject is a class deriving from ScriptableObject, and scriptableObjectAsset is a reference to the .asset file.