viernes, 21 de febrero de 2014

Unity3D Scripting: Trucos para optimizar código

Hoy voy a enseñar unos pequeños trucos de programación para optimizar al máximo nuestro código de manera que consuma la menor memoria posible en nuestro videojuego.

Estas prácticas, especialmente en los videojuegos son esenciales ya que de todo los tipos de software, es el que siempre va a requerir mas recursos al necesitar usar modelos 3D, físicas complejas, texturas bien definidas para los modelos 3D, y nuestro código, al fin y al cabo, es uno de los factores críticos en este sentido.

Como siempre, pondré los ejemplos en C# que es el lenguaje que suelo usar porque ya de por si, te obliga un poco mas que Javascript a "tipar" todos los objetos, y propiedades, además de que personalmente C, me gustaba mucho y en su día opté por aprender mejor C# que Javascript.



Cachear componentes
Básicamente consiste en, si por ejemplo en nuestro script usaremos el transform del objeto que lo contiene para desplazarlo, cachearlo al inicio del script de manera que el acceso a el será mas rápido teniendo cacheado el transform que buscándolo dentro del objeto. 
En resumen pasaríamos de esto:

using UnityEngine;
using System.Collections;

public class Ejemplo : MonoBehaviour {
    void Update() {
        transform.Translate(0, 0, 1); //Busca el transform del objeto que contiene este script y lo translada
    }
}
a esto:
using UnityEngine;
using System.Collections;

public class Ejemplo : MonoBehaviour {
    private Transform miTransform;
    void Awake() {
        miTransform = transform; //Cachea el transform del objeto que contiene este script
    }
    void Update() {
        myTransform.Translate(0, 0, 1); //Lo ejecutamos a través del objeto cacheado
    }
}
así el acceso, en este caso al transform, de nuestro objeto será mas rápido por tenerlo cacheado, recuerda que, si no entiendes muy bien los eventos de Unity3D o no los conoces bien, dejo aquí un enlace a un tutorial básico sobre los eventos en C#.

Cachear la definición de los Array
Al igual que el ejemplo anterior, si sabemos que vamos a usar un Array en un futuro, lo correcto sería definirlo en el evento Awake como hemos hecho en el ejemplo anterior, básicamente se haría así:
using UnityEngine;
using System.Collections;

public class Ejemplo : MonoBehaviour {
    private Vector3[] posiciones;
    void Awake() {
        posiciones = new Vector3[10];
        for (int i = 0;i < posiciones.Length;i++) {
            posiciones[i] = Vector3.zero; //Rellenamos nuestro Array de Vector3 con ceros
        }
    }
}
Aunque después se le vayan a dar valores diferentes, es muy buena práctica inicializarlos en el Awake y si quieres, asignarle los valores en el Start ya que, estarán los vectores ya definidos.
No llames a una función que no vas a usar
Un ejemplo práctico sería pues tener un enemigo que si está suficientemente cerca de nosotros como para mostrarse en la cámara se mueva y si está lejos estar quieto, en ese caso la solución fácil sería hacer esto:
using UnityEngine;
using System.Collections;

public class Ejemplo : MonoBehaviour {
    public Transform target;
    void Update() {
        if (Vector3.Distance(transform.position, target.position) > 10)
        {
            return;
        }
        
    }
}
Estaríamos llamando a un script para hacer esa comprobación constantemente, sin necesidad de ello en muchos casos, por ejemplo podríamos ver si está dentro de la cámara o no con estos eventos y le diríamos pues que se active o no con ellos:
using UnityEngine;
using System.Collections;

public class Ejemplo : MonoBehaviour {
    void OnBecameVisible() {
        enabled = true;
    }
    void OnBecameInvisible() {
        enabled = false;
    } 
}
O también se podría recurrir a triggers y que nuestro personaje tenga una esfera transparente que lo envuelva y todos los enemigos que entren dentro de esa esfera se activen al colisionar con el trigger de la misma, por ejemplo:
using UnityEngine;
using System.Collections;

public class Ejemplo : MonoBehaviour {
    void OnTriggerEnter(Collider c) {
        if (c.CompareTag("Player"))
            enabled = true;
        
    }
    void OnTriggerExit(Collider c) {
        if (c.CompareTag("Player"))
            enabled = false;
        
    }
}
Con un poco de ingenio se pueden hacer ciertas cosas para evitar llamar a un evento constantemente sin necesidad de ello.

Aunque en los otros tutoriales previos a este no haya usado algunas prácticas como cachear las propiedades, ha sido para poner el menor código posible y que quede bien claro el concepto de cada tutorial.

2 comentarios:

  1. Hola, me encuentro en es momento del desarrollo donde estoy optimizando scripts y me pareció muy interesante tu explicación. Algunas cosas ya las estaba haciendo pero otras, no. Trabajo normalmente con javascript. Sabes donde podría aprender bien C# para Unity?

    ResponderEliminar
    Respuestas
    1. Pues mi consejo es que lo mismo que hiciste en Javascript intentes traducirlo a C#, y para aprender lo básico yo usé la web oficial de Unity3D y el MSDN, aquí tienes un par de enlaces que te van a servir de ayuda, el resto es ponerse, a programar se aprende programando y aprender un lenguaje nuevo de programación pues mas de lo mismo ;)

      http://unity3d.com/es/learn
      http://msdn.microsoft.com/es-es/library/618ayhy6.aspx

      Eliminar