
Programación avanzada y arquitectura de código
Entiende el Unity PlayerLoop
El Unity PlayerLoop contiene funciones para interactuar con el núcleo del motor del juego. Esta estructura incluye una serie de sistemas que manejan la inicialización y actualizaciones por fotograma. Todos tus scripts dependerán de este PlayerLoop para crear la jugabilidad. Al perfilar, verás el código de usuario de tu proyecto bajo el PlayerLoop, con componentes de Editor bajo el EditorLoop.
Es importante entender el orden de ejecución del FrameLoop de Unity. Cada script de Unity ejecuta varias funciones de evento en un orden predeterminado. Aprende la diferencia entre Awake, Start, Update y otras funciones que crean el ciclo de vida de un script para fortalecer el rendimiento.
Algunos ejemplos incluyen usar FixedUpdate en lugar de Update al tratar con un Rigidbody o usar Awake en lugar de Start para inicializar variables o el estado del juego antes de que comience el juego. Utiliza estos para minimizar el código que se ejecuta en cada fotograma. Awake se llama solo una vez durante la vida útil de la instancia del script y siempre antes de las funciones Start. Esto significa que deberías usar Start para tratar con objetos que sabes que pueden comunicarse con otros objetos, o consultarlos una vez que han sido inicializados.
Consulta el diagrama de flujo del ciclo de vida del script para el orden específico de ejecución de las funciones de evento.

Construye un Administrador de Actualización personalizado
Si tu proyecto tiene requisitos de rendimiento exigentes (por ejemplo, un juego de mundo abierto), considera crear un Administrador de Actualización personalizado usando Update, LateUpdate o FixedUpdate.
Un patrón de uso común para Update o LateUpdate es ejecutar lógica solo cuando se cumple alguna condición. Esto puede llevar a un número de callbacks por fotograma que efectivamente no ejecutan ningún código excepto para verificar esta condición.
Cada vez que Unity llama a un método de mensaje como Update o LateUpdate, realiza una llamada de interoperabilidad, es decir, una llamada desde el lado de C/C++ al lado administrado de C#. Para un pequeño número de objetos, esto no es un problema. Cuando tienes miles de objetos, este costo adicional comienza a ser significativo.
Suscribe objetos activos a este Administrador de Actualización cuando necesiten callbacks, y desuscríbelos cuando no los necesiten. Este patrón puede reducir muchas de las llamadas de interoperabilidad a tus objetos Monobehaviour.
Consulta las técnicas de optimización específicas del motor de juego para ejemplos de implementación.
Minimiza el código que se ejecuta en cada fotograma
Considera si el código debe ejecutarse en cada fotograma. Puedes mover la lógica innecesaria fuera de Update, LateUpdate y FixedUpdate. Estas funciones de eventos de Unity son lugares convenientes para poner código que debe actualizarse en cada fotograma, pero puedes extraer cualquier lógica que no necesite actualizarse con esa frecuencia.
Ejecuta la lógica solo cuando las cosas cambian. Recuerda aprovechar técnicas como el patrón observador en forma de eventos para activar una firma de función específica.
Si necesitas usar Update, podrías ejecutar el código cada n fotogramas. Esta es una forma de aplicar Time Slicing, una técnica común de distribuir una carga de trabajo pesada a través de múltiples fotogramas.
En este ejemplo, ejecutamos ExampleExpensiveFunction una vez cada tres fotogramas.
El truco es entrelazar esto con otro trabajo que se ejecute en los otros fotogramas. En este ejemplo, podrías "programar" otras funciones costosas cuando Time.frameCount % interval == 1 o Time.frameCount % interval == 2.
Alternativamente, usa una clase de Administrador de Actualización personalizada para actualizar los objetos suscritos cada n fotogramas.
Almacena en caché los resultados de funciones costosas
En las versiones de Unity anteriores a 2020.2, GameObject.Find, GameObject.GetComponent y Camera.main pueden ser costosos, por lo que es mejor evitar llamarlos en métodos Update.
Además, intenta evitar colocar métodos costosos en OnEnable y OnDisable si se llaman con frecuencia. Llamar a estos métodos con frecuencia puede contribuir a picos de CPU.
Siempre que sea posible, ejecuta funciones costosas, como MonoBehaviour.Awake y MonoBehaviour.Start durante la fase de inicialización. Almacena en caché las referencias necesarias y reutilízalas más tarde. Consulta nuestra sección anterior sobre Unity PlayerLoop para obtener más detalles sobre el orden de ejecución de scripts.
Aquí hay un ejemplo que demuestra el uso ineficiente de una llamada repetida a GetComponent:
void Update()
{
Renderer myRenderer = GetComponent();
ExampleFunction(myRenderer);
}
En su lugar, invoca GetComponent solo una vez ya que el resultado de la función se almacena en caché. El resultado en caché se puede reutilizar en Update sin más llamadas a GetComponent.
Lee más sobre el Orden de ejecución para funciones de eventos.
Evita eventos vacíos de Unity y declaraciones de registro de depuración
Las declaraciones de registro (especialmente en Update, LateUpdate o FixedUpdate) pueden afectar el rendimiento, así que desactiva tus declaraciones de registro antes de hacer una compilación. Para hacer esto rápidamente, considera hacer un Atributo Condicional junto con una directiva de preprocesamiento.
Por ejemplo, podrías querer crear una clase personalizada como se muestra a continuación.
Genera tu mensaje de registro con tu clase personalizada. Si desactivas el ENABLE_LOG preprocesador en Configuración del Jugador > Símbolos de Definición de Script, todas tus declaraciones de registro desaparecen de un solo golpe.
Manejar cadenas y texto es una fuente común de problemas de rendimiento en proyectos de Unity. Por eso, eliminar declaraciones de registro y su costosa formateo de cadenas puede ser una gran mejora de rendimiento.
De manera similar, los MonoBehaviours vacíos requieren recursos, así que deberías eliminar métodos Update o LateUpdate en blanco. Usa directivas de preprocesador si estás empleando estos métodos para pruebas:
#if UNITY_EDITOR
void Update()
{
}
#endif
Aquí, puedes usar el Update en el Editor para pruebas sin que sobrecargas innecesarias se cuelen en tu compilación.
Esta publicación de blog sobre 10,000 llamadas a Update explica cómo Unity ejecuta el Monobehaviour.Update.
Desactiva el registro de Stack Trace
Usa las opciones de Stack Trace en Configuración del Jugador para controlar qué tipo de mensajes de registro aparecen. Si tu aplicación está registrando errores o mensajes de advertencia en tu compilación de lanzamiento (por ejemplo, para generar informes de fallos en el entorno), desactiva los Stack Traces para mejorar el rendimiento.
Aprende más sobre Stack Trace logging.
Usa valores hash en lugar de parámetros de cadena
Unity no utiliza nombres de cadena para dirigirse a Animator, Material o Shader propiedades internamente. Por velocidad, todos los nombres de propiedades se convierten en Property IDs, y estos IDs se utilizan para dirigirse a las propiedades.
Al usar un método Set o Get en un Animator, Material o Shader, aprovecha el método de valor entero en lugar de los métodos de valor de cadena. Los métodos de valor de cadena realizan un hash de cadena y luego envían el ID hash a los métodos de valor entero.
Usa Animator.StringToHash para nombres de propiedades de Animator y Shader.PropertyToID para nombres de propiedades de Material y Shader.
Relacionado está la elección de la estructura de datos, que impacta en el rendimiento a medida que iteras miles de veces por cuadro. Sigue la guía de MSDN sobre estructuras de datos en C# como una guía general para elegir la estructura adecuada.

Agrupa tus objetos
Instantiate y Destroy pueden generar picos de recolección de basura (GC). Este es generalmente un proceso lento, así que en lugar de instanciar y destruir GameObjects regularmente (por ejemplo, disparar balas de una pistola), usa piscinas de objetos preasignados que pueden ser reutilizados y reciclados.
Crea las instancias reutilizables en un punto del juego, como durante una pantalla de menú o una pantalla de carga, cuando un pico de CPU es menos notable. Rastrea esta "piscina" de objetos con una colección. Durante el juego, simplemente habilita la siguiente instancia disponible cuando sea necesario, y desactiva objetos en lugar de destruirlos, antes de devolverlos a la piscina. Esto reduce el número de asignaciones gestionadas en tu proyecto y puede prevenir problemas de GC.
De manera similar, evita agregar componentes en tiempo de ejecución; Invocar AddComponent tiene un costo. Unity debe verificar duplicados u otros componentes requeridos cada vez que se agregan componentes en tiempo de ejecución. Instanciar un Prefab con los componentes deseados ya configurados es más eficiente, así que usa esto en combinación con tu Object Pool.
Relacionado, al mover Transforms, usa Transform.SetPositionAndRotation para actualizar tanto la posición como la rotación a la vez. Esto evita la sobrecarga de modificar un Transform dos veces.
Si necesitas instanciar un GameObject en tiempo de ejecución, parentearlo y reposicionarlo para optimización, consulta a continuación.
Para más información sobre Object.Instantiate, consulta la API de Scripting.
Aprende a crear un sistema simple de Object Pooling en Unity aquí.

Aprovecha el poder de los ScriptableObjects
Almacena valores o configuraciones invariables en un ScriptableObject en lugar de un MonoBehaviour. El ScriptableObject es un activo que vive dentro del proyecto. Solo necesita configurarse una vez y no puede ser adjuntado directamente a un GameObject.
Crea campos en el ScriptableObject para almacenar tus valores o configuraciones, luego referencia el ScriptableObject en tus MonoBehaviours. Usar campos del ScriptableObject puede prevenir la duplicación innecesaria de datos cada vez que instancias un objeto con ese MonoBehaviour.
Mira este tutorial de Introducción a ScriptableObjects y encuentra la documentación relevante aquí.

Una de nuestras guías más completas reúne más de 80 consejos prácticos sobre cómo optimizar tus juegos para PC y consola. Creado por nuestros expertos ingenieros de Éxito y Soluciones Aceleradas, estos consejos detallados te ayudarán a aprovechar al máximo Unity y mejorar el rendimiento de tu juego.