Hi,
I managed to fix the duplicates item issue in same scene, I am not sure if it is the optimal way to do it but is the only one I found.
What I did is to create Unique ID into each pickup item and it is assigned in start if it is empty or null, created 2 methods one to get the ID and another to override the ID.
Created new SpawnPickup methods in ItemDropper and InventoryItem to add new variables as ID and for my game I added rotation (as I want the items to appears exactly in same position and rotation when load)
In loading pickups what I do is to check for all items of type Pickup if there are more than one with same ID, delete them.
I hope it helps others. I will paste below the codes I am currently using to help others with this bug.
If somebody else have better approach please let me know.
I still need to fix the equipment items not saving (IndexOutOfRangeException error)
ItemDropper.cs created new variables for ID and rotation, capture state for new variables, foreach loop to check if item to spawn is the same as existing one and new SpawnPickup method to accept the new variables
Summary
public class ItemDropper : MonoBehaviour, ISaveable
{
// STATE
private List<Pickup> droppedItems = new List<Pickup>();
// PUBLIC
/// <summary>
/// Create a pickup at the current position.
/// </summary>
/// <param name="item">The item type for the pickup.</param>
/// <param name="number">
/// The number of items contained in the pickup. Only used if the item
/// is stackable.
/// </param>
public void DropItem(InventoryItem item, int number)
{
SpawnPickup(item, GetDropLocation(), number);
}
/// <summary>
/// Create a pickup at the current position.
/// </summary>
/// <param name="item">The item type for the pickup.</param>
public void DropItem(InventoryItem item)
{
SpawnPickup(item, GetDropLocation(), 1);
}
// PROTECTED
/// <summary>
/// Override to set a custom method for locating a drop.
/// </summary>
/// <returns>The location the drop should be spawned.</returns>
protected virtual Vector3 GetDropLocation()
{
return transform.position;
}
// PRIVATE
public void SpawnPickup(InventoryItem item, Vector3 spawnLocation, int number)
{
var pickup = item.SpawnPickup(spawnLocation, number);
droppedItems.Add(pickup);
}
public void SpawnPickup(InventoryItem item, Vector3 spawnLocation, int number, string pickupID, Vector3 rotation)
{
var pickup = item.SpawnPickup(spawnLocation, number, pickupID, rotation);
droppedItems.Add(pickup);
}
[System.Serializable]
private struct DropRecord
{
public string itemID;
public SerializableVector3 position;
public SerializableVector3 rotation;
public int number;
public int scene;
public string pickupID;
}
private List<DropRecord> otherSceneDrops = new List<DropRecord>();
object ISaveable.CaptureState()
{
RemoveDestroyedDrops();
int currentScene = SceneManager.GetActiveScene().buildIndex;
//var droppedItemsList = new DropRecord[droppedItems.Count];
//for (int i = 0; i < droppedItemsList.Length; i++)
//{
// droppedItemsList[i].itemID = droppedItems[i].GetItem().GetItemID();
// droppedItemsList[i].position = new SerializableVector3(droppedItems[i].transform.position);
// droppedItemsList[i].number = droppedItems[i].GetNumber();
// droppedItemsList[i].scene = SceneManager.GetActiveScene().buildIndex;
//}
foreach (var droppedItem in droppedItems)
{
DropRecord record = new DropRecord();
record.itemID = droppedItem.GetItem().GetItemID();
record.position = new SerializableVector3(droppedItem.transform.position);
record.rotation = new SerializableVector3(droppedItem.transform.eulerAngles);
record.number = droppedItem.GetNumber();
record.scene = currentScene;
record.pickupID = droppedItem.GetPickupID();
otherSceneDrops.Add(record);
}
return otherSceneDrops;
}
void ISaveable.RestoreState(object state)
{
int currentScene = SceneManager.GetActiveScene().buildIndex;
otherSceneDrops = new List<DropRecord>();
var droppedItemsList = (List<DropRecord>)state;
foreach (var item in droppedItemsList)
{
if (item.scene == currentScene)
{
var pickupItem = InventoryItem.GetFromID(item.itemID);
Vector3 position = item.position.ToVector();
int number = item.number;
string pickupID = item.pickupID;
Vector3 rotation = item.rotation.ToVector();
foreach (var pickup in FindObjectsOfType<Pickup>())
{
if (pickup.GetPickupID() == pickupID)
{
print("Two pickups with same ID " + pickup.GetPickupID() + pickupID);
Destroy(pickup.gameObject);
}
}
SpawnPickup(pickupItem, position, number, pickupID, rotation);
}
else
{
otherSceneDrops.Add(item);
}
}
}
/// <summary>
/// Remove any drops in the world that have subsequently been picked up.
/// </summary>
private void RemoveDestroyedDrops()
{
var newList = new List<Pickup>();
foreach (var item in droppedItems)
{
if (item != null)
{
newList.Add(item);
}
}
droppedItems = newList;
}
}
}
In InventoryItem.cs added new SpawnPickup method with additional variables
Summary
public Pickup SpawnPickup(Vector3 position, int number)
{
var pickup = Instantiate(this.pickup);
pickup.transform.position = position;
pickup.Setup(this, number);
return pickup;
}
public Pickup SpawnPickup(Vector3 position, int number, string pickupID, Vector3 rotation)
{
var pickup = Instantiate(this.pickup);
pickup.transform.position = position;
pickup.transform.eulerAngles = rotation;
pickup.Setup(this, number);
pickup.SetPickupID(pickupID);
return pickup;
}
public Sprite GetIcon()
{
return icon;
}
And in Pickup.cs created Unique ID and two methods
Summary
public class Pickup : MonoBehaviour
{
// STATE
InventoryItem item;
int number = 1;
public string pickupID = null;
// CACHED REFERENCE
Inventory inventory;
// LIFECYCLE METHODS
private void Awake()
{
var player = GameObject.FindGameObjectWithTag("Player");
inventory = player.GetComponent<Inventory>();
}
private void Start()
{
if (String.IsNullOrEmpty(pickupID))
{
pickupID = Guid.NewGuid().ToString();
}
}
// PUBLIC
/// <summary>
/// Set the vital data after creating the prefab.
/// </summary>
/// <param name="item">The type of item this prefab represents.</param>
/// <param name="number">The number of items represented.</param>
public void Setup(InventoryItem item, int number)
{
this.item = item;
if (!item.IsStackable())
{
number = 1;
}
this.number = number;
}
public string GetPickupID()
{
return pickupID;
}
public string SetPickupID(string restoreID)
{
pickupID = restoreID;
return pickupID;
}
public InventoryItem GetItem()
{
return item;
}
public int GetNumber()
{
return number;
}
public void PickupItem()
{
bool foundSlot = inventory.AddToFirstEmptySlot(item, number);
if (foundSlot)
{
Destroy(gameObject);
}
}
public bool CanBePickedUp()
{
return inventory.HasSpaceFor(item);
}
}