[SOLVED] How to make one object type for different subclasses with scriptable objects?

Hi guys, I still not found answer on my question, I still not found way how to make it clear and easy.

I have some scriptable objects, for example: wood, iron, shovel.

Basic item class

public class Item : ScriptableObject
{
    public int itemID;
    public enum itemTypes
    {
        Resource,
        Product,
        Machine
    }
    public itemTypes itemType;
    public string itemTitle;
    public Sprite itemIcon;
    public int itemValue;
    public double weight = 0;

    public virtual void Use()
    {
        Debug.Log("You touch " + itemTitle);
    }

}

Product (shovel) item class

public class Production : Item
{
    public double priceSell;
    public List<Item> prodList = new List<Item>();

    public Inventory inventory;

    private void Awake()
    {
        GameObject gameObject = GameObject.Find("Inventory");
        inventory = gameObject.GetComponent<Inventory>();
    }

    public override void Use()
    {
        base.Use();

        Debug.Log("ItemID: " + itemID);
       Item item = inventory.GetItemByID(itemID);
       if (inventory.CheckItem(item))
        {
            Debug.Log("You have this item " + itemTitle);
        }
       else
        {
            Debug.Log("You haven't " + itemTitle);
        }
    }
}

I can’t check item in iventory, because inventory work with base Item class, it want Item type object for do functions.
For example part of Inventory.cs

public Item GetItemByID(int itemID)
    {
        Debug.Log("GetItemByID called, itemID: " + itemID);
        return factoryItems.Find(x => x.itemID == itemID);
    }

    public bool CheckItem(Item item)
    {
        Debug.Log("Check item function called!");
        return factoryItems.Exists(x => x == item);
    }

Inventory hold all items with Item object type and use List for that. But my items doesn’t have Item object type, they are Resource(wood, iron) and Product(shovel) item types. I know two ways how to solve it, but I don’t like anyone.

  1. Remove item subclasses and make all items like Item, use enum to get itemType and then use switch for each type when I want to Use() it - I don’t like this, because a lot of null variables will be for each item, for example I use List prodList for item recipe/blueprint(only Product items need this), simple wood doesn’t need this variable at all.
  2. Use it at is, but also with enum and check itemType with if and switch. - I lot of hard and massive logic in code, easy way to make problems for self.

Give me advice please how I can work with any item type in my inventory?

I’m not 100% sure what you want, it seems you forgot to ask your question, but does this help?

public abstract class Item : ScriptableObject
{
    public abstract bool IsProduct { get; }
}

public class ItemProduct : Item
{
    public override bool IsProduct { get { return true; } }
}

public class ItemResource : Item
{
    public override bool IsProduct { get { return false; } }
}

Item item = ...;
if (item.IsProduct) item.Use();

Thanks, but it doesn’t :slight_smile:

Problem in object’s types, Inventory script hold all items as Item, but actually some of them are Resource and some are Product.

So when script ask Invetory to CheckItem(item), I got NullPointReference, because as I think, script can’t use Resource/Product type, it know only Item type.

So main question is, how I can attach parent class type for child class objects? If all this object are scriptable and has one parent class.

So you are trying to cast the items to the Resource/Product type, but sometimes that results in a null pointer, since the Item in question is not of that type. Then I see a few options:

  • Do a check first:
if (!(item is Product)) return;
  • Add the check in the root class, as I described above:
if (!item.IsProduct) return;
  • Do a null check after the cast:
Product product = (Product)item;
if (product == null) return;

If you want to make things a little more flexible, you could do essentially the same, but use interfaces for the check:

public class Product : Item, IProduct
if (!(item is IProduct)) return;

That way you can have different subclasses implement the same interface and still only require one check. Also, a single subclass could implement multiple interfaces.

In your case I can imagine this to be an interface for example:

public interface IPrice
{
    public double PriceSell { get; }
}

On a side note, don’t store prices as floating point values, use an integer with fixed point representation instead. (Store as int in cents, half the memory, none of the rounding problems.)

I got it work, but I don’t understand logic actually.

My inventory script is instance

public static Inventory instance;

    private void Start()
    {
        instance = this;
        UpdatePanelSlots();
    }

First mistake was get Inventory component

private void Awake()
    {
        GameObject gameObject = GameObject.Find("Inventory");
        inventory = gameObject.GetComponent<Inventory>();
    }

In subclass scripts, that code is useless.

Al I need is call
Inventory.instance.CheckItemByID(itemID) and it works. Maybe I don’t understand object types for scripable objects…

For now I use:

public override void Use()
    {
        base.Use();

        Debug.Log("Try to get item " + itemID);
        Item item = Inventory.instance.GetItemByID(itemID);
        if(item)
        {
            Inventory.instance.AddItem(item);
        }

    }

I still have problems, if item not in inventory, script can’t found it. But this is just question of time. I need to improve it with some ItemsManager or something…

Thanks for your help and sorry for my stupidity :wink: