RPG Picking up items with polymorphism - probably quick :)
Hello everyone, I know inventory questions get asked a lot but I have a particular problem that I have not been able to find the answer to because every tutorial and answer seems to conveniently avoid this issue.
Essentially, I'm working on a bit of an RPG, and so I have a BaseItem class (not MonoBehaviour) and a few classes that extend it like BaseWeapon, etc. Then I have a spawn game object in the world with a script attached to it. This script is called something like "SteelSwordSpawnScript" for example, which is MonoBehaviour, and in its start function I would initialize a new BaseItem of type BaseSword and fill the appropriate values for a steel sword in this case. When I pick up the item through a raycast, I obtain the script component and then from that I obtain the actual sword item and add it to an inventory array. The problem is, I need this to work for any possible item. In this way, I'd have to create hundreds of scripts for all the different items and so in my pick-up code I'd have to go through hundreds of possible script names. How can I just make one script, assign it any random BaseItem for any spawn object, and put it my inventory?
Thanks in advance :)
Answer by Adam-Mechtley · Nov 25, 2016 at 01:11 PM
Hi @Andrzej828! From what I understand, you don't want to be defining each of these item types programmatically, but instead want to serialize data for each individual item and save them as assets in your project that you can reuse anywhere. That said, Unity does not support polymorphic serialization, so what you want to do is actually define a class for each item type and then create prefabs or other assets in your project to reference them.
For example, what I would do is something like this (obviously use real data for your game):
public abstract class BaseItem : ScriptableObject {
public float Weight {
get { return m_Weight; } set { m_Weight = Mathf.Max (0f, value); }
}
[SerializeFloat, Range (0f, 100f)]
private float m_Weight;
protected BaseItem () {}
}
[CreateAssetMenu]
public class Weapon : BaseItem {
public float Durability {
get { return m_Durability; } set { m_Durability = Mathf.Clamp01(value);
}
[SerializeFloat, Range (0f, 1f)]
private float m_Durability = 0f;
public float Power {
get { return m_Power; } set { m_Power = Mathf.Clamp01(value);
}
[SerializeFloat, Range (0f, 1f)]
private float m_Power = 0f;
protected Weapon () {}
}
public class ItemSpawner : MonoBehaviour {
[SerializeField]
private BaseItem m_Item;
protected virtual void Start () {
// do this so you get a unique instance and don't modify the source asset
m_Item = Instantiate (m_Item);
}
}
With this set up, you create each item as an asset in your project and serialize specific data for it. You can then reference any of these particular item definitions on any of your spawners.
Thank you for the quick reply! Actually, I am quite new to Unity and I have never worked with serialization before, but thank you for the suggestion. I did, however, manage to figure out a work-around that I think will be quite effective. I simply apply two scripts to the spawner. One called ItemSpawner which contains no code other than a field declaration and a getter/setter for a BaseItem, and one script for the very specific item, such as in my example, a steel sword. The steel sword script will be a $$anonymous$$onoBehaviour and on Start() it will create the sword item, access the spawner's script component, and set its item to equal the specific sword. Then, my player can just access the one ItemSpawner script which will always be the same. I don't know why I didn't think of this earlier, but I want to post this in case people have the same issue. I don't know if this is proper Unity work, but it works for me :) Thanks again for the information on serialization and I will probably learn it further at some point :)