Problem with pick up items

I have a problem with clickable pickup items, when i click on an item it go into my inventory (and it’s ok) but if i go with the cursor in the point where was that item it is still in the pickup mode and if i click it get the next item near it also if it is a bit away from that point and sometimes appear a NullReferenceError when i pass over objects without click them…

NullReferenceException: Object reference not set to an instance of an object
RPG.Inventories.Inventory.FindStack (RPG.Inventories.InventoryItem item) (at Assets/Scripts/Inventories/Inventory.cs:106)
RPG.Inventories.Inventory.FindSlot (RPG.Inventories.InventoryItem item) (at Assets/Scripts/Inventories/Inventory.cs:97)
RPG.Inventories.Inventory.HasSpaceFor (RPG.Inventories.InventoryItem item) (at Assets/Scripts/Inventories/Inventory.cs:27)
RPG.Inventories.Pickup.CanBePickedUp () (at Assets/Scripts/Inventories/Pickup.cs:38)
RPG.Control.ClickablePickup.GetCursorType () (at Assets/Scripts/Control/ClickablePickup.cs:18)
RPG.Control.PlayerController.InteractWithComponent () (at Assets/Scripts/Control/PlayerController.cs:74)
RPG.Control.PlayerController.Update () (at Assets/Scripts/Control/PlayerController.cs:42)

This is my inventory script:

using RPG.Saving;
using System;
using UnityEngine;

namespace RPG.Inventories
{
    public class Inventory : MonoBehaviour, ISaveable
    {
        [SerializeField] int inventorySize = 16;
        InventorySlot[] slots;
        public event Action inventoryUpdate;

        public struct InventorySlot
        {
            public InventoryItem item;
            public int number;
        }

        public static Inventory GetPlayerInventory()
        {
            var player = GameObject.FindWithTag("Player");
            return player.GetComponent<Inventory>();
        }

        public bool HasSpaceFor(InventoryItem item)
        {
            return FindSlot(item) >= 0;
        }
        public int GetSize()
        {
            return slots.Length;
        }
        public bool AddToFirstEmptySlot(InventoryItem item, int number)
        {
            int i = FindSlot(item);

            if (i < 0) return false;

            slots[i].item = item;
            slots[i].number += number;
            if (inventoryUpdate != null)
                inventoryUpdate();

            return true;
        }
        public bool HasItem(InventoryItem item)
        {
            for (int i = 0; i < slots.Length; i++)
            {
                if (object.ReferenceEquals(slots[i], item))
                    return true;
            }
            return false;
        }
        public InventoryItem GetItemInSlot(int slot)
        {
            return slots[slot].item;
        }
        public int GetNumberInSlot(int slot)
        {
            return slots[slot].number;
        }
        public void RemoveFromSlot(int slot, int number)
        {
            slots[slot].number -= number;
            if (slots[slot].number <= 0)
            {
                slots[slot].number = 0;
                slots[slot].item = null;    
            }
            if (inventoryUpdate != null)
                inventoryUpdate();
        }
        public bool AddItemToSlot(int slot, InventoryItem item, int number)
        {
            if (slots[slot].item != null)
                return AddToFirstEmptySlot(item, number);

            var i = FindStack(item);
            if (i >= 0)
                slot = i;

            slots[slot].item = item;
            slots[slot].number += number;
            if (inventoryUpdate != null)
                inventoryUpdate();

            return true;
        }

        private void Awake()
        {
            slots = new InventorySlot[inventorySize];
        }
        private int FindSlot(InventoryItem item)
        {
            int i = FindStack(item);
            if (i < 0)
                i = FindEmptySlot();

            return i;
        }

        private int FindStack(InventoryItem item)
        {
            if (!item.IsStackable())
                return -1;

            for(int i = 0; i< slots.Length; i++)
            {
                if (ReferenceEquals(slots[i].item, item))
                    return i;
            }

            return -1;
        }

        private int FindEmptySlot()
        {
            for(int i = 0; i < slots.Length; i++)
            {
                if (slots[i].item == null)
                    return i;
            }
            return - 1;
        }
        [System.Serializable]
        private struct InventorySlotRecord
        {
            public string itemID;
            public int number;
        }
        object ISaveable.CaptureState()
        {
            var slotRecords = new InventorySlotRecord[inventorySize];
            for (int i = 0; i < inventorySize; i++)
            {
                if (slots[i].item != null)
                {
                    slotRecords[i].itemID = slots[i].item.GetItemID();
                    slotRecords[i].number = slots[i].number;
                }
            }
            return slotRecords;
        }

        void ISaveable.RestoreState(object state)
        {
            var slotRecords = (InventorySlotRecord[])state;
            for (int i = 0; i < inventorySize; i++)
            {
                slots[i].item = InventoryItem.GetFromID(slotRecords[i].itemID);
                slots[i].number = slotRecords[i].number;
            }
            if (inventoryUpdate != null)
                inventoryUpdate();
        }
    }
}

This is my Pickup script:

using UnityEngine;

namespace RPG.Inventories
{
    public class Pickup : MonoBehaviour
    {
        InventoryItem item;
        int number;
        Inventory inventory;

        private void Awake()
        {
            var player = GameObject.FindWithTag("Player");
            inventory=player.GetComponent<Inventory>();
        }

        public void Setup(InventoryItem item, int number)
        {
            this.item = item;
            this.number = number;
        }
        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);
        }
    }
}

This is my clickable pickup script:

using UnityEngine;
using RPG.Inventories;

namespace RPG.Control
{
    [RequireComponent(typeof(Pickup))]
    public class ClickablePickup : MonoBehaviour, IRaycastable
    {
        Pickup pickup;

        private void Awake()
        {
            pickup = GetComponent<Pickup>();
        }

        public CursorType GetCursorType()
        {
            if (pickup.CanBePickedUp())
                return CursorType.Pickup;
            else
                return CursorType.FullPickup;
        }

        public bool HandleRaycast(PlayerController callingController)
        {
            if (Input.GetMouseButtonDown(0))
                pickup.PickupItem();

            return true;
        }
    }
}

And at the end this is my player controller script:

using RPG.Movement;
using RPG.Attributes;
using System;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.EventSystems;

namespace RPG.Control
{
    public class PlayerController : MonoBehaviour
    {
        [System.Serializable]
        struct CursorMapping
        {
            public CursorType type;
            public Texture2D texture;
            public Vector2 hotspot;
        }

        [SerializeField] CursorMapping[] cursorMappings = null;
        private CursorType currentCursor = CursorType.None;
        [SerializeField] float maxNavMeshProjectionDistance = 1f;
        [SerializeField] float raycastRadius = 1f;


        Health health;
        bool isDraggingUI = false;
        private void Awake()
        {
            health = GetComponent<Health>();
        }
        private void Update()
        {
            CheckSpecialAbilitiesKeys();
            if (InteractWithUI()) return;
            if (health.IsDead())
            {
                SetCursor(CursorType.None);
                return;
            }

            if (InteractWithComponent()) return;
            if(InteractWithMovement()) return;

            SetCursor(CursorType.None);
        }

        private bool InteractWithUI()
        {
            if (Input.GetMouseButtonUp(0))
                isDraggingUI = false;
            if (EventSystem.current.IsPointerOverGameObject())
            {
                if(Input.GetMouseButtonDown(0))
                    isDraggingUI = true;
                SetCursor(CursorType.UI);
                return true;
            }
            if(isDraggingUI)
                return true;
            return false;
        }

        private bool InteractWithComponent()
        {
            RaycastHit[] hits = RayCastAllSorted();
            foreach (RaycastHit hit in hits)
            {
                IRaycastable[] raycastables = hit.transform.GetComponents<IRaycastable>();
                foreach(IRaycastable raycastable in raycastables)
                {
                    if (raycastable.HandleRaycast(this))
                    {
                        SetCursor(raycastable.GetCursorType());
                        return true;
                    }
                }
            }
            return false;
        }

        RaycastHit[] RayCastAllSorted()
        {
            RaycastHit[] hits = Physics.SphereCastAll(GetMouseRay(), raycastRadius);

            float[] distances = new float[hits.Length];
            for(int i = 0; i < hits.Length; i++)
                distances[i] = hits[i].distance;

            Array.Sort(distances, hits);

            return hits;
        }

        private bool InteractWithMovement()
        {
            bool hasHit = RaycastNavMesh(out Vector3 target);

            if (hasHit)
            {
                if (!GetComponent<Mover>().CanMoveTo(target)) return false;

                if (Input.GetMouseButton(0))
                    GetComponent<Mover>().StartMoveAction(target, 1f);

                SetCursor(CursorType.Movement);
                return true;
            }
            return false;
        }

        private bool RaycastNavMesh(out Vector3 target)
        {
            target = new Vector3();

            bool hasHit = Physics.Raycast(GetMouseRay(), out RaycastHit hit);
            if(!hasHit) return false;

            bool hasCastToNavMesh = NavMesh.SamplePosition(hit.point, out NavMeshHit navMeshHit, maxNavMeshProjectionDistance, NavMesh.AllAreas);
            if(!hasCastToNavMesh) return false;

            target = navMeshHit.position;
            return true;
        }

        private void SetCursor(CursorType type)
        {
            if (currentCursor == type) return;

            CursorMapping mapping = GetCursorMapping(type);
            Cursor.SetCursor(mapping.texture, mapping.hotspot, CursorMode.Auto);
            currentCursor = type;
        }
        private void CheckSpecialAbilitiesKeys()
        {
            var actionStore = GetComponent<ActionStore>();
            if (Input.GetKeyDown(KeyCode.Alpha1))
                actionStore.Use(0, gameObject);
            if (Input.GetKeyDown(KeyCode.Alpha2))
                actionStore.Use(1, gameObject);
            if (Input.GetKeyDown(KeyCode.Alpha3))
                actionStore.Use(2, gameObject);
            if (Input.GetKeyDown(KeyCode.Alpha4))
                actionStore.Use(3, gameObject);
        }
        private CursorMapping GetCursorMapping(CursorType type)
        {
            foreach (CursorMapping mapping in cursorMappings)
            {
                if (mapping.type == type)
                    return mapping;
            }
            return cursorMappings[0];
        }
        private static Ray GetMouseRay()
        {
            return Camera.main.ScreenPointToRay(Input.mousePosition);
        }
    }
}

Any suggestion?
Thanks

The null reference is on the FindStack() method in Inventory.

        private int FindStack(InventoryItem item)
        {
            if (!item.IsStackable())
                return -1;

            for(int i = 0; i< slots.Length; i++)
            {
                if (ReferenceEquals(slots[i].item, item))
                    return i;
            }

            return -1;
        }

Mostly likely, this error is occuring on the first line of the method

if(!item.IsStackable())

This would cause a null reference if item was null, as we’re acting on the item without null checking it first. Generally, this is not a problem, as there are only a very few circumstances in which this would be null…

Most likely, this is caused by the Pickup not being setup. Without Setup being called, item will always be null, and this null reference error will be thrown when you to pick it up.

Setup is generally called on an InventoryItem reference as item.SpawnPickup(position, number)
You can see this in action in the ItemDropper script. RandomDropper (inhereting from ItemDropper) should be calling DropItem() which gets the process rolling to spawn the pickup and set up the correct item.

On the pickups that are causing null references, how are you spawning them into the world?

i have noticed that is caused when i pass over the pumpkins with the mouse and the pumpkins are not pickable up, maybe it is that the problem…

Are you placing the Pickups directly in the world, or are you using a spawner?

Privacy & Terms