Adventure Items/Weapons : Scriptable Objects or Prefabs ?

Hi guys,
I’m working on an action/adventure 2d game and I’m having a hard time making up my mind on which way to go for my “items and equipment system”.

I’ve learn a bit about scriptable objects which seems like a nice way of doing things but the non monoBehavior aspects make me uncomfortable.

Prefabs seems like a nice solution too.

I’m aiming at a “rpg style”, with character attributes that would impact his equipment and an inventory/equipment window. Of course, there would be different armors/weapons but not a whole bunch either.

For now, I’ve always made things like “well, let’s do a script for this weapon, another one for that piece of gear”, but now that I’d like to scale up the project, I really feel like I have to come up with a much cleaner approach : base item class, inheritance and stuff.

A simple roadmap and a few advices would be really appreciated !
Thank you guys :wink:

Neither. Make your own class. Make it serializable, and define your items by a List of that class in a ScriptableObject. And if saving is going to be a thing, plan for it and implement it straight off the bat, or you’ll be looking at a major redesign down the line.

There are literally tons of ways to do this even within the SO usage, but here is one common way.

public class BaseItem : ScriptableObject
    {
// Basic Item variables
    }
public class ItemDb : ScriptableObject
    {
        public List<BaseItem> Items;
    }
public static BaseItem GetItemByName(string title)
        {
            BaseItem value = ItemDb.Items.First(item => item.Title == title);
            return value;
        }

Here’s another solution someone shared on Slack.
https://hastebin.com/kofezifudo.cpp

1 Like

Hi Dameon, thank you for your input.
Sorry for being so newbish but your answer raises so many questions in my head ^^…
-Would my custom class inherit Monobehaviour ? I guess no.
-How do you manage the items properties in a custom class where you can’t access fields in unity
-What would be the benefits of having a list of them in a SO ? ( is it actually the answer of question 2 ? ^^’ )
-I’m so confused about custom classes as I don’t really get how you make instances of it to actually customize the different items

Sorry for being so silly but I started to develop in Unity and so far Monobehavior and Components has really been my way to go approach :confused:

Nope, you don’t want to inherit from MonoBehaviour, because you can’t serialize MonoBehaviours effectively.
You can access an item’s fields. First, the class has to have the [System.Serializable] attribute. Then, each field you want to show up in the inspector needs to be marked with a [SerializeField] attribute.

I’d do some learning about the fundamentals of classes and how they work, are instanced, and so on…these are definitely things you should understand as a basis of programming in an objected oriented environment.

1 Like

If you want some custom class to be put on an object as a component then it needs to be a Monobehavior. These are serialized automatically but have to exist as a component.

Thats why custom classes / ScriptableObject’s are better for data, they are serialized as an actual .asset file and any changes you make to them are saved into the file and not into some component that depends on the object it is attached to. They’re quick to read and can work more like a database.

1 Like

Thank you Lanefox, I took a look at the hastebin and I like this approach.
There is one thing though that I find not so cool : for each item, you have to create THREE extended classes (itemAsset, and itemData, and itemPrefab)

So is it standard to have multiple SO/Classes for one Item/Weapon ?

Also, I don’t really get how to actually USE the item.
Does the method “triggering” the item necessarily needs to be outside of it ? In that case, I guess you need A LOT of methods because not all items will require the same parameters, right ?

@LaneFox
Also, I have trouble with the hastebin scripts : ItemPrefab can’t be attached to a prefab because it’s an abstract script ( I didn’t know that abstract classes deriving from monoB couldn’t ! And I guess the code you provide is supposed to work … )
And I can’t create the ItemAsset Scriptable Obj with the [CreateAssetMenu] attribute, I get a “the class needs to derive from Scriptable Object” which it obviously does… -_-

If you need an off-the-shelf solution that works out of the box, you should just hit the asset store.

Ok I understand your point of view but don’t get me wrong, that’s not what I was looking for. I was just using the sample you provided for learning purposes ( as I’m not able to make these scripts work, I supposed that understanding why would be important ).

Also, It’s difficult to know if I’m doing something wrong or not as it’s the first time I try to create a SO.
Anyway, thank you for your time :slight_smile:

No problem, they can be confusing to work with at first!

The important thing to remember is that they’re basically just literal files that hold information for things. You can put all of the ScriptableObject Items into the ScriptableObject ItemDatabase list by drag/drop, then query that list with LINQ commands like the simple structure I first posted.

After that, it branches off everywhere and you can go 1000 different ways depending on your game and UI scope/requirements. That’s one of the main reasons the tutorials and stuff on the web stall out at some point mid-way, because its difficult to continue without making huge assumptions.

If you do make classes that inherit from ScriptableObject, make sure they’re in all in separate files otherwise they won’t be able to serialize.

Hi @LaneFox , I hope you’re having a nice time for this 2016 end of year ^^

I’ve been messing around with this ScriptableObjects list and trying to follow your advices, I’ve kind of managed to make it work but as i goes I’m facing a “problem” now :
To make a list of my Item SO, the class shouldn’t be an abstract class, which means that in my derived classes I can’t override whatever methods.

But obviously some items will behave different from the other : the Use() of a health potion will definitely not be the use of a bomb.

So here is the thing : If I go with an abstract class for my baseItem, I have to create multiple lists of the derived (non abstract) item classes, right ? Is it the common way of doing it ?
[Edit] Another problem with that : that would mean that I can’t loop through all my items since they would be “stored” in different lists regarding their classes… It makes the “getitembyname” much trickier…

Abstract classes are basically unimplemented or partially implemented classes. Kind of “header” classes from cpp. You don’t have to make it abstract, but its tidier.

The idea with the abstract class is to have a BaseItem, then never actually create a BaseItem because it is just the minimum amount of data that any ‘real’ item needs in order to exist. Eg stuff like weight, description, name, etc.

If you have a List then it can contain anything that derives from BaseItem, such as Weapon:BaseItem or Armor:BaseItem.

The database its simply a ScriptableObject with a List that you load at the start of a game into a static variable for easy reference. Then its just a matter of using a linq query on the db to find some item where Title == searchString.

public abstract class BaseItem : ScriptableObject
    {
        public virtual void Reset()
        {
            DatabaseTitle = "MustBeUnique";
            DisplayTitle = "New Item";
            Description = "It's an blank Item.";

            StackSize = 1;
            MaxStackSize = 1;

            UiIcon = null;
        }

        public string DisplayTitle;
        public string DatabaseTitle;
        public string Description;

        public int StackSize;
        public int MaxStackSize;

        public Sprite UiIcon;
    }
    public class Consumable : BaseItem
    {
        public override void Reset()
        {
            base.Reset();
            base.DisplayTitle = "New Food";
            base.DatabaseTitle = "Consumable_01";
            base.Description = "Brand new consumable item.";

            HealthAdjust = 10;
        }
        public int HealthAdjust;
    }
    public class Structure : BaseItem
    {
        public override void Reset()
        {
            base.Reset();
            base.DatabaseTitle = "New Structure";
            base.Description = "It's a structure";

            ColliderSize = Vector3.one;
            Requirements = new ItemRequirements();
        }

        public Vector3 ColliderSize;
        public ItemRequirements Requirements;
    }
public class ItemDb : ScriptableObject
    {
        public List<BaseItem> Items; // this can have anything that derives from BaseItem. Eg `Consumable` and `Structure`

        public virtual void Verify()
        {
            if(Items == null) Items = new List<BaseItem>();
        }
    }

Hi Foxy dude !
Thank you for this detailed reply.

Actually, I already get how inherited classes works ( or at least I know a bit about that ) but your examples did clarify how to implement it :wink:

Now about : “public List Items; // this can have anything that derives from BaseItem. Eg Consumable and Structure”
I actually did make it work, it’s just that in Inspector the list elements are not displayed (which is kind of annoying when you want to actually create and edit your “derived” items.)

Anyway, here is the solution I think I’ll come up with :

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[CreateAssetMenu (menuName=("InventorySystem/InventoryItemList"))]
public class InventoryItemList : ScriptableObject {
    public string listName;
    public List<InventoryItem> itemList = new List<InventoryItem>(); // InventoryItem is the base class

    public List<Potion> potionList = new List<Potion>(); // potion inherit from Consumable which inherit from base class InventoryItem
    public List<Bomb> bombList=new List<Bomb>(); //same for bomb
}

Here is my SO containing all the items of the game, I create a List of each type so I can create/edit my different items in the inspector.
Now I need to build a script to attach to an actual game object that represent an actual item in the game, this script contain a reference to an item in the database ( I get that item following your GetItem methods ).

I think to create another SO to be my actual PlayerInventory.
Now when I pick up any loot, let say a potion, I just add the corresponding item from the database into this Inventory (in this case, I should create another instance of the item and not just add the item from the database, right ??)

Now if I want to open my Inventory, I just have to loop through my items list and check the derived class to construct new lists : “consumable, weapons, armors” and display them in different panels.

Does this whole setup sounds ok to you ?
Again, thank you for your patience, you’re my Santa !

[EDIT] : just realized that your ItemBase class derives from ScriptableObject !
What benefits would you get from it ?

So far my base class looks like this :

using UnityEngine;
using System.Collections;

[System.Serializable]
public abstract class InventoryItem  {

    public string itemName = "New Item";
    public int itemID = 0;
    public Sprite itemIcon = null;
    public Rigidbody2D itemBody2D = null;
    public bool isStackable;
    public int maxStack;
    public bool destroyOnUse;
    public int cost = 0;
    public ItemType type;

    public enum ItemType{
        usable,
        weapon,
        equip,
    }
}

Then why are you asking this? :wink:

The architecture I posted hinges upon the fact that ItemBase is a scriptableobject.

Nice catch :smile:
Well I have been digging all this for hours and it starts to get clearer. :wink:

Though I’ve decided to make my database NOT a ScriptableObject because scriptableobjects list of scriptableobjects are a pain in the a** (and hard to serialize for what I understood)

My database is a custom class containing a static public list of scriptableO of items (and derived classes of items).
I find this much easier to handle, as I can create my items as SO, edit them in inspector.

My player inventory is another list where I can add/remove “cloned SO instances” from my Database, this inventory is saved/load in my game manager class with binary serialization.

So far it works just fine with the different tests I’ve been doing !
Before I try and implement override methods on derived item classes, if you find any “oh god no !” in my current setup, feel free to say ! :smile: