🎮 UnityDocs

Sistema de inventario

Un inventario completo y limpio: los ítems como ScriptableObjects, la lógica separada de la UI, apilado de cantidades y aviso por eventos cuando cambia. Base sólida y ampliable.

🍳 RecetaAvanzado✅ RevisadoUnity 6.0 LTSActualizado 2026-07-15
DificultadConceptual Código Matemáticas
Tiempo📖 Lectura 9 min · ⌨️ Práctica 40 min

El plan#

Un inventario bien hecho separa tres cosas: qué es un ítem (datos), qué llevas (lógica) y cómo se ve (UI). Si las mezclas, acabas con un monstruo imposible de mantener. Usaremos ScriptableObjects para los ítems y eventos para avisar a la UI.

Paso 1 — El ítem como dato (ScriptableObject)#

C#
using UnityEngine;

[CreateAssetMenu(fileName = "Item", menuName = "Inventario/Item")]
public class ItemData : ScriptableObject
{
    public string nombre;
    public Sprite icono;
    [TextArea] public string descripcion;
    public int maxApilado = 99;   // 1 = no apilable (armas), 99 = pociones
}

Cada ítem ("Poción", "Llave", "Espada") es un asset que creas y editas sin tocar código.

Paso 2 — Una ranura (slot)#

C#
[System.Serializable]
public class Ranura
{
    public ItemData item;
    public int cantidad;

    public Ranura(ItemData item, int cantidad)
    {
        this.item = item;
        this.cantidad = cantidad;
    }
}

Paso 3 — La lógica del inventario#

C#
using System;
using System.Collections.Generic;
using UnityEngine;

public class Inventario : MonoBehaviour
{
    [SerializeField] private int capacidad = 20;
    public List<Ranura> ranuras = new List<Ranura>();

    public event Action AlCambiar;   // la UI se suscribe a esto

    public bool Añadir(ItemData item, int cantidad = 1)
    {
        // 1) Si es apilable y ya lo tengo, suma a la ranura existente
        if (item.maxApilado > 1)
        {
            foreach (var r in ranuras)
            {
                if (r.item == item && r.cantidad < item.maxApilado)
                {
                    r.cantidad += cantidad;
                    AlCambiar?.Invoke();
                    return true;
                }
            }
        }
        // 2) Si hay hueco, crea una ranura nueva
        if (ranuras.Count < capacidad)
        {
            ranuras.Add(new Ranura(item, cantidad));
            AlCambiar?.Invoke();
            return true;
        }
        return false;   // inventario lleno
    }

    public void Quitar(ItemData item, int cantidad = 1)
    {
        for (int i = ranuras.Count - 1; i >= 0; i--)
        {
            if (ranuras[i].item == item)
            {
                ranuras[i].cantidad -= cantidad;
                if (ranuras[i].cantidad <= 0) ranuras.RemoveAt(i);
                AlCambiar?.Invoke();
                return;
            }
        }
    }
}
Por qué el evento AlCambiar

La lógica del inventario no sabe nada de la UI. Cuando algo cambia, solo grita "he cambiado" (AlCambiar?.Invoke()). La UI escucha y se redibuja. Así puedes cambiar toda la interfaz sin tocar la lógica, o tener varias vistas del mismo inventario. Ver eventos.

Paso 4 — La UI escucha#

C#
using UnityEngine;

public class InventarioUI : MonoBehaviour
{
    [SerializeField] private Inventario inventario;
    [SerializeField] private Transform contenedorSlots;
    [SerializeField] private GameObject slotPrefab;

    void OnEnable()  => inventario.AlCambiar += Redibujar;
    void OnDisable() => inventario.AlCambiar -= Redibujar;

    void Redibujar()
    {
        // Limpia y reconstruye (sencillo; para muchos slots, reutiliza en vez de recrear)
        foreach (Transform hijo in contenedorSlots) Destroy(hijo.gameObject);

        foreach (var ranura in inventario.ranuras)
        {
            var slot = Instantiate(slotPrefab, contenedorSlots);
            slot.GetComponent<SlotUI>().Mostrar(ranura.item.icono, ranura.cantidad);
        }
    }
}

Guardar el inventario#

Para persistirlo, guarda los nombres/ids de los ítems y sus cantidades (no los ScriptableObjects directamente) y reconstruye al cargar buscando los assets por id. Ver guardado con JSON.

Errores frecuentes#

  • Meter la lógica y la UI en la misma clase (imposible de mantener).
  • Guardar el ScriptableObject en el archivo de partida (guarda ids y reconstruye).
  • Recrear toda la UI cada cambio con muchos slots (para inventarios grandes, reutiliza los slots existentes).
  • No controlar el maxApilado y apilar armas que deberían ocupar una ranura cada una.

Resumen#

Separa ítem (ScriptableObject), lógica (Inventario con List<Ranura>, apilado y evento AlCambiar) y UI (escucha el evento y se redibuja). Los ítems son assets editables sin código; la UI está desacoplada por eventos; y para guardar, persiste ids + cantidades con JSON. Es una base que aguanta crafting, equipamiento y más.

Fuentes y para profundizar