Different way of saving dropped Pickups

Hey, there I would like to ask if it wouldn’t be more convenient if we were to save the state as a Dictionary with a key as scenesBuildIndex and the value of a List of Pickups so we won’t have to do the merging and filtering the Pickups?

1 Like

That’s an interesting idea. I’m not positive if it would be a better system or not. I’d be interested in seeing the code you come up with to manage it.

Here is the modification to the code (also I ment a List of DropRecord instead of Pickups for saving). I tried the game, I got no errors in console and the bug no longer seems to be. (maybe because I wasn’t trying to break it :laughing:)

using System.Collections.Generic;
using UnityEngine;
using GameDevTV.Saving;
using UnityEngine.SceneManagement;

namespace GameDevTV.Inventories
{
    /// <summary>
    /// To be placed on anything that wishes to drop pickups into the world.
    /// Tracks the drops for saving and restoring.
    /// </summary>
    public class ItemDropper : MonoBehaviour, ISaveable
    {
        // STATE
        private List<Pickup> currentSceneDroppedItems = new List<Pickup>();
        private Dictionary<int, List<DropRecord>> droppedItemsRecords = new Dictionary<int, List<DropRecord>>();

        // 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)
        {            
            Pickup pickup = item.SpawnPickup(spawnLocation, number);            
            currentSceneDroppedItems.Add(pickup);            
        }

        [System.Serializable]
        private struct DropRecord
        {
            public string itemID;
            public SerializableVector3 position;
            public int number;
        }

        object ISaveable.CaptureState()
        {
            RemoveDestroyedDrops();
            int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
            List<DropRecord> newDropRecords = new List<DropRecord>();
            foreach (Pickup pickup in currentSceneDroppedItems)
            {
                DropRecord droppedItem = new DropRecord();
                droppedItem.itemID = pickup.GetItem().GetItemID();
                droppedItem.position = new SerializableVector3(pickup.transform.position);
                droppedItem.number = pickup.GetNumber();
                newDropRecords.Add(droppedItem);
            }
            droppedItemsRecords[currentSceneIndex] = newDropRecords;
            return droppedItemsRecords;            
        }

        void ISaveable.RestoreState(object state)
        {
            int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
            Dictionary<int, List<DropRecord>> droppedItems = (Dictionary<int, List<DropRecord>>)state;
            if (!droppedItems.ContainsKey(currentSceneIndex))
            {
                droppedItemsRecords = droppedItems;
                return;
            }

            // Spawn Pickups for given Scene
            foreach (DropRecord dropRecord in droppedItems[currentSceneIndex])
            {
                InventoryItem pickupItem = InventoryItem.GetFromID(dropRecord.itemID);
                Vector3 position = dropRecord.position.ToVector();
                int amount = dropRecord.number;
                SpawnPickup(pickupItem, position, amount);
            }
            droppedItemsRecords = droppedItems;
        }

        /// <summary>
        /// Remove any drops in the world that have subsequently been picked up.
        /// </summary>
        private void RemoveDestroyedDrops()
        { 
            List<Pickup> newDroppedItemsList = new List<Pickup>();
            foreach (Pickup pickup in currentSceneDroppedItems)
            {
                if (pickup != null)
                {
                    newDroppedItemsList.Add(pickup);
                }
            }
            currentSceneDroppedItems = newDroppedItemsList;            
        }
    }
}

When I gave the bug a closer look only the player´s item drops are the problem since his Saveable Entity has the same ID amongst different scenes so his drops will spawn anywhere :smiley:

1 Like

Well done! I’ve pored through this line by line three times, and it looks sound1

Thank you very much for the praise and going through the code :slight_smile: I was also thinking about making a singleton class that keeps track of all the Drops and the class ItemDropper would only take care of dropping the items and the class “ItemDropsManager” would then take care of saving, loading and destroying them but so far Im happy with this solution already :smiley:

Yeah, I think that would be over-engineering, not to mention adding another Singleton to the project. (Despite the lecture title “Avoiding Singletons”, the PersistentObjectsPrefab is just another form of Singleton).

1 Like

This topic was automatically closed after 19 hours. New replies are no longer allowed.

Privacy & Terms