MonoBehavior de Unity

Unity puede estar desperdiciando mucho rendimiento de la CPU simplemente llamando a sus funciones MonoBehaviour. Realmente no importa lo que estén haciendo tus scripts. Si tiene cientos o miles de ellos, hay que tener en cuenta que hay un nuevo campo de optimización.

Métodos mágicos

Las llamadas a funciones MonoBehaviour son lentas. Estoy hablando de funciones como Update() , LateUpdate(), OnRender(), etc. Son los llamados métodos mágicos, y si estas familiarizado con los lenguajes de programación orientados a objetos, este concepto parece llamar a un método usando mecanismo de reflexión (la reflexión habilita llamadas de métodos incluso si no conoce la interfaz).

Las llamadas de reflexión son costosas, por lo que Unity hace todo lo posible para almacenar en caché cualquier operación, por lo que el conjunto de instrucciones de la CPU necesarias para llamar a un método mágico cada fotograma podría ser mínimo. Pero aún puede ser lento, muy lento …

¿Por qué es tan lento?

No voy a hablar sobre los detalles (pero si realmente quieres leer sobre los detalles, mira el final de este artículo para ver los enlaces), así que imagina que Unity intenta ser lo más flexible y fácil de usar. Hacer algo cuesta operaciones de CPU porque el motor no puede hacer suposiciones sobre su juego y necesita hacer un montón de comprobaciones para llamar a sus funciones mágicas en los objetos correctos, en el orden correcto, y no chocar mientras tanto.

¿Se puede volver más rápido?

¡Sí!, ¡Puede!, ¿Cómo?, Debe asumir la responsabilidad de llamar a la función Update() definiendo su propia función y llamándola desde un administrador. De esta forma, asumes la responsabilidad de actualizar tus objetos. ¿Cuánto más rápido puede llegar a ser? Bueno, depende de la plataforma. Mostrare las medidas hechas por Valentin SimonovValentin Simonov en el blog oficial de Unity:

.

Aquí puedes ver que la diferencia puede valer la pena. Esta es una medida de la llamada Update(), 10000 veces.

Creando un Manager

Presentaré un simple ejemplo de un administrador llamado BoxManager que está administrando scripts de BoxManaged. El manager tiene dos responsabilidades:

  • Mantener actualizada la lista de objetos administrados.
  • Llamar a funciones de tipo update en objetos administrados cuando se llama al administrador Update().

El código puede verse así:


using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class BoxManager : MonoBehaviour
{
	public static BoxManager Instance { get; private set; }

	public List<BoxManaged> _managedBoxes = new List<BoxManaged>();

	void Awake()
	{
		Instance = this;
	}

	void Update()
	{
		// Actualizar los objetos aquí
	}

	public void Register(BoxManaged box)
	{
		_managedBoxes.Add(box);
	}

	public void Unregister(BoxManaged box)
	{
		_managedBoxes.Remove(box);
	}
}

Como puedes ver, es realmente simple. Antes de implementar la función Update(), echemos un vistazo a BoxManaged.cs.


using UnityEngine;

public class BoxManaged : MonoBehaviour {

	private Vector3 _position;

	private Transform _transform;

	void OnEnable()
	{
		BoxManager.Instance.Register(this);
	}

	void OnDisable()
	{
		BoxManager.Instance.Unregister(this);
	}

	public void ManagedUpdate()
	{
		// El update que usariamos
	}
}

Se registra a sí mismo cuando está habilitado y se anula el registro cuando está deshabilitado. La función ManagedUpdate() es una función que reemplazará la función mágica Update(). Implementa BoxManager.Update() , por lo que podras llamar a todos los BoxManaged.ManagedUpdate() de una vez.


	void Update()
	{
		for (int i = 0; i < _managedBoxes.Count; ++i)
		{
			_managedBoxes[i].ManagedUpdate();
		}
	}

Ahora en ManagedUpdate() puedes hacer todo lo que normalmente harías en la función Update().

No usar foreach para iteraciones. En primer lugar, porque está generando una pequeña cantidad de basura de la versión de Unity de Mono. En segundo lugar, porque simplemente parece ser más lento.

¿Debería importarme?

Depende del tipo de juego que crees y de la plataforma objetivo. Hágase una pregunta: ¿tiene muchos objetos MonoBehaviour con llamadas a Update()? No necesariamente tiene que ser Update(), puede ser cualquier cosa que se invoque con cada fotograma. ¡Entonces, si te diriges a móviles, definitivamente vale la pena intentarlo! ¿Dirigiéndose a standalones? Todavía es algo que puedes considerar, especialmente si estás planeando tener una gran cantidad de objetos.

A veces puede necesitar un administrador, incluso si tiene una cantidad relativamente pequeña de objetos. En iOS hubo un problema con la función OnRender(). Tenerlo en 30-40 objetos podría disminuir el rendimiento del juego dos veces. ¿La solución? Un administrador como el presentado anteriormente, pero en lugar de llamar a Update() debería estar llamando al código OnRender().

Tenga en cuenta que esta es una de las muchas estrategias de optimización que puede usar. Sin embargo, este está bastante oculto, a menos que lo sepa, le será difícil encontrarlo. Esa es la razón por la cual este artículo ha cobrado vida.

Referencias

Ref
/img/ref.png
.