- Home /
What is the best way to set up an inventory system?
Hey guys,
Recently I've been trying to make an inventory system at the moment I have 5 categories of items inheriting from item class and the following way they have been initialized. First off I was wondering if these was a good way to how all items in the first place as I think it would take up a lot of RAM?? public List allItems = new List();
[SerializeField] Sprite[] weaponImagesInv;
[SerializeField] Sprite[] armorImagesInv;
[SerializeField] Sprite[] aidImagesInv;
[SerializeField] Sprite[] miscImagesInv;
[SerializeField] Sprite[] ammoImagesInv;
[SerializeField] Sprite[] weaponImages;
[SerializeField] Sprite[] armorImages;
[SerializeField] Sprite[] aidImages;
[SerializeField] Sprite[] miscImages;
[SerializeField] Sprite[] ammoImages;
[SerializeField] GameObject[] bulletPrefabs;
void Start () {
allItems.Add(new Weapon(0.000f, "Fists", weaponImagesInv[0], weaponImages[0], 1, 1, Weapon.WepType.Melee, Weapon.WepSubType.None, 1, 1));
allItems.Add(new Weapon(0.001f, "Swag Ray", weaponImagesInv[1], weaponImages[1], 1, 1, Weapon.WepType.Ranged, Weapon.WepSubType.AssaultRifle, 1, 0.1f, 1, 1, 10, 1, bulletPrefabs[0]));
allItems.Add(new Armor(1.000f, "Helmet for your Head", null, 1, 1, Armor.ArmorType.Head, 0, 1));
allItems.Add(new Aid(2.000f, "Name of Some Medicine", null, 1, 1, 1, 1));
allItems.Add(new Misc(3.000f, "Swagger", null, 1, 1500));
allItems.Add(new Ammo(4.000f, "Standard", null, 0, 1));
allItems.Add(new Ammo(4.001f, "MoreDamage!", null, 0, 5));
}
Secondly when I try and access the these items I get referred to the item created by the previous way instead of the item in the inventory, this is a result of instances I'm pretty sure but I'm not sure how to fix this issue.
public List<Item> itemInv = new List<Item>();
public InitialiseItems allInv;
[SerializeField] GameManager gm;
float weight = 0;
public void AddItem(Item i){
if(itemInv.Contains(i))
itemInv[itemInv.IndexOf(i)].quantity += i.quantity;
else{
itemInv.Add(i);
}
gm.DisplayNotification(i.itemName + " was added");
}
public void RemoveItem(Item i){
if(itemInv.Contains(i)){
if(i.quantity < itemInv[itemInv.IndexOf(i)].quantity){
itemInv[itemInv.IndexOf(i)].quantity -= i.quantity;
}
else{
itemInv.Remove(i);
}
gm.DisplayNotification(i.itemName + " was removed");
}
}
Answer by Cherno · Sep 13, 2015 at 10:30 AM
Use this code to copy values between two instances.
using System;
using System.Reflection;//for copying objects
using System.Linq;//for string[].Contains()
using UnityEngine;//for print
public class CopyData {
/// <summary>
/// Copies the data of one object to another. The target object gets properties of the first.
/// Any matching properties (by name) are written to the target.
/// </summary>
/// <param name="source">The source object to copy from</param>
/// <param name="target">The target object to copy to</param>
public static void CopyObjectData(object source, object target)
{
CopyObjectData(source, target, String.Empty, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
}
/// <summary>
/// Copies the data of one object to another. The target object gets properties of the first.
/// Any matching properties (by name) are written to the target.
/// </summary>
/// <param name="source">The source object to copy from</param>
/// <param name="target">The target object to copy to</param>
/// <param name="excludedProperties">A comma delimited list of properties that should not be copied</param>
/// <param name="memberAccess">Reflection binding access</param>
public static void CopyObjectData(object source, object target, string excludedProperties, BindingFlags memberAccess)
{
string[] excluded = null;
if (!string.IsNullOrEmpty(excludedProperties))
{
excluded = excludedProperties.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
MemberInfo[] miT = target.GetType().GetMembers(memberAccess);
foreach (MemberInfo Field in miT)
{
string name = Field.Name;
// Skip over excluded properties
if (string.IsNullOrEmpty(excludedProperties) == false
&& excluded.Contains(name))
{
continue;
}
if (Field.MemberType == MemberTypes.Field)
{
FieldInfo sourcefield = source.GetType().GetField(name);
if (sourcefield == null) { continue; }
object SourceValue = sourcefield.GetValue(source);
((FieldInfo)Field).SetValue(target, SourceValue);
}
else if (Field.MemberType == MemberTypes.Property)
{
PropertyInfo piTarget = Field as PropertyInfo;
PropertyInfo sourceField = source.GetType().GetProperty(name, memberAccess);
if (sourceField == null) { continue; }
if (piTarget.CanWrite && sourceField.CanRead)
{
object targetValue = piTarget.GetValue(target, null);
object sourceValue = sourceField.GetValue(source, null);
if (sourceValue == null) { continue; }
if (sourceField.PropertyType.IsArray
&& piTarget.PropertyType.IsArray
&& sourceValue != null )
{
CopyArray(source, target, memberAccess, piTarget, sourceField, sourceValue);
}
else
{
CopySingleData(source, target, memberAccess, piTarget, sourceField, targetValue, sourceValue);
}
}
}
}
}
private static void CopySingleData(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object targetValue, object sourceValue)
{
//instantiate target if needed
if (targetValue == null
&& piTarget.PropertyType.IsValueType == false
&& piTarget.PropertyType != typeof(string))
{
if (piTarget.PropertyType.IsArray)
{
targetValue = Activator.CreateInstance(piTarget.PropertyType.GetElementType());
}
else
{
targetValue = Activator.CreateInstance(piTarget.PropertyType);
}
}
if (piTarget.PropertyType.IsValueType == false
&& piTarget.PropertyType != typeof(string))
{
CopyObjectData(sourceValue, targetValue, "", memberAccess);
piTarget.SetValue(target, targetValue, null);
}
else
{
if (piTarget.PropertyType.FullName == sourceField.PropertyType.FullName)
{
object tempSourceValue = sourceField.GetValue(source, null);
piTarget.SetValue(target, tempSourceValue, null);
}
else
{
CopyObjectData(piTarget, target, "", memberAccess);
}
}
}
private static void CopyArray(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object sourceValue)
{
int sourceLength = (int)sourceValue.GetType().InvokeMember("Length", BindingFlags.GetProperty, null, sourceValue, null);
Array targetArray = Array.CreateInstance(piTarget.PropertyType.GetElementType(), sourceLength);
Array array = (Array)sourceField.GetValue(source, null);
for (int i = 0; i < array.Length; i++)
{
object o = array.GetValue(i);
object tempTarget = Activator.CreateInstance(piTarget.PropertyType.GetElementType());
CopyObjectData(o, tempTarget, "", memberAccess);
targetArray.SetValue(tempTarget, i);
}
piTarget.SetValue(target, targetArray, null);
}
}
Use it like this:
Item newItem = new Item();
CopyData.CopyObjectData(ItemTemplate, newItem);
It doesn't work D: won't even copy the itemName which is a string
Post your code then. You would do it like this:
public void AddItem(Item i){
if(itemInv.Contains(i))
itemInv[itemInv.IndexOf(i)].quantity += i.quantity;
else{
Item newItem = new Item();
CopyData.CopyObjectData(i, newItem);
itemInv.Add(newItem);
}
gm.DisplayNotification(i.itemName + " was added");
}
public void AddItem(Item i){
if(itemInv.Contains(i))
itemInv[itemInv.IndexOf(i)].quantity += i.quantity;
else{
Item newItem = new Item();
CopyData.CopyObjectData(i, newItem);
itemInv.Add(newItem);
}
if(this.transform.tag == "Player")
gm.DisplayNotification(i.itemName + " was added");
}
public class Item {
public enum Type{
Weapon,
Armor,
Aid,
$$anonymous$$isc,
Ammo
}
public string itemName;
public Type type;
public Sprite invImage;
public Sprite image;
public float weight;
public float value;
public float id;
public int quantity;
public Item (){
this.itemName = "";
this.type = Type.$$anonymous$$isc;
this.invImage = null;
this.image = null;
this.weight = 0;
this.value = 0;
this.id = 0;
this.quantity = 0;
}
}
that's how i did it but when i try and use the newly copied object it says its values are null which is weird since its string should be "" before going into the copyData
Hint: Don't use enums or any classes named "Type", as this is used by System and can (and will) lead to problems. Name it "ItemType" or something like this ins$$anonymous$$d. Also, don't name variables "type" for the same reason.
It probably doesn't have anything to do with the issues you are having, though. All I can say is that the CopyData script works and is reliable, I have used is many times in the past.
Okay, thanks man I'll try and figure it out :)
do you think it has something to do with it being a Weapon that inherits from Item?
Possibly, I don't know. I never use class inheritance, I ins$$anonymous$$d keep it all in one class with sub-classes as required.