How to Build a Godot Inventory System With Resources

A Godot inventory system needs to track both what an item is and how many the player holds. Drawing on Zenva’s experience teaching coding and game development across more than 400 courses, this tutorial guides you through that split in an action RPG: item resources hold the shared properties, while inventory slots track items and quantities.

You’ll create item types, configure weapons and consumables, and write the logic for stacking, removing, and finding items. This assumes you have Godot installed, know basic GDScript, and are working with the project’s Player scene, sprites, and equipment scenes.

Download Project Files

The files and full project are available through the course included in the Intermediate Godot Mini-Degree. If you’d like the full guided path, you can follow along there, or keep reading this free tutorial to work through the item data and inventory logic.

Make a Complete Card Battler in Godot e1788415191322 - How to Build a Godot Inventory System With Resources
FREE GODOT COURSE
LEARN GODOT, UNITY, UNREAL & MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Build the Item Data for Your Godot Inventory System

Start with Godot’s resource system. You’ll define item types in scripts that extend Resource, then create individual .tres files for the items in your game.

Planning the Item Data Hierarchy

Instead of putting every item into one catch-all type, use a base class for shared properties and subclasses for distinct categories. This makes the system expandable and lets later code require a specific type, such as WeaponItemData, without accepting an arrow or health potion as an equippable weapon.

  • ItemData: the base resource, with a display name, description, icon, and maximum stack size.
  • WeaponItemData: inherits from ItemData and provides a separate type for weapons. Equipping logic is not added here.
  • ShieldItemData: inherits from ItemData and categorizes shields separately from weapons.
  • ConsumableItemData: inherits from ItemData and represents items such as health potions or food.

Creating the Base ItemData Script

In the FileSystem dock, create an Items folder inside Scripts. In that folder, create item_data.gd.

Change its base type from Node to Resource so you can save instances as resource files. Give it a global class_name so other scripts and the editor can reference the type:

class_name ItemData
extends Resource

Organizing the Project Folders

At the project root, res://, create another folder named Items. Inside it, create Weapons and Consumables folders for the resource files.

Godot FileSystem dock showing the newly created Items folder at the project root, containing Weapons and Consumables subfolders.

Creating the Arrow Resource

An arrow can use the base ItemData type because it doesn’t need the special behavior of a weapon, shield, or consumable.

  1. Right-click the Items/Weapons folder and choose New Resource….
  2. Search for ItemData, select it, and click Create.
  3. Save the file as arrow.tres.

Double-click the resource to open it in the Inspector. It has no editable properties yet, so your next step is to add them to the script.

Adding Properties to ItemData

In item_data.gd, use @export to expose the following properties in the Inspector:

  • display_name: the name shown in the inventory UI and when hovering over an item.
  • description: a short explanation of the item and what it does.
  • max_stack_size: the maximum quantity in one slot. Weapons can be limited to one, while arrows and consumables can stack higher.
  • icon: the texture that represents the item in the UI.
  • equip_scene: a packed scene for equippable items. This stays empty for arrows and consumables but is available to weapons and shields through the base class.
class_name ItemData
extends Resource

@export var display_name : String
@export var description : String
@export var max_stack_size : int = 1
@export var icon : Texture
@export var equip_scene : PackedScene

Select arrow.tres again to see all five fields.

Configuring the Arrow

Fill in the arrow’s Inspector properties:

  • Display Name: Arrow
  • Description: Shot by a bow.
  • Max Stack Size: 30
  • Icon: drag in the arrow sprite from Sprites/Items/.
  • Equip Scene: leave empty because the arrow is not equipped directly.

Godot Inspector for Arrow.tres showing Display Name "Arrow", Description "Shot by a bow.", Max Stack Size 30, an arrow icon assigned, and an empty Equip Scene.

Creating the WeaponItemData Subclass

In Scripts/Items, create weapon_item_data.gd. For now, it only needs its own class name and the ItemData base type:

class_name WeaponItemData
extends ItemData

Godot FileSystem dock showing the Scripts/Items folder containing item_data.gd and the new weapon_item_data.gd script.

The subclass gives weapons a distinct type that later equipping code can require.

Creating the ShieldItemData Subclass

Create shield_item_data.gd in the same folder. Like the weapon subclass, it establishes a category without implementing equipping behavior yet:

class_name ShieldItemData
extends ItemData

Creating the ConsumableItemData Subclass

Create consumable_item_data.gd. Consumables also need a health-gain value, which specifies how much health the player gains when using the item:

class_name ConsumableItemData
extends ItemData

@export var health_gain : int = 1

Creating the Health Potion Resource

The New Resource… dialog now lists your subclasses when you search for ItemData. In Items/Consumables, create a ConsumableItemData resource named health_potion.tres, then set its fields:

  • Health Gain: 2
  • Display Name: Health Potion
  • Description: +2 Health
  • Max Stack Size: 12
  • Icon: the health potion sprite from Sprites/Items/.
  • Equip Scene: leave empty.

Creating the Sword Resource

In Items/Weapons, create a WeaponItemData resource named sword.tres. This is your first item with an assigned equip scene:

  • Display Name: Sword
  • Description: Light weapon with quick attacks.
  • Max Stack Size: 1, so swords do not stack.
  • Icon: the sword sprite.
  • Equip Scene: weapon_sword.tscn from Scenes/Weapons/.

Your Turn

For practice, use the same resource-creation pattern for the bow and axe, choosing WeaponItemData for both. The shield needs ShieldItemData; the next sections review your weapon setup and walk through that shield configuration.

Reviewing the Axe and Bow

Check that the axe is a separate WeaponItemData resource with its display name, description, icon, and equip scene filled in.

Godot editor showing the axe resource selected in the FileSystem dock, with the Inspector panel displaying its WeaponItemData fields filled in.

The bow follows the same pattern: its own WeaponItemData resource with its own display name, description, icon, and equip scene.

Godot editor showing the bow resource selected in the FileSystem dock, with the Inspector panel displaying its WeaponItemData fields filled in.

Creating the Shield Item Data

The shield uses ShieldItemData so the game can distinguish it from weapons. To create its resource:

  1. Create a new Resource in the editor.
  2. Select ShieldItemData in the Create New Resource dialog.
  3. Save the resource with the name shield.

Godot Create New Resource dialog with ShieldItemData highlighted in the list of resource types.

Select the shield resource and fill in its Inspector properties:

  • display_name: Shield
  • description: Blocks incoming attacks
  • icon: the shield icon asset
  • equip_scene: the shield scene file

Godot editor showing the shield resource in the Inspector with display name, description, icon, and equip_scene assigned.

Next Steps

You now have a set of item resources ready for the inventory. You can extend it with more weapons, consumables, or other items; next, you’ll write the logic that stores them.

Create the Inventory Script and Item Slots

A single script will hold the inventory data and logic on a child node of the Player. It doesn’t need a visual representation or physics.

Creating the Inventory Script

Create an Inventory folder inside Scripts, then create inventory.gd inside it. Extend Node rather than Node2D, and give the script the class name Inventory:

class_name Inventory
extends Node

The ItemSlot Subclass

Before adding the inventory variables, define an inner class named ItemSlot. Each slot stores an ItemData reference in item and an integer count in quantity:

class_name Inventory
extends Node

class ItemSlot:
	var item : ItemData
	var quantity : int

Signals

Declare two signals so other parts of the game, such as the UI, can respond to changes without being tightly coupled to the inventory script:

  • UpdatedInventory: emitted when the inventory changes, including when an item is added or removed.
  • UpdatedSlot: emitted when a particular slot changes, passing that ItemSlot to listeners.

Variables and Export Fields

Next, add the slot array and the exported fields that configure the inventory:

  • item_slots: a typed array holding the inventory’s ItemSlot instances.
  • size: the starting number of slots, defaulting to 9.
  • start_items: a dictionary with ItemData resources as keys and integer quantities as values, allowing you to configure starting items in the Inspector.

With the signals and variables in place, the script looks like this:

class_name Inventory
extends Node

class ItemSlot:
	var item : ItemData
	var quantity : int

signal UpdatedInventory
signal UpdatedSlot (slot : ItemSlot)

var item_slots : Array[ItemSlot]

@export var size : int = 9
@export var start_items : Dictionary[ItemData, int]

Outlining the Inventory Functions

Outline the functions before filling in their behavior. This gives you the structure of the inventory in one place:

  1. _ready() will create the slots and add starting items when the node enters the scene tree.
  2. add_item(item) will add one item, returning true on success or false when there is no room.
  3. remove_item(item) will remove one instance of an item.
  4. remove_item_from_slot(slot) will remove one item from a specific slot, useful when a player selects a slot to consume an item.
  5. get_item_slot(item) will return the first slot containing the specified item.
  6. get_empty_item_slot() will return the first empty slot, or null if none is available.
  7. has_item(item) will report whether the inventory contains an item, such as arrows a bow could check for later.

For now, add these placeholders:

func _ready ():
	pass

func add_item (item : ItemData) -> bool:
	return false

func remove_item (item : ItemData):
	pass

func remove_item_from_slot (slot : ItemSlot):
	pass

func get_item_slot (item : ItemData) -> ItemSlot:
	return null

func get_empty_item_slot () -> ItemSlot:
	return null

func has_item (item : ItemData) -> bool:
	return false

Add and Remove Inventory Items

Implementing add_item

Start with add_item. It first looks for a slot containing the item and tries to increase that stack. If the matching slot is full or no match exists, it looks for an empty slot instead.

  1. Use get_item_slot(item) to find the first matching slot.
  2. If that slot exists and its quantity is below max_stack_size, increase the quantity by one.
  3. Otherwise, use get_empty_item_slot() to find a free slot.
  4. If no empty slot exists, return false. A caller handling world pickups can use that result to avoid destroying an item the player couldn’t collect.
  5. If an empty slot is available, assign the item and set its quantity to 1.
  6. Emit both update signals and return true.
# adds an item to the inventory
func add_item (item : ItemData) -> bool:
	var slot : ItemSlot = get_item_slot(item)

	if slot and slot.quantity < item.max_stack_size:
		slot.quantity += 1
	else:
		slot = get_empty_item_slot()

		if not slot:
			return false

		slot.item = item
		slot.quantity = 1

	UpdatedInventory.emit()
	UpdatedSlot.emit(slot)

	return true

Implementing remove_item

The remove_item function delegates the removal to remove_item_from_slot. First, check has_item(item) and return if the item isn’t present. Otherwise, find the matching slot and pass it to the slot-removal function:

# removes an item from the inventory
func remove_item (item : ItemData):
	if not has_item(item):
		return

	var slot : ItemSlot = get_item_slot(item)
	remove_item_from_slot(slot)

Removing an Item From a Slot

Now fill in remove_item_from_slot. If the slot contains no item, return immediately. If its quantity is 1, clear slot.item by setting it to null. For a larger quantity, subtract one instead. Finish by emitting both update signals:

# removes an item from the specified slot
func remove_item_from_slot (slot : ItemSlot):
	if not slot.item:
		return

	if slot.quantity == 1:
		slot.item = null
	else:
		slot.quantity -= 1

	UpdatedInventory.emit()
	UpdatedSlot.emit(slot)

Find Inventory Items and Empty Slots

Finding a Slot That Contains a Specific Item

Implement get_item_slot by looping through item_slots. Return the first slot whose item matches the supplied resource. If the loop finishes without a match, return null:

# returns an item slot containing the specified item
func get_item_slot (item : ItemData) -> ItemSlot:
	for slot in item_slots:
		if slot.item == item:
			return slot

	return null

Finding an Empty Slot

For get_empty_item_slot, look for the first slot whose item is null. Return null if no empty slot is available:

# returns an item slot with no item in it
func get_empty_item_slot () -> ItemSlot:
	for slot in item_slots:
		if slot.item == null:
			return slot

	return null

Checking Whether the Inventory Has an Item

The has_item helper also searches the slots, but returns a Boolean rather than a slot. Return true at the first matching item, or false if there is no match:

# is this item in the inventory?
func has_item (item : ItemData) -> bool:
	for slot in item_slots:
		if slot.item == item:
			return true

	return false

Initialize the Player’s Inventory

Filling in the _ready Function

With the helpers complete, fill in _ready to create the slots and populate them with starting items.

First, loop over range(size), create each slot with ItemSlot.new(), and append it to item_slots. The default size produces nine empty slots.

Then loop through the start_items dictionary. Each key is an ItemData resource, and its value is the starting quantity. The nested loop calls add_item(key) once for each item to add. Use start_items[key] to access a dictionary value by its key:

func _ready ():
	# create slots
	for i in range(size):
		item_slots.append(ItemSlot.new())

	# add start items
	for key in start_items:
		for i in range(start_items[key]):
			add_item(key)

Attaching the Inventory to the Player

Open the Player scene and add a default Node as a child of the Player root. Rename it Inventory and attach inventory.gd.

Godot Scene dock showing the Player node tree with a new child node named Inventory being added and renamed.

Leave the exported Size at 9. Press Play; the game should run without errors as the slots are created.

Configuring the Starting Items

Open the Start Items dictionary in the Inspector. Click Add Key/Value Pair, drag an ItemData resource from the FileSystem dock into the key field, and enter its starting quantity as the value. For example, use the arrow resource from the weapons folder with a quantity of 10.

Godot Inspector for the Inventory node showing Size set to 9 and a Start Items dictionary entry for arrow with quantity 10.

Repeat this for other starting items, such as a bow, shield, and health potions.

Godot Inspector showing the Start Items dictionary populated with multiple ItemData entries including HealthPotion, Arrow, Axe, Bow, Shield, and Sword.

When you press Play, the inventory data is initialized, but the items won’t appear on screen yet. This inventory has no user interface; displaying, equipping, and using items through that interface comes after the logic covered here.

Recap and Next Steps

You now have item resources and the core inventory logic for your action RPG. In this tutorial, you:

  • Defined shared item properties and separate weapon, shield, and consumable resource types.
  • Configured item resources with icons, stack limits, and equipment scenes where applicable.
  • Created inventory slots and signals for inventory-wide and slot-specific changes.
  • Implemented item stacking, removal, and lookup helpers.
  • Attached the inventory to the Player and configured starting items in the Inspector.

Your next step is to build the inventory UI so the player can see and interact with these items. The data and logic you’ve put in place provide the foundation for that work.

Continue building your action RPG with the Intermediate Godot Mini-Degree. Follow the full guided project to connect these inventory foundations to the rest of the game.

Did you come across any errors in this tutorial? Please let us know by completing this form and we’ll look into it!

FREE COURSES
Python Blog Image - How to Build a Godot Inventory System With Resources

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.