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