I don’t think this has been asked about this specifically yet but I seem to be getting an error when I try to load from hitting Play or switching scenes now that I have equipment that actually swaps the Player’s weapons. The items work; they can be picked up and do seem to swap into the player’s hands correctly, but there is some sort of issue with the dictionary key that I don’t fully understand. Here is the full error:
ArgumentNullException: Value cannot be null.
Parameter name: key
System.Collections.Generic.Dictionary2[TKey,TValue].FindEntry (TKey key) (at <1f66344f2f89470293d8b67d71308c07>:0) System.Collections.Generic.Dictionary
2[TKey,TValue].ContainsKey (TKey key) (at <1f66344f2f89470293d8b67d71308c07>:0)
RPG.Saving.Inventories.InventoryItem.GetFromID (System.String itemID) (at Assets/OurScripts/InventorySystem/InventoryItem.cs:53)
RPG.Saving.Inventories.Inventory.RPG.Saving.ISaveable.RestoreState (System.Object state) (at Assets/OurScripts/InventorySystem/Inventory.cs:248)
RPG.Saving.SaveableEntity.RestoreState (System.Object state) (at Assets/OurScripts/Saving/SaveableEntity.cs:39)
RPG.Saving.SavingSystem.RestoreState (System.Collections.Generic.Dictionary`2[TKey,TValue] state) (at Assets/OurScripts/Saving/SavingSystem.cs:85)
RPG.Saving.SavingSystem.Load (System.String saveFile) (at Assets/OurScripts/Saving/SavingSystem.cs:35)
RPG.Saving.SavingWrapper.Load () (at Assets/OurScripts/Saving/SavingWrapper.cs:49)
RPG.SceneManagement.Gate+d__8.MoveNext () (at Assets/OurScripts/SceneManagement/Gate.cs:60)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <4014a86cbefb4944b2b6c9211c8fd2fc>:0)
The script it is referring immediately to is as follows (and I think untouched from what Sam gave us):
using System;
using System.Collections.Generic;
using UnityEngine;
namespace RPG.Saving.Inventories
{
/// <summary>
/// A ScriptableObject that represents any item that can be put in an
/// inventory.
/// </summary>
/// <remarks>
/// In practice, you are likely to use a subclass such as `ActionItem` or
/// `EquipableItem`.
/// </remarks>
public abstract class InventoryItem : ScriptableObject, ISerializationCallbackReceiver
{
// CONFIG DATA
[Tooltip("Auto-generated UUID for saving/loading. Clear this field if you want to generate a new one.")]
[SerializeField] string itemID = null;
[Tooltip("Item name to be displayed in UI.")]
[SerializeField] string displayName = null;
[Tooltip("Item description to be displayed in UI.")]
[SerializeField][TextArea] string description = null;
[Tooltip("The UI icon to represent this item in the inventory.")]
[SerializeField] Sprite icon = null;
[Tooltip("The prefab that should be spawned when this item is dropped.")]
[SerializeField] Pickup pickup = null;
[Tooltip("If true, multiple items of this type can be stacked in the same inventory slot.")]
[SerializeField] bool stackable = false;
// STATE
static Dictionary<string, InventoryItem> itemLookupCache;
// PUBLIC
/// <summary>
/// Get the inventory item instance from its UUID.
/// </summary>
/// <param name="itemID">
/// String UUID that persists between game instances.
/// </param>
/// <returns>
/// Inventory item instance corresponding to the ID.
/// </returns>
public static InventoryItem GetFromID(string itemID)
{
if (itemLookupCache == null)
{
itemLookupCache = new Dictionary<string, InventoryItem>();
var itemList = Resources.LoadAll<InventoryItem>("");
foreach (var item in itemList)
{
if (itemLookupCache.ContainsKey(item.itemID))
{
Debug.LogError(string.Format("Looks like there's a duplicate GameDevTV.UI.InventorySystem ID for objects: {0} and {1}", itemLookupCache[item.itemID], item));
continue;
}
itemLookupCache[item.itemID] = item;
}
}
if (itemID == null || !itemLookupCache.ContainsKey(itemID)) return null;
return itemLookupCache[itemID];
}
/// <summary>
/// Spawn the pickup gameobject into the world.
/// </summary>
/// <param name="position">Where to spawn the pickup.</param>
/// <param name="number">How many instances of the item does the pickup represent.</param>
/// <returns>Reference to the pickup object spawned.</returns>
public Pickup SpawnPickup(Vector3 position, int number)
{
var pickup = Instantiate(this.pickup);
pickup.transform.position = position;
pickup.Setup(this, number);
return pickup;
}
public Sprite GetIcon()
{
return icon;
}
public string GetItemID()
{
return itemID;
}
public bool IsStackable()
{
return stackable;
}
public string GetDisplayName()
{
return displayName;
}
public string GetDescription()
{
return description;
}
// PRIVATE
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
// Generate and save a new UUID if this is blank.
if (string.IsNullOrWhiteSpace(itemID))
{
itemID = System.Guid.NewGuid().ToString();
}
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
// Require by the ISerializationCallbackReceiver but we don't need
// to do anything with it.
}
}
}