This huge inventory creation tutorial is a part of even bigger guide about How To Make A Game, which I recommend you to read before following this lesson.

In our previous lesson we added a third person camera to our character that follows the player and looks at the mouse all the time.

In this article we will go through implementing efficient and functional inventory system to our 3D game.

Introduction

Game designers often consider inventory system easy to create and implement, yet they fail when attempting.

Why does that happen? Because it’s not as easy as they thought? Or because it involved a lot of elements of various type and cross-script compatibility?

Lets find out…

This is the result you can expect.

Good, fresh start for our game!

Why Inventory Is Hard

Inventory in itself, isn’t hard at all. It’s just passing object references, spawning item’s model and item’s inventory image when appropriate, and creating some drag and drop behavior.

On their own, these things are very simple to achieve.

The hard part is making all required elements work with each other and interconnecting them in a way that doesn’t result in error after error.

That’s because imagining, let alone inventing and coding such inventory system takes 105% of focus from us and our tired brains.

No wait. It actually takes 130%.

Because to create an error-less plan that we can follow when programming it, we have to focus on ton of scripts, elements, objects and potential bugs in the same time.

There’s so many of them that the task changes from easy and simple to hard and complex.

The Best Traits Of This System

When programming this inventory, I wasn’t looking for anyone’s advice.

I had my own idea that I wanted to try and see if it works.

After rewriting the whole system few times, I eventually made it to the point where I can say that I am proud.

Now I am absolutely satisfied of how functional this system is yet how it doesn’t have much code at all.

Before, I considered using some free asset for my game. And in fact, I found one, great asset. It did everything I wanted, and more. It however wasn’t perfect. The amount of files, scripts and code it contained, was absolutely unnecessary huge, and whole asset was complex, despite having top rating in asset store.

That’s why I made my own inventory, and here’s why you will love it:

  • Only 5 small script files, without limiting functionality.
  • Offers everything that any other inventory systems have.
  • Contains Drag & Drop feature.
  • Can rearrange items in inventory.
  • When picking item’s model object, it disappears and image spawns in inventory. When throwing item out, image destroys and model object appears under our character.
  • Picking up item with “E” (close to potential WASD movement) and toggling window on/off with “I” (first letter of inventory). Very intuitive.
  • Optimized, doesn’t contain laggy code and solutions.
  • Fast to implement and easy to extend due to ease of code readability.

This tutorial includes writing scripts, adding components and adjusting them, setting references in inspector, and designing our inventory visually with canvas and other UI elements.

Programming Our Inventory

Let’s start from coding. That’s because we will be later duplicating our inventory slots after we set up one correctly, and to do it we have to first write our scripts.

This inventory system contains five scripts:

  • CharacterPickUp – code for detecting 3D collision with item object model, picking item up, hiding it and spawning it’s UI image in inventory.
  • Item – base class for any item object, contains their names and 2D images for inventory. Can also contain more RPG variables like durability, type, damage.
  • DraggingSystem – code for dragging inventory items that we will later assign to canvas. Contains methods for inserting items inside (dragging ends), taking them out (dragging starts), and attaching them to mouse in-between.
  • SlotLogic – detecting when mouse dragged item to this particular inventory slot, makes calls to DraggingSystem to insert item or take it out (and therefore start drag and drop mode) depending on variables.
  • SlotItemLogic – detects when mouse dragged object outside of inventory and clicked. If this condition is met then it drops item on floor (removes UI image and spawns object near our character).

And that’s it.

CharacterPickUp

Start from class variables:

public DraggingSystem draggingSystem;
public GameObject inventory;
public GameObject slotItemPrefab;

GameObject itemObject;
GameObject slot;
GameObject slotItem;

bool canPickUp = false;

It has 3 public variables that we will have to set in inspector.

It also has 4 private variables. 3 of type GameObject that contain information about item scene object, slot, and slot item (could be also called dragged/UI item or item image). Also contains one boolean that changes to true if we are colliding with item and there are slots in inventory.

Now detecting collision:

void OnTriggerEnter(Collider otherCollider)
{
    if (otherCollider.gameObject.tag == "Item")
    {
        canPickUp = true;
        itemObject = otherCollider.gameObject;

    }
}

void OnTriggerExit(Collider otherCollider)
{
    if (otherCollider.gameObject.tag == "Item")
    {
        canPickUp = false;
    }
}

To get this working we need collider component (we will later add it) added and set to trigger these methods.

Component makes calls to these functions when we touch any other collider during game.

This is tricky because they will be also called when we touch ground and other things, but we only want our code to work when the object we touched is an collectable item.

So the most straight-forward way to detect which kind of object we interact with is to use their tags. We will later add tag to our item.

These methods use colliders components as parameters so we get their gameObject and then their tag.

Checking input:

void LateUpdate()
{
    if (Input.GetKeyDown(KeyCode.E) && canPickUp == true)
    {
        SearchEmptySlot();
    }
}

It detects every frame if we are colliding with item (with variable that we’ve set previously) and if we pressed “E”.

Then it proceeds to search first empty slot in our inventory:

void SearchEmptySlot()
    {
        for (int i = 1; i <= 8; i++)
        {
            slot = inventory.transform.GetChild(i).gameObject;

            if (slot.transform.childCount == 0)
            {
                SpawnSlotItem();

                break;
            }
        }
    }

Simple for loop that performs its code up to 8 times (8 slots).

Inside loop we assign children of our backpack to temporary local variable slot. Our slots are the children we are talking about. And everyone of them contains their index number starting from 1 to 8. That’s because first child in our backpack is not an slot but backpack’s window title UI element. And index starts from 0. That’s why we start our loop from index 1.

After we’ve found the slot by its index, we proceed to check if it has any children. If the number of children under the slot is 0 (so we know it doesn’t contain item, otherwise would be 1), then we call next method.

The next method is used to spawn item image in our inventory:

void SpawnSlotItem()
{
    slotItem = Instantiate(slotItemPrefab);
    slotItem.GetComponent<Image>().sprite = itemObject.GetComponent<Item>().sprite;
    slotItem.GetComponent<SlotItemLogic>().itemObject = itemObject;

    InsertSlotItem();
}

First line spawns an object instance from its prefab (prefab is like a blueprint for this kind of items). What we create here is image of our item.

Second line gets reference to image component of our newly spawned 2D version of our item, then sets it as the sprite previously set for this particular item object. Notice we are calling the script Item to get its image.

Third line is used to save object reference in our slot. So when we decide to get item back to life, it knows which item is it.

Then we call method for inserting our spawned UI element inside our backpack:

void InsertSlotItem()
{
    draggingSystem.currentSlot = slot;
    draggingSystem.draggedItem = slotItem;
    draggingSystem.AddItem();

    ParentAndHideObject();
}

This is quite simple.

We are just passing information about which slot was detected as empty by our script.

We also pass reference to the newly created item. We assign it to variable dragged item, because that’s the variable our system uses for inserting items inside slots during drag and drop mode.

Then we call method AddItem which we will describe later as its on other script.

In the end we call ParentAndHideObject():

void ParentAndHideObject()
{
    Debug.Log("Acquired: " + itemObject.name + "!");
    itemObject.transform.SetParent(gameObject.transform);

    itemObject.SetActive(false);

    canPickUp = false;
}

This deactivates the scene item object and sets our character as its parent. So this way we can later easily unparent it and activate when we decide to get rid of this item from our bag.

Item

This class is as empty as it could be. All we needed it to do was to store two variables:

  • Name.
  • Image that we will display in inventory after acquiring item.

This is the code:

public string name;
public int durability;
public Sprite sprite;

You can, and should, however, include many more settings here.

Whether item is weapon or armor, or something else, its damage and price, and so on, could and should be set here.

Eventually you can add additional classes like this for every type (armors, weapons, consumables). But everything can also be included in one script.

DraggingSystem

First, class variables:

public bool dragMode;

public GameObject draggedItem;
public GameObject currentSlot;
public GameObject inventory;

They are public because they are read and set by other scripts and objects (items, slots, and character).

So our first method is for adding items:

public void AddItem()
{
    currentSlot.GetComponent<SlotLogic>().spotTaken = true;

    draggedItem.transform.SetParent(currentSlot.transform);

    draggedItem.transform.position = currentSlot.transform.position;

    dragMode = false;
}

This is called whenever we decide to drag our item from one slot to another, or when item was picked up and spawned by character.

First line locks the current slot in use by marking it as taken. So we can’t later add any more items here.

Second line sets parent of UI item element. The parrent is our current slot in use.

Third line changes position of our item to match our item position.

Fourth line turns off drag and drop.

Then lets write method for removing items:

public void RemoveItem()
{
    currentSlot.GetComponent<SlotLogic>().spotTaken = false;

    draggedItem.transform.SetParent(gameObject.transform.GetChild(0).transform);

    dragMode = true;
}

This gets called whenever we get take item out of its slot (and attach it to mouse later).

First line sets spot to not taken.

Second line sets first child of this object as parent of our item. First child of this element is Inventory, since this element is Canvas. You could also parent it under canvas though becase later we will be adding character equipment window and so on.

Now toggling our inventory and attaching item to mouse:

void LateUpdate()
{
    if (Input.GetKeyDown(KeyCode.I))
    {
        if (inventory.activeSelf)
        {
            inventory.SetActive(false);
        }
        else
        {
            inventory.SetActive(true);
        }
    }

    if (dragMode)
    {
        draggedItem.transform.position = Input.mousePosition;
    }
}

First if statement just toggles the inventory on and off depending if its active or not.

Second if statement attaches dragged item to mouse during drag and drop.

SlotLogic

Thats the script for slots in our bag.

First, class variales:

public DraggingSystem draggingSystem;
public bool spotTaken;

GameObject draggedItem;

bool collidingNow;

First variable is reference to DraggingSystem.

Second line is used to determine whether this particular spot is taken or not. If its taken we won’t be able to insert another item in it.

Then two private variables, one for dragged item and the other for checking whether the item is colliding with our slot. We need the latter variable to know if we can insert it.

Now detecting collision and Start() method:

void Start()
{
    if (transform.childCount != 0)
    {
        spotTaken = true;
    }
}

void OnTriggerEnter2D(Collider2D itemCollider)
{
    collidingNow = true;

    draggedItem = itemCollider.gameObject;
}

void OnTriggerExit2D()
{
    collidingNow = false;
}

On Start() we check if slot has any child. If not, then its empty. Useful for when we start saving our game state or if we decide to add some starting items to our character even before game starts.

Then two methods for detecting collision with item dragged by mouse. If we detect collision then we set variable to true and we also assign gameObject of our local parameter itemCollider to public variable.

Now calling DraggingSystem():

public void SpotClicked()
{
    if (spotTaken && !draggingSystem.dragMode)
    {
        draggingSystem.currentSlot = gameObject;
        draggingSystem.draggedItem = transform.GetChild(0).gameObject;
        draggingSystem.RemoveItem();
    }
    else if (!spotTaken && draggingSystem.dragMode && collidingNow)
    {
        draggingSystem.currentSlot = gameObject;
        draggingSystem.AddItem();
    }
}

This method is called when the slot is clicked.

First if statement executes if spot is taken and if drag and drop is turned off.

It means we are not currently dragging anything and there’s an item inside slot.

So we can remove the item from slot and attach it to mouse.

Before calling the RemoveItem() method we first pass references about what slot we are currently using and about what item are we dragging.

Second if statement executes if spot is not taken, drag and drop is turned on, and dragged item is colliding currently with this particular slot instance.

In first line we pass reference of this particular slot.

And in second we call method AddItem() to insert the item.

SlotItemLogic

This is the script for our UI version of item – the one that we drag from one slot and drop in another, insert as armor in our eventual equipment window, etc.

First class variables:

public GameObject itemObject;
public Image image;

DraggingSystem draggingSystem;

Collision stays:

void OnTriggerStay2D(Collider2D inventoryCollider)
{
    if (inventoryCollider.gameObject.name == "Inventory" && draggingSystem == null)
    {
        draggingSystem = inventoryCollider.gameObject.transform.parent.GetComponent<DraggingSystem>();
    }

    if (inventoryCollider.gameObject.name == "Inventory")
    {
        image.raycastTarget = false;
    }
}

Since this script is placed on prefab, we can’t get a reference the normal way through object inspector.

So instead, in first if statement we check during collision if the colliding object is called Inventory.

And we also check if reference is not set yet.

Then we assign the object we collided with as the reference.

In second if statement we check if our 2D item is colliding with inventory. We have to set proper size in our box collider component if it doesn’t pick up collision properly.

If we are colliding with it, then we turn off target raycasting of our slot.

When its turned off, the mouse clicks are passed through it. So we know that the slot is clicked instead.

So when our slot is dragged somewhere inside inventory, and we click on a slot, with raycasting turned off, the slot is clicked instead, not the item. If item was clicked, then we wouldn’t be able to trigger slot’s OnClick() event.

Collision exits:

void OnTriggerExit2D(Collider2D inventoryCollider)
{
    if (inventoryCollider.gameObject.name == "Inventory" && draggingSystem == null)
    {
        draggingSystem = inventoryCollider.gameObject.transform.parent.GetComponent<DraggingSystem>();
    }

    if (inventoryCollider.gameObject.name == "Inventory")
    {
        image.raycastTarget = true;
    }
}

This is for opposite situation. When we drag item outside of inventory, we want the target raycasting to be turned on.

So when we click and item is outside, it drops on the floor.

Dropping items:

public void DropItem()
{
    if (draggingSystem.dragMode)
    {
        Debug.Log("Dropped: " + itemObject.name + "!");

        itemObject.SetActive(true);
        itemObject.transform.parent = null;
        itemObject.transform.position = new Vector3(0, 1, 0);

        draggingSystem.dragMode = false;
        draggingSystem.currentSlot = null;
        draggingSystem.draggedItem = null;

        Destroy(gameObject);
    }
}

If we are currently during drag and drop and this method is triggered by on click event, then it unparents the item from our character, activates it (so its not invisible anymore), and sets its position right next to our character with one point above ground so it doesn’t spawn under ground and fall.

We also cancel all the important variables by setting them to false and null when we drop items. We don’t want the drag mode to still be turned on after we drop the item, right?

Then we destroy 2D image of our 3D item (here, weapon). It will spawn again if we decide to pick up item.

And that’s it. Our scripts are done.

Creating Needed Elements

Scripts are made. We are not done yet though.

What’s left:

  • UI elements – we have to create a structure of UI elements in our scene, such as canvas, panels, images.
  • Item – we need to have item object with its model, and its 2D image. You can screenshot the model.
  • Scripts – scripts are made, but we need to assign them properly.
  • Components – Our objects need to have proper componets added, such as colliders.
  • Prefabs – we need to make our slot a prefab. We also need a prefab for every item object.
  • References – remember these public variables? We need to set them in inspector.

Lets get started!

Desining UI Element Structure

So first we need to create Canvas object in hierarchy. Inside it we can place any other UI element.

Inside the canvas, we need few panels or images. If you want round edges, go for panels. Images have square edges. I prefer panels.

So create panel and call it Inventory.

Inside Inventory create panel and call it Title.

Inside Title create text and leave its default name but change its text to “Inventory”.

Now create another panel inside Inventory and call it Slot (1).

Friendly reminder that you can design your inventory however you want.

This is how my design looks like:

To achieve similar look you need to play with panels, colors white and black, and with their alpha level.

And this is the hierarchy structure:

Now create another panel inside first slot and call it SlotItem. We will later adjust it, then set it as prefab, then remove from scene.

Item Model And Image

You can easily make item models in Blender.

But for now lets set a basic 3D shape as our weapon:

  1. Create a capsule.
  2. Set its size in a way so it looks like a wooden pole.
  3. Name the object Wooden Sword.

Now to get image:

  1. Place the sword somewhere on scene.
  2. Screenshot it.
  3. Cut it in paint if you need.
  4. Import the sword screen to your game.
  5. Name it WoodenSwordIcon.
  6. Select it in Project window, then in Inspector choose “Sprite (2D and UI)” as texture type.
  7. Press Apply.

Now we can use the image as background for our UI elements!

Adding Scripts To Objects

We will need to drag our objects with scripts to some components fields, so lets add scripts first.

Canvas

Here, drag Dragging System into inspector. You don’t need to set references yet.

Slot (1)

Drag Slot Logic here.

Slot Item

Drag Slot Item logic here.

Wooden Sword

Drag Item script here.

Character

Drag Character Pick Up script here.

Adding Components To Objects

All our objects are made, and scripts are assigned, so now we can assign components to them.

Inventory

Our Inventory object needs to have these components:

  • Image.
  • Box Collider 2D.

On collider, make sure you have Is Trigger checked. Also press Edit Collider and make it cover whole inventory rectangle. You can just set same size as inventory has.

This is the main rectangle.

Title

Only one component:

  • Image.

With desired settings.

Also under Title object, add a text if you didn’t, adjust it however you want, and set the value of text to “Inventory”.

Slot (1)

Components you need:

  • Image.
  • Box Collider 2D.
  • Button.

Again, adjust image however you want.

Check Is Trigger on collider. My offset is 0 and size is 50.

Add one on click event in button component of Slot (1).

Drag the Slot (1) object from hierarchy to it.

And select the function SlotLogic.SpotClicked.

Slot Item

You need:

  • Image.
  • Box Collider 2D.
  • Rigidbody 2D.
  • Button.

Turn Raycast Target to off in Image.

Set Is Trigger In Box Collider 2D.

Add On Click event in Button.

Drag Slot Item there, and select SlotItemLogic.DropItem.

Wooden Sword

Add:

  • Rigidbody.
  • Capsule Collider.

That’s it.

Making Prefabs

To make prefabs, you need to drag scene objects from Hierarchy to our Project window.

Make prefab for:

  • Slot Item.
  • Wooden Sword.

Remove Slot Item from hierarchy after.

Assigning References:

Canvas:

Slot (1):

Wooden Sword:

Character:

Slot Item (prefab):

Summary

If you went through this tutorial while paying attention, and did everything as I said, you can test your game, it should work now!

Pick up item with “E” once you’re near it, and launch Inventory with “I”.

This is made in very simple way so you won’t have issues extending it.

It’s optimized, but further optimization is also possible.

The world of indie gamedev is the world of workarounds, of creating best solutions, of rewriting your systems many times, of writing readable code, and most importantly of being proud your work.

If you feel like my Inventory is not enough for you, then that’s even better!

Because you can improve it in many ways.

You can make it require less code, you can find better variable names, perhaps you can obtain references in better way, and you can write more clean code.

Every lesson in this guide teaches you additional things that you didn’t know about before.

Once you learn many things, writing your own, complex systems and features becomes easier. With time, you will stop googling solutions and instead writing your own!

In next chapter we will implement simple enemy AI behavior, RPG combat system with chasing and healthbars.

1 Comment

  1. This is 3rd inventory tutorial i am attempting as they all failed at some point. Your worked, the inventory is great. Nice design too. But could you please release the objects and their settings? I would like to have the same design/colors/sizes/elements as you.

Post A Comment