I’m creating an inventory system, in which items all derive from a basic item scriptable object. I wrote some example code which should help explain my issue, but it’s not the exact code I’m running (in other words, I’m not sure if it would compile).
public class Item : ScriptableObject
{
public string name;
public ItemTypeEnum itemType;
}
Each item that derives from it also has additional data.
public class Weapon : Item
{
public int baseDamage;
}
I want to be able to use Item as a generic reference, similar to how you can refer to scripts. I’m actually doing something very similar with an abstract class (does not derive from MonoBehaviour) and 5 scripts that derive from it, but in this case I get an error that I cannot convert from Item to Weapon.
For reference, this is what I’m doing:
List<Weapon> weaponList = new List<Weapon>();
void AddItem (Item newItem) {
if (newItem.ItemTypeEnum == ItemTypeEnum.Weapon) {
weaponList.Add(newItem);
}
}
You can, but you need to explicitly case the Item to a weapon.
Are you sure you want to do things this way???! This isn’t correct as far as how one would add value with inheritance. You might want to review some tutorials to see what I mean. It’s usually the other way around: a list of Items MAY contain things like Weapons, Items, Clothes, etc.
Another approach is to have a generic InventoryItem interface that all things in your game implement, such as Weapons, Potions, Clothes, anything that can go in your inventory. Interfaces play really nice with Unity.
Do you really need an ItemTypeEnum to tell your code what kind of item it is? You can just use a switch-case to easily check the type of the object in a pretty clean way.
Item thing;
switch(thing)
{
case Weapon weaponItem:
// do weapon thing
break;
default:
break;
}
It’s kind of weird that you’re making a List<Weapon>, as it’s a little counterproductive to the whole “having a base class” thing.
It does make sense to have generic Items if you were to say, have an inventory system with a List<Item> which could contain weapons, but why would you need the weapon to be an Item if you’re not going to treat them like one?
I’m using an interface to add the item to the inventory (I call an interface on the receiver and then call the AddItem function on said receiver). Are you describing that I do the opposite?
I’m saying the inventory should have the ability to accept and handle ANYTHING that expresses the InventoryItem interface or (IInventoryItem if you prefer).
That way the attack system knows about weapons (perhaps through an IWeapon interface!), and the armor system knows about IArmor, and weapons or armor that can be put in the inventory would also express the IInventoryItem interface.
Not saying that’s perfect or the only way, but that’s probably the way I’d reach for first, see how it goes.
I was doing that originally (List), but I was trying to automatically equip a weapon if you had one in the inventory and was running into a typecast error there (mostly for testing purposes, since I haven’t implemented a GUI). I think I would still run into this issue when trying to design an inventory UI and being able to equip an item as a weapon despite having a reference to an Item. I’ll try the switch case suggestion
No, he meant that you just have to cast the item to the right type. Of course a cast would fail if the actual instance is not a Weapon. That’s why a conditional cast with the “is” operator or the new pattern matching switch statement as RadRedPanda mentioned above should be used to avoid potential errors at runtime.
I think I’m having trouble understanding what to do with the interface. I get the gist of what you’re saying, but I’ve only ever used interfaces so that I know what function to look for in a script. My inventory items don’t do anything, they’re just data containers that tell other things what to do. What would the interface be doing in this case?
Just to be clear, here’s your own code with an “is” operator.
void AddItem (Item newItem) {
if (newItem is Weapon wep) {
weaponList.Add(wep);
}
}
This is possible thanks to the new is operator variable declaration. In the past the is operator could only test for a certain type and return true / false and that’s it. Now we can declare a variable inline so when the type check is positive, we automatically have a strongly typed variable “wep” of type Weapon insice the if body which we can use. The classical equivalent would have been
if (newItem is Weapon) {
weaponList.Add((Weapon)wep);
}
Though that’s wasteful as the type check as well as the cast itself has to check the type. The is operator (or the switch statement) can do the casting automatically for you.