Qué es#
Un ScriptableObject es un contenedor de datos que vive como asset en tu proyecto, no pegado a un GameObject ni a una escena. Piensa en él como una "ficha de datos" reutilizable: la defines una vez y la referencias desde donde quieras.
El problema que resuelve#
Imagina 100 enemigos "Goblin" en pantalla. Si guardas sus stats (vida, daño, velocidad) en un MonoBehaviour, cada uno de los 100 lleva su propia copia de esos números en memoria, aunque sean idénticos. Y si quieres cambiar el daño del goblin, ¿editas los 100?
Con un ScriptableObject, los 100 goblins apuntan al mismo asset de datos. Un solo sitio que editar, una sola copia en memoria.
MonoBehaviour | ScriptableObject | |
|---|---|---|
| Vive en | Un GameObject, en una escena | Un asset en el proyecto |
| Tiene ciclo de vida (Update…) | Sí | No (no se adjunta a objetos) |
| Copias en memoria | Una por instancia | Una compartida |
| Ideal para | Comportamiento en escena | Datos y configuración |
Crear uno#
using UnityEngine;
// Añade la opción al menú Assets > Create
[CreateAssetMenu(fileName = "NuevaArma", menuName = "Juego/Arma")]
public class ArmaData : ScriptableObject
{
public string nombre;
public int daño = 10;
public float cadencia = 0.5f;
public Sprite icono;
public GameObject prefabProyectil;
}Ahora, clic derecho en el Project → Create → Juego → Arma, y creas "Pistola", "Escopeta", "Rifle" como assets independientes que un diseñador puede balancear sin tocar código.
Usarlo#
public class Arma : MonoBehaviour
{
[SerializeField] private ArmaData datos; // arrastras "Escopeta" aquí
void Disparar()
{
Debug.Log($"Disparando {datos.nombre}, daño {datos.daño}");
Instantiate(datos.prefabProyectil, transform.position, transform.rotation);
}
}Este patrón separa datos de comportamiento. El programador escribe la lógica del arma una vez; el diseñador crea y ajusta decenas de armas como assets. Es el corazón del diseño data-driven.
Más allá de los datos: SO como sistema#
Los ScriptableObjects no solo guardan datos estáticos; pueden ser piezas de arquitectura:
- Variables compartidas: un
FloatVariablecomo asset al que varios sistemas acceden, en vez de un Singleton. - Canales de eventos: un
GameEventcomo asset; unos lo lanzan y otros lo escuchan, sin referencias directas entre escenas. Combina genial con eventos. - Estados y configuraciones: dificultad, tablas de loot, definiciones de niveles.
Cuidado: en el editor, modificar un ScriptableObject en Play persiste tras salir de Play (estás editando el asset real). En una build, los cambios en runtime no se guardan al cerrar. Por eso un SO no sirve como sistema de guardado de partidas: úsalo para datos de diseño, no para el progreso del jugador. Para eso, serializa a JSON.
Cuándo NO usarlo#
- Para el estado que cambia durante la partida y debe persistir (vida actual, inventario del jugador): eso va en runtime + guardado, no en el asset.
- Para lógica con ciclo de vida (necesitas
Update): eso es unMonoBehaviour.
Errores frecuentes#
- Modificar un SO en runtime esperando que en la build se guarde (no pasa).
- Meter estado mutable de partida en un SO compartido (todos comparten el mismo dato sin querer).
- Olvidar
[CreateAssetMenu]y no poder crear el asset desde el editor.
Ponte a prueba#
Resumen#
El ScriptableObject guarda datos como asset, independientes de escenas y GameObjects: una sola copia compartida, un solo sitio que editar. Perfecto para configurar armas, enemigos e ítems, y como base de arquitecturas data-driven y de eventos. No lo uses para el estado de partida que deba persistir en una build.