Retrieving values from list of Scriptable Objects
I'm trying to make a composition-based inventory after reading some forums on here and watching some tutorials and talks on youtube but I am not sure I am doing it right, or at least elegantly. I have a large list of item attribute scriptable objects, i.e.
public class Damage : ItemAttribute
`{
public int damage;
}
They are all inheriting from ItemAttribute SciptableObject which just has a virtual void DoLayout(). So I have loads of those that I can apply as and when needed on items...
public enum ItemType { CONSUMABLE, WEAPON, EQUIPMENT }
[CreateAssetMenu (menuName = "Item/Item")]
public class Item : ScriptableObject {
public ItemType itemType;
public List<ItemAttribute> attributes = new List<ItemAttribute>();
}
These are compiled into Inventories:
public class InventorySO : ScriptableObject
{
public List<Item> items = new List<Item>();
}
Which then go into the Inventory Manager...
public class InventoryManager : MonoBehaviour {
public InventorySO charInventory;
void Start()
{
for (int i = 0; i < charInventory.items.Count; i++)
{
if (charInventory.items[i] != null)
{
Item tmp = charInventory.items[i];
Debug.Log("Item" + tmp.name);
var weapons = tmp.attributes.Where(ItemAttribute => ItemAttribute.GetType() == typeof(Damage));
foreach (Damage damage in weapons)
{
Debug.Log("Damage: " + damage.damage);
}
var names = tmp.attributes.Where(ItemAttribute => ItemAttribute.GetType() == typeof(ItemName));
foreach (ItemName name in names)
{
Debug.Log("Name: " + name.itemName);
}
}
else
{
Debug.Log("Empty");
}
}
}
}
I'm going to use the inventory manager to get all the information stored in the items attributes but if I have to do that linq statement and foreach loop for every possible attribute in every item it is going to get very messy. Is there another way to do this? Is this even a good way to create an inventory? I plan of duplicating this system for skills/abilities if this works out
Answer by Ran-Quan · Mar 22, 2017 at 03:56 AM
If you really need this level of flexibility, then the overhead of the system is acceptable, as long as the number of attributes on an item is relatively small (e.g. below 100).
Instead of using Linq, which will allocate from the heap (cause more GC collections), implementing a simple helper method will make access to the attributes easier. In your Item class:
public TAttr Attribute<TAttr>()
where TAttr : ItemAttribute {
TAttr attr = null;
var count = attributes.Count;
for (int i = 0; i < count; i++) {
attr = attributes[i] as TAttr;
if (attr != null) return attr;
}
attr = ScriptableObject.CreateInstance<TAttr>();
attributes.Add(attr);
return attr;
}
Then you can write code like this:
item.Attribute<Damage>().damage = 42;
var damage = item.Attribute<Damage>().damage;
Thank you so much, this works and is much nicer.
The number of attributes in each item will be closer to 10 than 100 but i like the flexibility of, for example, having a hat that can be equipped as a weapon and had a damage value. A unique case that would confuse me if i tried to work out how to deal with the inheritance. Do you think what I'm doing is the wrong way to do things? What would you recommend?
Your answer
Follow this Question
Related Questions
How to convert object types? 0 Answers
c# foreach does not work 0 Answers
Attaching method to scriptableobject 1 Answer
Attach a ScriptableObject Asset to a GameObject in code 0 Answers
If statement not working. 0 Answers