
Cómo utilizar un conjunto de tiempo de ejecución basado en ScriptableObject
Para tu comodidad, tradujimos esta página mediante traducción automática. No podemos garantizar la precisión ni la confiabilidad del contenido traducido. Si tienes alguna duda sobre la precisión del contenido traducido, consulta la versión oficial en inglés de la página web.
Nota importante antes de empezar
Antes de sumergirte en el proyecto de demostración ScriptableObject y en esta serie de miniguías, recuerda que, en el fondo, los patrones de diseño son sólo ideas. No se aplicarán a todas las situaciones. Estas técnicas pueden ayudarte a aprender nuevas formas de trabajar con Unity y ScriptableObjects.
Cada modelo tiene sus pros y sus contras. Elija sólo las que beneficien significativamente a su proyecto específico. ¿Sus diseñadores confían mucho en el editor de Unity? Un patrón basado en ScriptableObject podría ser una buena opción para ayudarles a colaborar con sus desarrolladores.
En última instancia, la mejor arquitectura de código es la que se adapta a tu proyecto y a tu equipo.
Conjuntos de tiempo de ejecución
Los ScriptableObjects pueden almacenar datos estáticos. También pueden modificarse para contener datos dinámicos. Esto es particularmente útil para referenciar una colección de GameObjects o componentes en tiempo de ejecución.
Un "conjunto de tiempo de ejecución" como éste replica la razón por la que algunos desarrolladores utilizan singletons: fácil acceso global. Desgraciadamente, los singletons vienen acompañados de dependencias indeseables. Estas dependencias adicionales pueden complicar las pruebas y dificultar la depuración.
Como alternativa, considere el uso de un conjunto de tiempo de ejecución basado en ScriptableObject para los componentes de referencia frecuente. Por ejemplo, úsalas para controlar elementos del juego como los enemigos que aparecen, los objetos coleccionables o los puntos de control.
Como activos a nivel de proyecto, los ScriptableObjects proporcionan acceso global a los datos de todos los objetos de una escena. Esta característica permite compartir información sin muchos de los inconvenientes asociados a los singletons.
Aplicación básica
Aquí hay una manera de hacer un conjunto de tiempo de ejecución para GameObjects. Piénsalo como un contenedor de datos que guarda una colección pública de elementos, pero que también incluye métodos para añadir y eliminar miembros.
En tiempo de ejecución, cualquier MonoBehaviour puede hacer referencia a la propiedad pública Items para obtener la lista completa de GameObjects. Otro script o componente puede llamar a Añadir o Eliminar para modificar la lista de Elementos.
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "GameObject Runtime Set", fileName
= "GORuntimeSet")]
public class GameObjectRuntimeSetSO: ScriptableObject
{
private List<GameObject> items = new List<GameObject>();
public List<GameObject> Items => items;
public void Add(GameObject thingToAdd)
{
if (!items.Contains(thingToAdd))
items.Add(thingToAdd);
}
public void Remove(GameObject thingToRemove)
{
if (items.Contains(thingToRemove))
items.Remove(thingToRemove);
}
}
Versión genérica
Puedes hacer que un conjunto de tiempo de ejecución sea más específico para que sólo rastree un tipo concreto de MonoBehaviour. En ese caso, crea conjuntos específicos para cada tipo (por ejemplo, EnemyRuntimeSet, PickupRuntimeSet, etc.).
Una clase abstracta genérica puede agilizar este proceso. A continuación, podría derivar otros conjuntos de tiempo de ejecución de esta clase base. Por ejemplo, si quisieras hacer un conjunto de tiempo de ejecución para un componente Enemigo personalizado, podrías definir clases para un RuntimeSetSO<T> genérico y un EnemyRuntimeSetSO concreto como el fragmento de código siguiente.
Observa que la clase concreta, EnemyRuntimeSetSO, no tiene ningún detalle de implementación, sólo un atributo CreateAssetMenu.
Cada nuevo componente que desee rastrear sólo necesita un conjunto de tiempo de ejecución correspondiente para gestionarlo. Construye tantas clases concretas como necesites. Los enemigos, los PNJ, los objetos del inventario, las misiones y mucho más pueden tener sus propios conjuntos de tiempo de ejecución.
public abstract class RuntimeSetSO<T>: ScriptableObject
{
public List<T> Items = new List<T>();
public void Add(T thing)
{
if (!Items.Contains(thing))
Items.Add(thing);
}
public void Remove(T thing)
{
if (Items.Contains(thing))
Items.Remove(thing);
}
}
[CreateAssetMenu(menuName = "Enemy Runtime Set", fileName =
"EnemyRuntimeSet")]
public class EnemyRuntimeSetSO: RuntimeSet<Enemy>
{
}

Uso y limitaciones
La gestión del conjunto del tiempo de ejecución depende de ti. A menudo, puedes utilizar un segundo MonoBehaviour para configurarlo o actualizarlo durante el juego.
Si desea que un componente se incluya automáticamente en un conjunto, también puede invocar los métodos Add o Remove del conjunto en tiempo de ejecución desde OnEnable y OnDisable del MonoBehaviour.
Por ejemplo, su clase Enemigo ficticia podría parecerse al siguiente fragmento de código.
Aquí, cada componente Enemigo puede añadirse o eliminarse usando sus métodos OnEnable o OnDisable. Si se establece el campo m_RuntimeSet en el Inspector, el componente Enemy aparecerá en el EnemyRuntimeSetSO automáticamente. Esto es especialmente útil si utiliza el componente Enemigo con Prefabs.
Una limitación de esta técnica es que si inspeccionas el ScriptableObject en tiempo de ejecución, no podrás ver el contenido de la lista de Items en el Inspector por defecto.
En su lugar, aparece un "Error de tipo" en cada campo de elemento. Por diseño, un ScriptableObject no puede serializar un objeto de escena. La lista funciona, pero los datos no se muestran correctamente.
Utilice una propiedad pública o el atributo HideInInspector si desea evitar confusiones e impedir que la lista se muestre en el Inspector.
También puede solucionar este problema con un script personalizado del Editor y el Inspector. Buenos ejemplos incluyen Soap - ScriptableObject Architecture Pattern en el Unity Asset Store o el ejemplo de demostración de Patterns abajo.
public class Enemy: MonoBehaviour
{
[SerializeField] private EnemyRuntimeSetSO m_RuntimeSet;
private void OnEnable()
{
m_RuntimeSet.Add(this);
}
private void OnDisable()
{
m_RuntimeSet.Remove(this);
}
}

Patterns demo
Aunque PaddleBallSO no presenta un caso de uso para los conjuntos en tiempo de ejecución, la demo de Patterns del proyecto incluye un pequeño ejemplo que muestra este patrón de diseño en funcionamiento.
Haz clic en el botón Generar bloque para rellenar el muro de bloques de forma incremental. El balón destruye un bloque al contacto.
Seleccione Seleccionar Conjunto para mostrar el BlockRuntimeSet_Data en el Inspector. Contiene la lista de todos los bloques de la escena.
Puedes rellenar continuamente bloques en la cuadrícula tan rápido como la bola los elimine. Al mismo tiempo, el conjunto en tiempo de ejecución realiza un seguimiento del número de bloques que cambian activamente en la lista de elementos.
La demostración Patterns muestra algunas mejoras en la calidad de vida cuando se trabaja con conjuntos en tiempo de ejecución:
- Inspector personalizado: Dado que ScriptableObjects no puede serializar objetos de escena fuera de la caja, el script GameObjectRuntimeSetEditor proporciona un inspector personalizado que anula el error de desajuste de tipo por defecto. Haz clic en cualquier bloque del Inspector para resaltarlo en la jerarquía de escenas.
- Actualizar eventos: El conjunto de tiempo de ejecución tiene un System.Action llamado ItemsChanged. Esto notifica al script Editor cuando se añaden o eliminan miembros de la lista de Elementos. Esto actualiza el Inspector cuando cambia la lista de Elementos. Un canal de eventos también sincroniza el número actual de Elementos con un componente personalizado RuntimeSetCounter. Muestra el recuento total de bloques en la interfaz de usuario en pantalla.

Cómo ayudan los conjuntos de tiempo de ejecución
La próxima vez que necesites una lista centralizada de GameObjects o componentes, considera los conjuntos en tiempo de ejecución. Pueden ser menos costosos que una operación de búsqueda(Object.FindObjectOfType o GameObject.FindWithTag) y menos problemáticos que los singletons.
Como ScriptableObjects, están desacoplados de GameObjects individuales en la escena. Mantenga tantos conjuntos de tiempo de ejecución como sea necesario para cualquier cosa que desee rastrear con frecuencia en tiempo de ejecución.
Los conjuntos de tiempo de ejecución pueden simplificar la supervisión de los objetos de juego sin incurrir en dependencias innecesarias. Puede dedicar el tiempo ahorrado durante la depuración y las pruebas a desarrollar nuevas experiencias de juego para sus jugadores.

Más recursos de ScriptableObject
Esperamos que los conjuntos de tiempo de ejecución puedan beneficiar a sus próximos proyectos.
Lee más sobre patrones de diseño con ScriptableObjects en nuestro e-book técnico, Crear una arquitectura de juego modular en Unity con ScriptableObjects. También puedes encontrar más información sobre patrones de diseño comunes en el desarrollo de Unity en Mejora tu código con patrones de programación de juegos.
¿Te gustó este contenido?
¡Sí!
No tanto.