Buscar..


Observaciones

  1. Si es posible, deshabilite los scripts en los objetos cuando no sean necesarios. Por ejemplo, si tiene un script en un objeto enemigo que los buscadores buscan y dispara al jugador, considere desactivar este script cuando el enemigo está demasiado lejos, por ejemplo, del jugador.

Cheques rápidos y eficientes

Evite operaciones innecesarias y llamadas a métodos siempre que pueda, especialmente en un método que se llama muchas veces por segundo, como Update .

Controles de distancia / rango

Use sqrMagnitude lugar de magnitude al comparar distancias. Esto evita operaciones sqrt innecesarias. Tenga en cuenta que al utilizar sqrMagnitude , el lado derecho también debe estar cuadrado.

if ((target.position - transform.position).sqrMagnitude < minDistance * minDistance))

Cheques de límites

Las intersecciones de objetos pueden verificarse crudamente verificando si sus límites de Collider / Renderer intersecan. La estructura de Bounds también tiene un práctico método Intersects que ayuda a determinar si se intersecan dos límites.

Bounds también nos ayudan a calcular una aproximación aproximada de la distancia real (de superficie a superficie) entre los objetos (consulte Bounds.SqrDistance ).

Advertencias

La comprobación de límites funciona realmente bien para los objetos convexos, pero las comprobaciones de límites en objetos cóncavos pueden llevar a imprecisiones mucho más altas según la forma del objeto.

Mesh.bounds se recomienda usar Mesh.bounds ya que devuelve los límites del espacio local. Utilice MeshRenderer.bounds en MeshRenderer.bounds lugar.

Poder coroutine

Uso

Si tiene una operación de larga ejecución que se basa en la API de Unity no segura para subprocesos, utilice Coroutines para dividirla en varios marcos y mantener su aplicación receptiva.

Coroutines también ayuda a realizar acciones caras en cada fotograma en lugar de ejecutar esa acción en cada fotograma.

División de rutinas de larga ejecución en varios marcos

Coroutines ayuda a distribuir operaciones de larga duración en varios marcos para ayudar a mantener la velocidad de cuadros de su aplicación.

Las rutinas que pintan o generan terreno por procedimientos o generan ruido son ejemplos que pueden necesitar el tratamiento de Coroutine.

for (int y = 0; y < heightmap.Height; y++) 
{
    for (int x = 0; x < heightmap.Width; x++)
    {
        // Generate pixel at (x, y)
        // Assign pixel at (x, y)
        
        // Process only 32768 pixels each frame
        if ((y * heightmap.Height + x) % 32 * 1024) == 0)
            yield return null; // Wait for next frame
    }
}

El código anterior es un ejemplo fácil de entender. En el código de producción que es mejor evitar el cheque por píxel que comprueba cuándo yield return (tal vez hacerlo cada 2-3 filas) y comprobar la validez de calcular for la longitud del bucle de antemano.

Realización de acciones caras con menos frecuencia

Coroutines lo ayuda a realizar acciones costosas con menos frecuencia, por lo que no es un golpe de rendimiento tan grande como lo sería si se realizara cada fotograma.

Tomando el siguiente ejemplo directamente del Manual :

private void ProximityCheck() 
{
    for (int i = 0; i < enemies.Length; i++) 
    {
        if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance)
                return true;
    }
    return false;
}

private IEnumerator ProximityCheckCoroutine() 
{
    while(true) 
    {
        ProximityCheck();
        yield return new WaitForSeconds(.1f);
    }
}

Las pruebas de proximidad se pueden optimizar aún más utilizando la API de CullingGroup .

Errores comunes

Un error común que cometen los desarrolladores es acceder a los resultados o efectos secundarios de las coroutinas fuera de ellas. Coroutines devuelve el control a la persona que llama tan pronto como se encuentra una declaración de yield return y el resultado o efecto secundario puede que aún no se haya realizado. Para evitar problemas en los que tenga que usar el resultado / efecto secundario fuera de la rutina, marque esta respuesta .

Instrumentos de cuerda

Se podría argumentar que hay un mayor número de recursos en la Unidad que en la humilde cadena, pero es uno de los aspectos más fáciles de solucionar desde el principio.

Las operaciones de cuerdas construyen basura

La mayoría de las operaciones de cadena generan pequeñas cantidades de basura, pero si esas operaciones se llaman varias veces en el transcurso de una sola actualización, se acumula. Con el tiempo, activará la recolección automática de basura, lo que puede resultar en un pico de CPU visible.

Caché tus operaciones de cadena

Considere el siguiente ejemplo.

string[] StringKeys = new string[] {
    "Key0",
    "Key1",
    "Key2"
};

void Update()
{
    for (var i = 0; i < 3; i++)
    {
        // Cached, no garbage generated
        Debug.Log(StringKeys[i]);
    }

    for (var i = 0; i < 3; i++)
    {
        // Not cached, garbage every cycle
        Debug.Log("Key" + i);
    }

    // The most memory-efficient way is to not create a cache at all and use literals or constants.
    // However, it is not necessarily the most readable or beautiful way.
    Debug.Log("Key0");
    Debug.Log("Key1");
    Debug.Log("Key2");
}

Puede parecer tonto y redundante, pero si está trabajando con Shaders, puede encontrarse en situaciones como estas. Cachear las llaves marcará la diferencia.

Tenga en cuenta que las cadenas literales y las constantes no generan basura, ya que se inyectan estáticamente en el espacio de la pila del programa. Si está generando cadenas en tiempo de ejecución y está garantizado que generará las mismas cadenas cada vez que el ejemplo anterior, el almacenamiento en caché definitivamente ayudará.

Para otros casos donde la cadena generada no es la misma cada vez, no hay otra alternativa para generar esas cadenas. Como tal, el pico de memoria con cadenas que se generan manualmente cada vez es generalmente despreciable, a menos que se generen decenas de miles de cadenas a la vez.

La mayoría de las operaciones de cadena son mensajes de depuración

Hacer operaciones de cadena para mensajes de depuración, es decir. Debug.Log("Object Name: " + obj.name) está bien y no se puede evitar durante el desarrollo. Sin embargo, es importante asegurarse de que los mensajes de depuración irrelevantes no terminen en el producto publicado.

Una forma es usar el atributo condicional en sus llamadas de depuración. Esto no solo elimina las llamadas de método, sino también todas las operaciones de cadena que entran en él.

using UnityEngine;
using System.Collections;

public class ConditionalDebugExample: MonoBehaviour
{
    IEnumerator Start()
    {
        while(true)
        {
            // This message will pop up in Editor but not in builds
            Log("Elapsed: " + Time.timeSinceLevelLoad);
            yield return new WaitForSeconds(1f);
        }
    }

    [System.Diagnostics.Conditional("UNITY_EDITOR")]
    void Log(string Message)
    {
        Debug.Log(Message);
    }
}

Este es un ejemplo simplificado. Es posible que desee invertir algo de tiempo en diseñar una rutina de registro más completa.

Comparación de cuerdas

Esta es una optimización menor, pero vale la pena una mención. Comparar cadenas es un poco más complicado de lo que uno podría pensar. El sistema intentará tener en cuenta las diferencias culturales por defecto. Puede optar por utilizar una comparación binaria simple, que funciona más rápido.

// Faster string comparison
if (strA.Equals(strB, System.StringComparison.Ordinal)) {...}
// Compared to
if (strA == strB) {...}

// Less overhead
if (!string.IsNullOrEmpty(strA)) {...}
// Compared to
if (strA == "") {...}

// Faster lookups
Dictionary<string, int> myDic = new Dictionary<string, int>(System.StringComparer.Ordinal);
// Compared to
Dictionary<string, int> myDictionary = new Dictionary<string, int>();

Referencias de caché

Cache las referencias para evitar las llamadas costosas, especialmente en la función de actualización. Esto se puede hacer almacenando estas referencias en caché al inicio, si están disponibles o cuando estén disponibles, y comprobando que no haya null / bool flat para evitar volver a obtener la referencia.

Ejemplos:

Referencias de componentes de caché

cambio

void Update()
{
    var renderer = GetComponent<Renderer>();
    renderer.material.SetColor("_Color", Color.green);
}

a

private Renderer myRenderer;
void Start()
{
    myRenderer = GetComponent<Renderer>();
}

void Update()
{
    myRenderer.material.SetColor("_Color", Color.green);
}

Referencias de objetos de caché

cambio

void Update()
{
    var enemy = GameObject.Find("enemy");
    enemy.transform.LookAt(new Vector3(0,0,0));
}

a

private Transform enemy;

void Start()
{
    this.enemy = GameObject.Find("enemy").transform;
}

void Update()
{
    enemy.LookAt(new Vector3(0, 0, 0));
}

Además, caché llamadas costosas como llamadas a Mathf cuando sea posible.

Evitar los métodos de llamada utilizando cadenas

Evite llamar a métodos utilizando cadenas que puedan aceptar métodos. Este enfoque hará uso de la reflexión que puede ralentizar el juego, especialmente cuando se utiliza en la función de actualización.

Ejemplos:

    //Avoid StartCoroutine with method name
    this.StartCoroutine("SampleCoroutine");

    //Instead use the method directly
    this.StartCoroutine(this.SampleCoroutine());

    //Avoid send message
    var enemy = GameObject.Find("enemy");
    enemy.SendMessage("Die");

    //Instead make direct call
    var enemy = GameObject.Find("enemy") as Enemy;
    enemy.Die();

Evita los métodos de unidad vacía.

Evita los métodos de unidad vacía. Además de ser un mal estilo de programación, hay una pequeña sobrecarga involucrada en las secuencias de comandos de tiempo de ejecución. En muchos casos, esto puede acumularse y afectar el rendimiento.

void Update
{
}

void FixedUpdate
{
}


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow