Lesson 8: Inventory & Item Management
Welcome to one of the most important systems in your AI-Powered RPG Game! In this lesson, you'll learn how to create a comprehensive inventory system with item management, equipment slots, and AI-generated items that will make your RPG feel alive and engaging.
What You'll Build
By the end of this lesson, you'll have:
- Dynamic Inventory System with drag-and-drop functionality
- Equipment System with stat bonuses and visual changes
- AI-Generated Items with procedural stats and descriptions
- Item Categories (weapons, armor, consumables, quest items)
- Inventory UI with sorting, filtering, and search capabilities
- Item Database with persistent storage and loading
Why Inventory Systems Matter
The Problem: Static inventory systems are boring and predictable. Players quickly memorize item locations and stats.
The Solution: AI-driven inventory systems that:
- Generate unique items with procedural stats
- Adapt to player preferences and playstyle
- Evolve based on game progression
- Create meaningful choices and trade-offs
Prerequisites
Before starting this lesson, make sure you have:
- ✅ Completed Lesson 7: Combat System & AI Enemies
- ✅ Working Unity project with combat system
- ✅ Basic C# scripting knowledge
- ✅ Understanding of Unity's UI system
Step 1: Design Your Inventory System Architecture
Item Data Structures
First, let's design the core item system:
[System.Serializable]
public class Item
{
public string id;
public string name;
public string description;
public ItemType type;
public ItemRarity rarity;
public Sprite icon;
public int stackSize;
public int currentStack;
public ItemStats stats;
public List<ItemEffect> effects;
public bool isEquippable;
public EquipmentSlot equipmentSlot;
}
[System.Serializable]
public class ItemStats
{
public int damage;
public int defense;
public int health;
public int mana;
public int speed;
public int criticalChance;
public int criticalDamage;
public Dictionary<string, float> customStats;
}
[System.Serializable]
public class ItemEffect
{
public string effectName;
public EffectType type;
public float value;
public float duration;
public bool isPermanent;
}
public enum ItemType
{
Weapon,
Armor,
Consumable,
Quest,
Material,
Special
}
public enum ItemRarity
{
Common,
Uncommon,
Rare,
Epic,
Legendary,
Mythic
}
public enum EquipmentSlot
{
None,
Helmet,
Chest,
Legs,
Boots,
Weapon,
Shield,
Accessory
}
Inventory Manager Class
Create a new script called InventoryManager.cs
:
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class InventoryManager : MonoBehaviour
{
[Header("Inventory Settings")]
public int maxInventorySlots = 50;
public int maxStackSize = 99;
[Header("Equipment Slots")]
public Transform equipmentParent;
public Transform inventoryParent;
private List<Item> inventory = new List<Item>();
private Dictionary<EquipmentSlot, Item> equippedItems = new Dictionary<EquipmentSlot, Item>();
private PlayerStats playerStats;
// Events
public System.Action<Item> OnItemAdded;
public System.Action<Item> OnItemRemoved;
public System.Action<Item> OnItemEquipped;
public System.Action<Item> OnItemUnequipped;
private void Start()
{
playerStats = GetComponent<PlayerStats>();
InitializeInventory();
}
private void InitializeInventory()
{
// Initialize equipment slots
foreach (EquipmentSlot slot in System.Enum.GetValues(typeof(EquipmentSlot)))
{
if (slot != EquipmentSlot.None)
{
equippedItems[slot] = null;
}
}
// Load saved inventory
LoadInventory();
}
public bool AddItem(Item item)
{
// Check if item can be stacked
if (item.stackSize > 1)
{
Item existingItem = inventory.FirstOrDefault(i =>
i.id == item.id && i.currentStack < i.stackSize);
if (existingItem != null)
{
int spaceAvailable = existingItem.stackSize - existingItem.currentStack;
int amountToAdd = Mathf.Min(item.currentStack, spaceAvailable);
existingItem.currentStack += amountToAdd;
item.currentStack -= amountToAdd;
OnItemAdded?.Invoke(existingItem);
if (item.currentStack <= 0)
{
return true;
}
}
}
// Check if inventory has space
if (inventory.Count >= maxInventorySlots)
{
Debug.Log("Inventory is full!");
return false;
}
// Add new item
inventory.Add(item);
OnItemAdded?.Invoke(item);
// Save inventory
SaveInventory();
return true;
}
public bool RemoveItem(Item item, int quantity = 1)
{
if (item.currentStack > quantity)
{
item.currentStack -= quantity;
OnItemRemoved?.Invoke(item);
}
else
{
inventory.Remove(item);
OnItemRemoved?.Invoke(item);
}
SaveInventory();
return true;
}
public bool EquipItem(Item item)
{
if (!item.isEquippable || item.equipmentSlot == EquipmentSlot.None)
{
Debug.Log("Item cannot be equipped!");
return false;
}
// Unequip current item in slot
if (equippedItems[item.equipmentSlot] != null)
{
UnequipItem(equippedItems[item.equipmentSlot]);
}
// Equip new item
equippedItems[item.equipmentSlot] = item;
RemoveItem(item, 1);
// Apply item stats to player
ApplyItemStats(item);
OnItemEquipped?.Invoke(item);
SaveInventory();
return true;
}
public bool UnequipItem(Item item)
{
if (equippedItems[item.equipmentSlot] != item)
{
Debug.Log("Item is not equipped!");
return false;
}
// Remove item stats from player
RemoveItemStats(item);
// Add item back to inventory
AddItem(item);
equippedItems[item.equipmentSlot] = null;
OnItemUnequipped?.Invoke(item);
SaveInventory();
return true;
}
private void ApplyItemStats(Item item)
{
if (playerStats == null) return;
// Apply base stats
playerStats.AddDamage(item.stats.damage);
playerStats.AddDefense(item.stats.defense);
playerStats.AddHealth(item.stats.health);
playerStats.AddMana(item.stats.mana);
playerStats.AddSpeed(item.stats.speed);
// Apply custom stats
foreach (var stat in item.stats.customStats)
{
playerStats.AddCustomStat(stat.Key, stat.Value);
}
// Apply item effects
foreach (ItemEffect effect in item.effects)
{
ApplyItemEffect(effect);
}
}
private void RemoveItemStats(Item item)
{
if (playerStats == null) return;
// Remove base stats
playerStats.AddDamage(-item.stats.damage);
playerStats.AddDefense(-item.stats.defense);
playerStats.AddHealth(-item.stats.health);
playerStats.AddMana(-item.stats.mana);
playerStats.AddSpeed(-item.stats.speed);
// Remove custom stats
foreach (var stat in item.stats.customStats)
{
playerStats.AddCustomStat(stat.Key, -stat.Value);
}
// Remove item effects
foreach (ItemEffect effect in item.effects)
{
RemoveItemEffect(effect);
}
}
private void ApplyItemEffect(ItemEffect effect)
{
// Apply effect based on type
switch (effect.type)
{
case EffectType.DamageBoost:
playerStats.AddDamageMultiplier(effect.value);
break;
case EffectType.DefenseBoost:
playerStats.AddDefenseMultiplier(effect.value);
break;
case EffectType.SpeedBoost:
playerStats.AddSpeedMultiplier(effect.value);
break;
case EffectType.HealthRegen:
playerStats.AddHealthRegen(effect.value);
break;
case EffectType.ManaRegen:
playerStats.AddManaRegen(effect.value);
break;
}
}
private void RemoveItemEffect(ItemEffect effect)
{
// Remove effect based on type
switch (effect.type)
{
case EffectType.DamageBoost:
playerStats.AddDamageMultiplier(-effect.value);
break;
case EffectType.DefenseBoost:
playerStats.AddDefenseMultiplier(-effect.value);
break;
case EffectType.SpeedBoost:
playerStats.AddSpeedMultiplier(-effect.value);
break;
case EffectType.HealthRegen:
playerStats.AddHealthRegen(-effect.value);
break;
case EffectType.ManaRegen:
playerStats.AddManaRegen(-effect.value);
break;
}
}
public List<Item> GetInventory()
{
return new List<Item>(inventory);
}
public List<Item> GetEquippedItems()
{
return equippedItems.Values.Where(item => item != null).ToList();
}
public Item GetEquippedItem(EquipmentSlot slot)
{
return equippedItems.ContainsKey(slot) ? equippedItems[slot] : null;
}
private void SaveInventory()
{
// Save inventory to PlayerPrefs or file
string json = JsonUtility.ToJson(new InventoryData(inventory, equippedItems));
PlayerPrefs.SetString("Inventory", json);
PlayerPrefs.Save();
}
private void LoadInventory()
{
if (PlayerPrefs.HasKey("Inventory"))
{
string json = PlayerPrefs.GetString("Inventory");
InventoryData data = JsonUtility.FromJson<InventoryData>(json);
inventory = data.inventory ?? new List<Item>();
equippedItems = data.equippedItems ?? new Dictionary<EquipmentSlot, Item>();
}
}
}
[System.Serializable]
public class InventoryData
{
public List<Item> inventory;
public Dictionary<EquipmentSlot, Item> equippedItems;
public InventoryData(List<Item> inv, Dictionary<EquipmentSlot, Item> equipped)
{
inventory = inv;
equippedItems = equipped;
}
}
Step 2: Create AI-Generated Item System
AI Item Generator
Create an AIItemGenerator.cs
script:
using System.Collections.Generic;
using UnityEngine;
using System;
public class AIItemGenerator : MonoBehaviour
{
[Header("AI Settings")]
public string openaiApiKey;
public string openaiModel = "gpt-4";
[Header("Generation Settings")]
public int maxItemsPerRequest = 5;
public float generationCooldown = 30f;
private float lastGenerationTime;
private bool isGenerating = false;
public System.Action<List<Item>> OnItemsGenerated;
public async void GenerateItems(ItemType itemType, int playerLevel, int quantity = 1)
{
if (isGenerating)
{
Debug.Log("Item generation in progress...");
return;
}
if (Time.time - lastGenerationTime < generationCooldown)
{
Debug.Log($"Please wait {generationCooldown - (Time.time - lastGenerationTime):F1} seconds before generating more items.");
return;
}
isGenerating = true;
lastGenerationTime = Time.time;
try
{
string prompt = CreateItemGenerationPrompt(itemType, playerLevel, quantity);
string response = await CallOpenAI(prompt);
List<Item> generatedItems = ParseItemResponse(response);
OnItemsGenerated?.Invoke(generatedItems);
}
catch (Exception e)
{
Debug.LogError($"Error generating items: {e.Message}");
}
finally
{
isGenerating = false;
}
}
private string CreateItemGenerationPrompt(ItemType itemType, int playerLevel, int quantity)
{
return $@"Generate {quantity} unique {itemType} items for a fantasy RPG game.
Player Level: {playerLevel}
Item Type: {itemType}
For each item, provide:
1. Name (creative and thematic)
2. Description (2-3 sentences, atmospheric)
3. Rarity (Common, Uncommon, Rare, Epic, Legendary, Mythic)
4. Base Stats (damage, defense, health, mana, speed)
5. Special Effects (up to 3 unique effects)
6. Equipment Slot (if applicable)
Format as JSON array with this structure:
[
{{
""name"": ""Item Name"",
""description"": ""Item description here"",
""rarity"": ""Rare"",
""stats"": {{
""damage"": 15,
""defense"": 5,
""health"": 20,
""mana"": 10,
""speed"": 2
}},
""effects"": [
{{
""name"": ""Fire Damage"",
""type"": ""DamageBoost"",
""value"": 0.15,
""duration"": 0,
""isPermanent"": true
}}
],
""equipmentSlot"": ""Weapon""
}}
]
Make items appropriate for level {playerLevel} and ensure they're balanced and interesting.";
}
private async System.Threading.Tasks.Task<string> CallOpenAI(string prompt)
{
// Implement OpenAI API call
// This is a simplified version - you'll need to implement the actual API call
using (var client = new System.Net.Http.HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {openaiApiKey}");
var requestBody = new
{
model = openaiModel,
messages = new[]
{
new { role = "user", content = prompt }
},
max_tokens = 2000,
temperature = 0.8f
};
string json = JsonUtility.ToJson(requestBody);
var content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.openai.com/v1/chat/completions", content);
string responseContent = await response.Content.ReadAsStringAsync();
// Parse response and extract content
var responseObj = JsonUtility.FromJson<OpenAIResponse>(responseContent);
return responseObj.choices[0].message.content;
}
}
private List<Item> ParseItemResponse(string response)
{
List<Item> items = new List<Item>();
try
{
// Parse JSON response
var itemDataList = JsonUtility.FromJson<ItemDataList>("{\"items\":" + response + "}");
foreach (var itemData in itemDataList.items)
{
Item item = CreateItemFromData(itemData);
items.Add(item);
}
}
catch (Exception e)
{
Debug.LogError($"Error parsing item response: {e.Message}");
}
return items;
}
private Item CreateItemFromData(ItemData data)
{
Item item = new Item
{
id = System.Guid.NewGuid().ToString(),
name = data.name,
description = data.description,
type = data.type,
rarity = data.rarity,
stackSize = 1,
currentStack = 1,
isEquippable = data.equipmentSlot != EquipmentSlot.None,
equipmentSlot = data.equipmentSlot,
stats = data.stats,
effects = data.effects
};
// Load icon based on item type and rarity
item.icon = LoadItemIcon(item.type, item.rarity);
return item;
}
private Sprite LoadItemIcon(ItemType type, ItemRarity rarity)
{
// Load appropriate icon based on type and rarity
string iconPath = $"Icons/{type}/{rarity}";
return Resources.Load<Sprite>(iconPath);
}
}
[System.Serializable]
public class ItemData
{
public string name;
public string description;
public ItemType type;
public ItemRarity rarity;
public ItemStats stats;
public ItemEffect[] effects;
public EquipmentSlot equipmentSlot;
}
[System.Serializable]
public class ItemDataList
{
public ItemData[] items;
}
[System.Serializable]
public class OpenAIResponse
{
public Choice[] choices;
}
[System.Serializable]
public class Choice
{
public Message message;
}
[System.Serializable]
public class Message
{
public string content;
}
Step 3: Create Inventory UI System
Inventory UI Manager
Create an InventoryUI.cs
script:
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Collections.Generic;
using System.Linq;
public class InventoryUI : MonoBehaviour
{
[Header("UI References")]
public GameObject inventoryPanel;
public Transform inventoryGrid;
public Transform equipmentGrid;
public GameObject itemSlotPrefab;
public TextMeshProUGUI inventoryTitle;
public Button closeButton;
public Button sortButton;
public Button filterButton;
[Header("Item Display")]
public GameObject itemTooltip;
public TextMeshProUGUI itemNameText;
public TextMeshProUGUI itemDescriptionText;
public TextMeshProUGUI itemStatsText;
public Image itemIcon;
[Header("Equipment Slots")]
public EquipmentSlotUI[] equipmentSlots;
private InventoryManager inventoryManager;
private List<ItemSlotUI> itemSlots = new List<ItemSlotUI>();
private Item currentTooltipItem;
private ItemType currentFilter = ItemType.Weapon;
private bool isAscending = true;
private void Start()
{
inventoryManager = FindObjectOfType<InventoryManager>();
SetupUI();
SetupEventListeners();
}
private void SetupUI()
{
// Initialize inventory grid
for (int i = 0; i < inventoryManager.maxInventorySlots; i++)
{
GameObject slotObj = Instantiate(itemSlotPrefab, inventoryGrid);
ItemSlotUI slotUI = slotObj.GetComponent<ItemSlotUI>();
slotUI.Initialize(this);
itemSlots.Add(slotUI);
}
// Initialize equipment slots
foreach (EquipmentSlotUI slot in equipmentSlots)
{
slot.Initialize(this);
}
// Hide inventory panel initially
inventoryPanel.SetActive(false);
itemTooltip.SetActive(false);
}
private void SetupEventListeners()
{
closeButton.onClick.AddListener(CloseInventory);
sortButton.onClick.AddListener(SortInventory);
filterButton.onClick.AddListener(FilterInventory);
// Subscribe to inventory events
inventoryManager.OnItemAdded += OnItemAdded;
inventoryManager.OnItemRemoved += OnItemRemoved;
inventoryManager.OnItemEquipped += OnItemEquipped;
inventoryManager.OnItemUnequipped += OnItemUnequipped;
}
public void ToggleInventory()
{
inventoryPanel.SetActive(!inventoryPanel.activeSelf);
if (inventoryPanel.activeSelf)
{
RefreshInventory();
}
}
public void CloseInventory()
{
inventoryPanel.SetActive(false);
itemTooltip.SetActive(false);
}
private void RefreshInventory()
{
// Clear all slots
foreach (ItemSlotUI slot in itemSlots)
{
slot.SetItem(null);
}
// Get filtered inventory
List<Item> filteredItems = GetFilteredInventory();
// Populate slots
for (int i = 0; i < filteredItems.Count && i < itemSlots.Count; i++)
{
itemSlots[i].SetItem(filteredItems[i]);
}
// Update equipment slots
RefreshEquipmentSlots();
}
private List<Item> GetFilteredInventory()
{
List<Item> inventory = inventoryManager.GetInventory();
// Filter by type
if (currentFilter != ItemType.Weapon) // Default filter
{
inventory = inventory.Where(item => item.type == currentFilter).ToList();
}
// Sort inventory
inventory = SortItems(inventory);
return inventory;
}
private List<Item> SortItems(List<Item> items)
{
switch (currentFilter)
{
case ItemType.Weapon:
return items.OrderBy(item => isAscending ? item.stats.damage : -item.stats.damage).ToList();
case ItemType.Armor:
return items.OrderBy(item => isAscending ? item.stats.defense : -item.stats.defense).ToList();
default:
return items.OrderBy(item => isAscending ? item.name : item.name).ToList();
}
}
private void RefreshEquipmentSlots()
{
foreach (EquipmentSlotUI slot in equipmentSlots)
{
Item equippedItem = inventoryManager.GetEquippedItem(slot.slotType);
slot.SetItem(equippedItem);
}
}
public void OnItemSlotClicked(ItemSlotUI slot)
{
if (slot.item == null) return;
// Handle item interaction
if (slot.item.isEquippable)
{
if (inventoryManager.GetEquippedItem(slot.item.equipmentSlot) == null)
{
inventoryManager.EquipItem(slot.item);
}
else
{
inventoryManager.UnequipItem(inventoryManager.GetEquippedItem(slot.item.equipmentSlot));
inventoryManager.EquipItem(slot.item);
}
}
else
{
// Use consumable item
UseItem(slot.item);
}
}
public void OnItemSlotHover(ItemSlotUI slot)
{
if (slot.item == null)
{
itemTooltip.SetActive(false);
return;
}
currentTooltipItem = slot.item;
ShowItemTooltip(slot.item);
}
public void OnItemSlotUnhover()
{
itemTooltip.SetActive(false);
}
private void ShowItemTooltip(Item item)
{
itemTooltip.SetActive(true);
itemNameText.text = item.name;
itemDescriptionText.text = item.description;
itemIcon.sprite = item.icon;
// Build stats text
string statsText = "";
if (item.stats.damage > 0) statsText += $"Damage: +{item.stats.damage}\n";
if (item.stats.defense > 0) statsText += $"Defense: +{item.stats.defense}\n";
if (item.stats.health > 0) statsText += $"Health: +{item.stats.health}\n";
if (item.stats.mana > 0) statsText += $"Mana: +{item.stats.mana}\n";
if (item.stats.speed > 0) statsText += $"Speed: +{item.stats.speed}\n";
itemStatsText.text = statsText;
}
private void UseItem(Item item)
{
if (item.type == ItemType.Consumable)
{
// Apply consumable effects
ApplyConsumableEffects(item);
inventoryManager.RemoveItem(item, 1);
RefreshInventory();
}
}
private void ApplyConsumableEffects(Item item)
{
foreach (ItemEffect effect in item.effects)
{
// Apply effect to player
Debug.Log($"Applied {effect.effectName}: {effect.value}");
}
}
private void SortInventory()
{
isAscending = !isAscending;
RefreshInventory();
}
private void FilterInventory()
{
// Cycle through item types
currentFilter = (ItemType)(((int)currentFilter + 1) % System.Enum.GetValues(typeof(ItemType)).Length);
RefreshInventory();
}
// Event handlers
private void OnItemAdded(Item item)
{
RefreshInventory();
}
private void OnItemRemoved(Item item)
{
RefreshInventory();
}
private void OnItemEquipped(Item item)
{
RefreshInventory();
}
private void OnItemUnequipped(Item item)
{
RefreshInventory();
}
}
Step 4: Create Item Slot UI
Item Slot UI Component
Create an ItemSlotUI.cs
script:
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.EventSystems;
public class ItemSlotUI : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
{
[Header("UI References")]
public Image itemIcon;
public TextMeshProUGUI stackText;
public Image rarityBorder;
public GameObject emptySlot;
[Header("Colors")]
public Color commonColor = Color.white;
public Color uncommonColor = Color.green;
public Color rareColor = Color.blue;
public Color epicColor = Color.magenta;
public Color legendaryColor = Color.yellow;
public Color mythicColor = Color.red;
public Item item { get; private set; }
public InventoryUI inventoryUI;
public void Initialize(InventoryUI ui)
{
inventoryUI = ui;
SetItem(null);
}
public void SetItem(Item newItem)
{
item = newItem;
if (item == null)
{
ShowEmptySlot();
}
else
{
ShowItem();
}
}
private void ShowEmptySlot()
{
itemIcon.gameObject.SetActive(false);
stackText.gameObject.SetActive(false);
emptySlot.SetActive(true);
rarityBorder.color = Color.gray;
}
private void ShowItem()
{
itemIcon.gameObject.SetActive(true);
itemIcon.sprite = item.icon;
if (item.currentStack > 1)
{
stackText.gameObject.SetActive(true);
stackText.text = item.currentStack.ToString();
}
else
{
stackText.gameObject.SetActive(false);
}
emptySlot.SetActive(false);
rarityBorder.color = GetRarityColor(item.rarity);
}
private Color GetRarityColor(ItemRarity rarity)
{
switch (rarity)
{
case ItemRarity.Common:
return commonColor;
case ItemRarity.Uncommon:
return uncommonColor;
case ItemRarity.Rare:
return rareColor;
case ItemRarity.Epic:
return epicColor;
case ItemRarity.Legendary:
return legendaryColor;
case ItemRarity.Mythic:
return mythicColor;
default:
return Color.white;
}
}
public void OnPointerClick(PointerEventData eventData)
{
if (item != null)
{
inventoryUI.OnItemSlotClicked(this);
}
}
public void OnPointerEnter(PointerEventData eventData)
{
inventoryUI.OnItemSlotHover(this);
}
public void OnPointerExit(PointerEventData eventData)
{
inventoryUI.OnItemSlotUnhover();
}
}
Step 5: Create Equipment Slot UI
Equipment Slot UI Component
Create an EquipmentSlotUI.cs
script:
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.EventSystems;
public class EquipmentSlotUI : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
{
[Header("UI References")]
public Image itemIcon;
public TextMeshProUGUI slotLabel;
public Image slotBackground;
[Header("Slot Settings")]
public EquipmentSlot slotType;
public Sprite emptySlotSprite;
public Sprite filledSlotSprite;
public Item item { get; private set; }
public InventoryUI inventoryUI;
public void Initialize(InventoryUI ui)
{
inventoryUI = ui;
slotLabel.text = slotType.ToString();
SetItem(null);
}
public void SetItem(Item newItem)
{
item = newItem;
if (item == null)
{
itemIcon.gameObject.SetActive(false);
slotBackground.sprite = emptySlotSprite;
}
else
{
itemIcon.gameObject.SetActive(true);
itemIcon.sprite = item.icon;
slotBackground.sprite = filledSlotSprite;
}
}
public void OnPointerClick(PointerEventData eventData)
{
if (item != null)
{
// Unequip item
inventoryUI.inventoryManager.UnequipItem(item);
}
}
public void OnPointerEnter(PointerEventData eventData)
{
if (item != null)
{
inventoryUI.OnItemSlotHover(this);
}
}
public void OnPointerExit(PointerEventData eventData)
{
inventoryUI.OnItemSlotUnhover();
}
}
Step 6: Create Player Stats System
Player Stats Manager
Create a PlayerStats.cs
script:
using System.Collections.Generic;
using UnityEngine;
public class PlayerStats : MonoBehaviour
{
[Header("Base Stats")]
public int baseHealth = 100;
public int baseMana = 50;
public int baseDamage = 10;
public int baseDefense = 5;
public int baseSpeed = 10;
[Header("Current Stats")]
public int currentHealth;
public int currentMana;
public int currentDamage;
public int currentDefense;
public int currentSpeed;
[Header("Multipliers")]
public float damageMultiplier = 1f;
public float defenseMultiplier = 1f;
public float speedMultiplier = 1f;
[Header("Regeneration")]
public float healthRegen = 1f;
public float manaRegen = 0.5f;
private Dictionary<string, float> customStats = new Dictionary<string, float>();
private void Start()
{
InitializeStats();
}
private void InitializeStats()
{
currentHealth = baseHealth;
currentMana = baseMana;
currentDamage = baseDamage;
currentDefense = baseDefense;
currentSpeed = baseSpeed;
}
public void AddDamage(int amount)
{
currentDamage += amount;
}
public void AddDefense(int amount)
{
currentDefense += amount;
}
public void AddHealth(int amount)
{
currentHealth += amount;
currentHealth = Mathf.Min(currentHealth, baseHealth);
}
public void AddMana(int amount)
{
currentMana += amount;
currentMana = Mathf.Min(currentMana, baseMana);
}
public void AddSpeed(int amount)
{
currentSpeed += amount;
}
public void AddDamageMultiplier(float amount)
{
damageMultiplier += amount;
}
public void AddDefenseMultiplier(float amount)
{
defenseMultiplier += amount;
}
public void AddSpeedMultiplier(float amount)
{
speedMultiplier += amount;
}
public void AddHealthRegen(float amount)
{
healthRegen += amount;
}
public void AddManaRegen(float amount)
{
manaRegen += amount;
}
public void AddCustomStat(string statName, float value)
{
if (customStats.ContainsKey(statName))
{
customStats[statName] += value;
}
else
{
customStats[statName] = value;
}
}
public float GetCustomStat(string statName)
{
return customStats.ContainsKey(statName) ? customStats[statName] : 0f;
}
private void Update()
{
// Regenerate health and mana
if (currentHealth < baseHealth)
{
currentHealth += (int)(healthRegen * Time.deltaTime);
currentHealth = Mathf.Min(currentHealth, baseHealth);
}
if (currentMana < baseMana)
{
currentMana += (int)(manaRegen * Time.deltaTime);
currentMana = Mathf.Min(currentMana, baseMana);
}
}
}
Step 7: Testing and Balancing
Inventory Testing Script
Create an InventoryTester.cs
script:
using UnityEngine;
using System.Collections.Generic;
public class InventoryTester : MonoBehaviour
{
[Header("Test Settings")]
public int testItemCount = 10;
public ItemType testItemType = ItemType.Weapon;
public int playerLevel = 5;
private InventoryManager inventoryManager;
private AIItemGenerator itemGenerator;
private void Start()
{
inventoryManager = FindObjectOfType<InventoryManager>();
itemGenerator = FindObjectOfType<AIItemGenerator>();
// Subscribe to events
itemGenerator.OnItemsGenerated += OnItemsGenerated;
}
private void Update()
{
// Test inventory with key presses
if (Input.GetKeyDown(KeyCode.I))
{
ToggleInventory();
}
if (Input.GetKeyDown(KeyCode.G))
{
GenerateTestItems();
}
if (Input.GetKeyDown(KeyCode.C))
{
ClearInventory();
}
}
private void ToggleInventory()
{
InventoryUI inventoryUI = FindObjectOfType<InventoryUI>();
if (inventoryUI != null)
{
inventoryUI.ToggleInventory();
}
}
private void GenerateTestItems()
{
itemGenerator.GenerateItems(testItemType, playerLevel, testItemCount);
}
private void ClearInventory()
{
List<Item> inventory = inventoryManager.GetInventory();
for (int i = inventory.Count - 1; i >= 0; i--)
{
inventoryManager.RemoveItem(inventory[i], inventory[i].currentStack);
}
}
private void OnItemsGenerated(List<Item> items)
{
Debug.Log($"Generated {items.Count} items:");
foreach (Item item in items)
{
Debug.Log($"- {item.name} ({item.rarity}) - {item.description}");
inventoryManager.AddItem(item);
}
}
}
Mini-Task: Create Your First Inventory System
Your Mission: Build a basic inventory system with AI-generated items and equipment slots.
Steps:
- Set up the inventory data structures
- Create the inventory manager with add/remove/equip functionality
- Build the inventory UI with drag-and-drop support
- Implement AI item generation
- Test the system with generated items
Success Criteria:
- ✅ Items can be added and removed from inventory
- ✅ Equipment system works with stat bonuses
- ✅ AI generates unique items with proper stats
- ✅ UI displays items correctly with tooltips
- ✅ Inventory persists between game sessions
Common Issues and Solutions
Issue 1: Items Not Appearing in UI
Problem: Items are added to inventory but don't show in UI Solution: Check UI refresh calls and event subscriptions
Issue 2: Equipment Stats Not Applying
Problem: Equipped items don't affect player stats Solution: Verify stat application in EquipItem method
Issue 3: AI Generation Failing
Problem: AI item generation returns errors Solution: Check API key, network connection, and response parsing
Next Steps
Congratulations! You've built a comprehensive inventory system. In the next lesson, you'll learn about:
- Advanced Item Systems - Enchanting, upgrading, and crafting
- AI Item Balancing - Dynamic difficulty adjustment
- Performance Optimization - Efficient inventory management
Resources
Community Challenge
Share Your Inventory: Post a screenshot of your inventory system and tag us on social media! Show off your AI-generated items and creative UI design.
Pro Tip: Experiment with different item rarities and effects to create unique gameplay experiences that keep players engaged.
Ready to create an inventory system that feels alive and engaging? Let's build the item management system that will make your RPG unforgettable!