In this topic, we will explore ways that we can add inventories to NPCs and Treasure chests that you can open just like the Player Inventory. Our UI is centered around the Player’s inventory, but any GameObject can have an inventory. We just need to make some modifications to our UI to accomodate this.
Start by cloning the Inventory panel. Move your new Inventory panel into a position you like. For my purposes, I used the same coordinates as the Player’s inventory, but -1250 in the X. Now the two inventories are side by side. If your game scales, you may have to make other adjustments.
Once we have the new inventory in place, we need to make some changes to a few scripts. I don’t see the point in creating a new InventoryUI script when we have a perfectly good script already… But it needs a little help.
Let’s start by renaming the variable Inventory playerInventory; to selectedInventory; You can do this in Visual Studio Code by putting the cursor over the word playerInventory, and pressing F2 and typing in the new name. This will just make a little more sense as we go over the changes to our code, since we want our UI to be able to handle anybody’s inventory.
Next is adding a couple of fields to our //CONFIG DATA
// CONFIG DATA
[SerializeField] InventorySlotUI InventoryItemPrefab = null;
[SerializeField] bool isPlayerInventory = true;
[SerializeField] TextMeshProUGUI Title;
At this point, you can save and drag the Text field under the title into the Title field in InventoryUI’s inspector. (The InventoryUI component, by the way, is on the object in the Inventory’s heirarchy named Inventory Items. Uncheck the box labed Is Player Inventory. Since we’ve defaulted to true, we shouldn’t need to do anything to our original PlayerInventory.
Now back to our InventoryUI script:
The way our InventoryUI is set up now, in Awake, it looks for the Player’s inventory and sets everything up. Then in Start, it redraws the inventory. We don’t want this behavior, because this new window won’t be drawing the Player’s inventory. Wrap the Awake and Start content with if statements.
// LIFECYCLE METHODS
private void Awake()
{
if (isPlayerInventory)
{
selectedInventory = Inventory.GetPlayerInventory();
selectedInventory.inventoryUpdated += Redraw;
}
}
private void Start()
{
if(isPlayerInventory)
{
Redraw();
}
}
This still leaves us needing a way to specify which inventory we will be drawing.
public bool Setup(GameObject user)
{
if (user.TryGetComponent(out selectedInventory))
{
selectedInventory.inventoryUpdated += Redraw;
Title.text = selectedInventory.name; //perhaps add a field to Inventory for a displayname
Redraw();
return true;
}
return false;
}
For this example, I’m just using the name of the GameObject the container is on… This would require you to be creative with naming your enemies and treasure chests. You could add a field in Inventory.cs with a display name if you wish and use that instead.
I made the Setup a bool, in case for some reason there isn’t a valid inventory on the GameObject. We’ll use that later in the this post.
This works by checking to see if the object has an Inventory. If it does, it sets the selectedInventory, subscribes to the changes, and immediately redraws the inventory (which it will do off of our new inventory).
If you’re using a version if Unity prior to 2019, you’ll need to adjust the setup to the classic check
Inventory selectedInventory = user.GetComponent<Inventory>();
if(selectedInventory!=null)
{
That’s it for our changes to InventoryUI. Redraw is keyed already to setup everything with the currently selected inventory in mind.
We do, however, need a way to show our container’s inventory. Since we want the inventory to open automatically when we select an item with an inventory, we’re going to make some modifications to ShowHideUI.cs.
First, we need a field for our InventoryUI.
using GameDevTV.UI.Inventories;
using UnityEngine;
namespace GameDevTV.UI
{
public class ShowHideUI : MonoBehaviour
{
[SerializeField] KeyCode toggleKey = KeyCode.Escape;
[SerializeField] GameObject uiContainer = null;
[Serializefield] GameObject otherInventoryContainer=null;
[SerializeField] InventoryUI otherInventoryUI = null;
Drag the OtherInventoryPanel’s root into the ShowHideUI script field otherInventoryContainer. (You can find the ShowHideUI script on the root of the UI Canvas.
Drag the InventoryItems object into the ShowHideUI script field otherInventoryUI.
You’ll need to add the line otherInventoryContainer.SetActive(false);
to both the Start() method and inside the if code block in Update();
Believe it or not, we’re almost done setting up our system.
public void ShowOtherInventory(GameObject go)
{
uiContainer.SetActive(true);
otherInventoryContainer.SetActive(true);
otherInventoryUI.Setup(go);
}
This method will automatically open our inventory panel, and active the OtherInventory, setting it up with the selected GameObject.
You may need to also add a specfic finding method if you’re going to use more than one ShowHideUI in your scene. Otherwise, to call this method you just need to
FindObjectOfType<ShowHideUI>().ShowOtherInventory(gameObject);
I’ll leave the decision of when to open other inventories up to you, because it will depend a lot on your own setup. A great example might be to put an IRayCastable component on the GameObject which then opens the inventory.
You also need to have a way to add inventory items to the other inventory. If you want the inventory to save, make sure that there is a SaveableEntity on the GameObject.
A great use of this would be for a player “Bank”… a place that the player can store and retrieve items… If you click on the bank, it will open the window, the player can add and replace items as wanted… by naming the SaveableEntity “Bank” in all scenes, this bank will maintain it’s stock through the game.
Here’s a simple IRayCastable to handle this.
using GameDevTV.UI;
using RPG.Control;
using UnityEngine;
namespace GameDevTV.Inventories
{
[RequireComponent(typeof(Inventory))]
public class OtherInventory : MonoBehaviour, IRaycastable
{
public CursorType GetCursorType()
{
return CursorType.Pickup;
}
public bool HandleRaycast(PlayerController callingController)
{
if (Input.GetMouseButtonDown(0))
{
FindObjectOfType<ShowHideUI>().ShowOtherInventory(gameObject);
}
return true;
}
}
}
Place this on an object in your scene along with a SaveableEntity script. Change the SaveableEntity’s id to “Bank”. Prefab this and put it in each of your scenes so your player can access his “Bank”.