Diferencia entre revisiones de «UNITY Operaciones sobre objetos 3D»

De MediaWiki
Ir a la navegación Ir a la búsqueda
(No se muestran 47 ediciones intermedias del mismo usuario)
Línea 33: Línea 33:
 
[[Imagen:Unity3d_operaciones_1B.jpg|300px|center]]
 
[[Imagen:Unity3d_operaciones_1B.jpg|300px|center]]
  
 +
 +
::<u>Nota:</u> En el ejemplo se está creando una variable de tipo Transform para guardar el transform local del GameObject asociado al script. Esto no es necesario ya que siempre vamos a poder acceder al mismo escribiendo '''transform''' con minúsculas.
 +
:: Crear una variable de tipo Transform es necesario cuando queremos referenciar el transform de otros GameObhects diferentes.
  
  
Línea 57: Línea 60:
  
  
:* Seleccionado en la barra de herramientas la opción de escala y después, gráficamente sobre la figura, <u>presionar el botón izquierdo del ratón sobre uno de los ejes de colores</u> y sin soltar, mover el ratón.
+
:* Seleccionado en la barra de herramientas la opción de escala (o pulsando la '''tecla E''') y después, gráficamente sobre la figura, <u>presionar el botón izquierdo del ratón sobre uno de los ejes de colores</u> y sin soltar, mover el ratón.
 
<gallery caption="Cambiando la escala" widths="350" heights="300px" perrow="2">
 
<gallery caption="Cambiando la escala" widths="350" heights="300px" perrow="2">
 
Image:Unity3d_escala_3.jpg| Escogemos la opción '''Escala''' de la '''Barra de herramientas'''.
 
Image:Unity3d_escala_3.jpg| Escogemos la opción '''Escala''' de la '''Barra de herramientas'''.
 
Image:Unity3d_escala_4.jpg| Presionamos con el botón izquierdo del ratón uno de los ejes (el que queramos modificar la escala) y sin soltar, movemos el ratón. Podemos observar como el valor en la ventana Inspector se modifica.
 
Image:Unity3d_escala_4.jpg| Presionamos con el botón izquierdo del ratón uno de los ejes (el que queramos modificar la escala) y sin soltar, movemos el ratón. Podemos observar como el valor en la ventana Inspector se modifica.
 
</gallery>
 
</gallery>
 +
 +
 +
* Si pulsamos la '''tecla Ctrl' mientras escalamos, iremos escalando de una décima de unidad en una décima de unidad.
  
 
<br />
 
<br />
Línea 114: Línea 120:
  
  
:* Seleccionado en la barra de herramientas la opción de rotación y después, gráficamente sobre la figura, <u>presionar el botón izquierdo del ratón sobre uno de los ejes de colores</u> y sin soltar, mover el ratón.
+
:* Seleccionado en la barra de herramientas la opción de rotación (o pulsando la '''tecla E''') y después, gráficamente sobre la figura, <u>presionar el botón izquierdo del ratón sobre uno de los ejes de colores</u> y sin soltar, mover el ratón.
 
<gallery caption="Rotando en Unity3D" widths="450" heights="300px" perrow="2">
 
<gallery caption="Rotando en Unity3D" widths="450" heights="300px" perrow="2">
 
Image:Unity3d_rotacion_8.jpg| Escogemos la opción '''Rotar''' de la '''Barra de herramientas'''.
 
Image:Unity3d_rotacion_8.jpg| Escogemos la opción '''Rotar''' de la '''Barra de herramientas'''.
Línea 120: Línea 126:
 
</gallery>
 
</gallery>
 
: <u>Nota:</u> No os extrañéis sin al modificar uno de los ejes veis que se modifica la posición. Eso es normal, ya que estamos moviendo la figura.
 
: <u>Nota:</u> No os extrañéis sin al modificar uno de los ejes veis que se modifica la posición. Eso es normal, ya que estamos moviendo la figura.
 +
 +
<br />
 +
==Pivot==
 +
 +
* Como comenté en otro punto, los objetos que arrastramos o creamos en la Hierarchy Window son GameObjects los cuales pueden ser dispuestos de forma jerárquica, unos 'dentro' de otros, como si fueran carpetas dentro de carpetas.
 +
: Al hacer esto, todas las propiedades 'transform' del GameObject hijo tienen unos valores relativos con respecto al GameObject padre.
 +
: Además, cualquier modificación que hagamos sobre el padre (rotar, trasladar o escala) también se reflejará en el hijo de la misma forma.
 +
 +
* En todos los objetos 3D existe un punto denominado '''Pivot''' que indica al punto de referencia para rotar dicho objeto.
 +
: Normalmente este punto se encuentra en el centro del objeto 3D, pero no siempre es así.
 +
: Cuando importamos modelos complejos formados por múltiples figuras 3D puede ser que cada figura individual (imaginar un tanque con su torreta, o un avión con sus hélices, ruedas y ametralladoras) tenga su pivot en otro punto. En ese caso, cuando intentemos rotar, la rotación no la hará con respecto al punto que pensamos, sino con respecto a su pivot.
 +
: El pivot lo establece el diseñador gráfico con el programa 3D que haya utilizado.
 +
 +
 +
<gallery caption="Punto Pivot" widths="450" heights="300px" perrow="2">
 +
Image:Unity3d_rotac_10b.jpg| En nuestra escena creamos un cubo en el punto (0,0,0) y una esfera que se encuentre a su lado '''y que se hija del cubo en la jerarquía'''.
 +
Image:Unity3d_rotac_10c.jpg| Si pulsamos sobre el cubo, podemos ver como se muestra el 'centro' de los dos objetos. Si ahora movemos el cubo, también se moverá la esfera.
 +
Image:Unity3d_rotac_10d.jpg| Si pulsamos en el botón de 'centro' cambia a '''pivot''' y ahora aparece el punto de referencia que va a utilizar el cubo para rotar, y que también va a utilizar la esfera cuando se rote el cubo.
 +
Image:Unity3d_rotac_10e.jpg| Si ahora rotáis el cubo podéis observar como rota la esfera de acuerdo al punto pivot del cubo.
 +
</gallery>
 +
 +
 +
* '''<u>Nota:</u>''' Recordar por tanto que cuando trabéis con modelos importados, si cuando vayáis a realizar una rotación, el modelo no rota según lo esperado, comprobar donde se encuentra su pivot.
 +
 +
 +
<br />
 +
==Ejes locales / globales de rotación==
 +
 +
* Otro aspecto ya comentado anteriormente es de los ejes X-Y-Z asociados a un GameObject.
 +
: Cuando rotamos un GameObject, sus ejes también son rotados.
 +
 +
[[Imagen:Unity3d_rotac_10f.jpg|500px|center]]
 +
: Podemos comprobar como el cubo, al rotarlo -45º en el eje Z, sus ejes Y (color amarillo) y X (color rojo) también son rotados.
 +
 +
 +
* En cualquier momento podemos 'ver' cuales son los ejes del 'mundo' (los no rotados) si pulsamos sobre el botón que pone '''Local''' en la ToolBar (el texto del botón pasará a poner '''Global''', para indicar que estamos viendo los ejes del mundo, no los ejes locales del GameObject):
 +
 +
[[Imagen:Unity3d_rotac_10g.jpg|500px|center]]
 +
: Se puede observar como los ejes coinciden con los de Unity.
 +
 +
 +
* Veremos que cuando programemos por medio de 'scripts', podemos acceder:
 +
:* A los ejes locales de la forma: transform.up (eje Y) / transform.forward (eje Z) / transform.right (eje X)
 +
:* A los ejes globales (los no rotados): Vector3.up (eje Y) / Vector3.forward (eje Z) / Vector3.right (eje X)
 +
 +
 +
 +
 +
* Si pulsamos la '''tecla Ctrl' mientras rotamos, rotaremos de 15º en 15º.
  
 
<br />
 
<br />
Línea 144: Línea 199:
  
  
:* Seleccionado en la barra de herramientas la opción de traslación y después, gráficamente sobre la figura, <u>presionar el botón izquierdo del ratón sobre uno de los ejes de colores</u> y sin soltar, mover el ratón.
+
:* Seleccionado en la barra de herramientas la opción de traslación (o pulsando la '''tecla W''') y después, gráficamente sobre la figura, <u>presionar el botón izquierdo del ratón sobre uno de los ejes de colores</u> y sin soltar, mover el ratón.
 
<gallery caption="Trasladando en Unity3D" widths="450" heights="300px" perrow="2">
 
<gallery caption="Trasladando en Unity3D" widths="450" heights="300px" perrow="2">
 
Image:Unity3d_trasladar_3.jpg| Escogemos la opción '''Mover''' de la '''Barra de herramientas'''.
 
Image:Unity3d_trasladar_3.jpg| Escogemos la opción '''Mover''' de la '''Barra de herramientas'''.
Línea 152: Línea 207:
  
  
 +
 +
* Si pulsamos la '''tecla Ctrl' mientras nos trasladamos, iremos moviendo la figura de unidad en unidad.
  
  
  
 
<br />
 
<br />
 +
==Ajustando los planos==
 +
 +
* Cuando tenemos varias figuras 3D y queremos 'colocar' unas adyacentes a otras, en cualquiera de sus 3 ejes (x,y,z) puede llegar a ser una operación 'complicada'.
 +
: Veamos un ejemplo:
 +
<gallery caption="Ajustando los planos" widths="450" heights="300px" perrow="2">
 +
Image:Unity3d_trasladar_5.jpg| Imaginemos que queremos colocar el cubo encima de la superficie de otro cubo el cual hemos modificado su escala. Si lo hacemos 'manualmente' puede ser complicado ajustar exactamente el punto en el que el cubo se coloca 'encima'.
 +
Image:Unity3d_trasladar_6.jpg| Con el cubo seleccionado, '''pulsamos la <u>tecla V</u>''' y sin soltar, movemos el ratón a uno de los vértices de la figura. Como en nuestro caso queremos colocar el cubo encima, nos valdría cualquiera de los vértices inferiores.
 +
Image:Unity3d_trasladar_7.jpg| Sin soltar la tecla V y una vez situado el el vértice que queramos, presionamos el botón izquierdo del ratón y sin soltar, '''lo llevamos a uno de los vértices superiores del cubo inferior'''. Veremos que el cubo se sitúa justo encima. Ahora solo tenemos que desplazarlo horizontalmente al lugar que queramos. La misma operación se puede realizar con cualquier vértice.
 +
</gallery>
 +
 +
 +
 +
<br />
 +
 
=Avanzado: Conceptos para programadores=
 
=Avanzado: Conceptos para programadores=
  
Línea 282: Línea 353:
 
<source lang="javascript" line highlight="2-4,18-20">
 
<source lang="javascript" line highlight="2-4,18-20">
 
public class PruebaMatrix4x4 : MonoBehaviour {
 
public class PruebaMatrix4x4 : MonoBehaviour {
     public Vector3 translation;
+
     public Vector3 translation = new Vector3(5, 2, 3);
     public Vector3 eulerAngles;
+
     public Vector3 eulerAngles = new Vector3(30, 45, 25);
 
     public Vector3 scale = new Vector3(1, 1, 1);
 
     public Vector3 scale = new Vector3(1, 1, 1);
 
     private MeshFilter mf;
 
     private MeshFilter mf;
Línea 326: Línea 397:
  
 
<br />
 
<br />
 +
 
===Manipular la posición en Unity===
 
===Manipular la posición en Unity===
  
Línea 345: Línea 417:
  
 
<syntaxhighlight lang="java" line highlight="">
 
<syntaxhighlight lang="java" line highlight="">
         posicion = gameObject.transform.position;  // Aparece en Unity en la ventana Inspector debajo de la sección 'Transform'. No hace falta poner gameObject
+
         Vector3 posicion = gameObject.transform.position;  // Aparece en Unity en la ventana Inspector debajo de la sección 'Transform'. No hace falta poner gameObject
         rotacion = gameObject.transform.rotation;  // Aparece en Unity en la ventana Inspector debajo de la sección 'Transform'. No hace falta poner gameObject
+
         Quaternion rotacion = gameObject.transform.rotation;  // Aparece en Unity en la ventana Inspector debajo de la sección 'Transform'. No hace falta poner gameObject
         escala = transform.localScale;              // No hace falta poner gameObject
+
         Vector3 escala = transform.localScale;              // No hace falta poner gameObject
  
 
</syntaxhighlight>
 
</syntaxhighlight>
Línea 416: Línea 488:
 
public class UD_Movim_basico_1 : MonoBehaviour {
 
public class UD_Movim_basico_1 : MonoBehaviour {
  
     [SerializeFiled]public Vector3 posInicial = new Vector3(0, 0, 0);
+
     [SerializeFiled]private Vector3 posInicial = new Vector3(0, 0, 0);
     [SerializeFiled]public Vector3 rotInicial = new Vector3(0, 0, 0);
+
     [SerializeFiled]private Vector3 rotInicial = new Vector3(0, 0, 0);
     [SerializeFiled]public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
+
     [SerializeFiled]private Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
  
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
: Así si 'arrastramos' este GameObject a otro diferente, no podremos acceder a las propiedades anteriores a pesar de estar definidas como '''public'''.
+
: Así si 'arrastramos' este GameObject a otro diferente, no podremos acceder a las propiedades anteriores al estar definidas como '''private'''.
  
  
Línea 714: Línea 786:
  
 
<br />
 
<br />
=====Método MoveTowards=====
+
=====Método Translate=====
  
:Dentro de la [https://docs.unity3d.com/ScriptReference/Vector3.html clase Vector3].
+
* [https://docs.unity3d.com/ScriptReference/Transform.Translate.html Método 'translate'] de la clase Transform: Modifica la posición de un gameobject desplazándola en los valores indicados. Es equivalente a poner: position = position+valor. Es decir, '''traslada''' el objeto desde su posición a una nueva añadiendo un nuevo valor. No hace como la propiedad position, que 'mueve' el gameobject a la posición indicada.
  
 +
<u>Nota:</u> Tener en cuenta que el método está sobrecargado y existen varias formas de realizar la misma acción.
  
* [https://docs.unity3d.com/ScriptReference/Vector3.MoveTowards.html Método MoveTowards]: Mueve el GameObject desde la posición indicada por el parámetro 'current' hasta la posición indicada por el parámetro 'target' en la distancia indicada por el parámetro step.
 
  
::* Normalmente 'step' tiene como valor: Velocidad * Time.deltaTime
 
  
:: Debo aclarar que 'mover' no es la definición exacta, ya que no modifica los vectores que le pasamos como parámetros, sino que '''devuelve un Vector3''' con la nueva posición (la que va de origen a destino) movida un 'poquito' en cada frame.
+
: Veamos un ejemplo sobre el script anterior:
:: Lo que debemos hacer es igualar ese vector a la posición de lo que queramos mover, de la forma: transform.position=Vector.moveTowards(origen, destino, velocidad*delta).
 
  
 
+
<syntaxhighlight lang="java" line highlight="20">
:: Veamos un ejemplo, modificando el código del script 'UD_Movim_basico_1' para que el cubo se mueva hacia donde se encuentre el ratón en la pantalla:
 
<u>Nota:</u> Recordar que [http://wiki.cifprodolfoucha.es/index.php?title=UNITY_C%C3%A1mara_en_perspectiva#Paso_de_coordenadas_desde_ScreenSpace_a_WorldSpace vimos anteriormente como proyectar un punto desde la pantalla al espacio 3D].
 
 
 
::<syntaxhighlight lang="java" line highlight="21-25">
 
 
public class UD_Movim_basico_1 : MonoBehaviour {
 
public class UD_Movim_basico_1 : MonoBehaviour {
  
Línea 737: Línea 803:
 
     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 
     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
  
    public float posFinalX = 5f;
+
     public float velocidad = 2f;
     public float velocidad = 1f;
 
  
 
     // Use this for initialization
 
     // Use this for initialization
Línea 747: Línea 812:
  
 
     }
 
     }
 +
 +
// Update is called once per frame
 +
void Update () {
  
    // Update is called once per frame
+
        transform.Translate(Vector3.up* velocidad * Time.deltaTime);
    void Update () {
 
  
        Vector3 posRaton = Input.mousePosition;
+
}
        posRaton.z = 8;        // Plano donde se encuentra el cubo con respecto a la cámara. Adaptarlo a vuestro caso si la cámara se encuentra en otra posición.
 
        Vector3 posMundo = Camera.main.ScreenToWorldPoint(posRaton);
 
 
 
        transform.position = Vector3.MoveTowards(transform.position, posMundo, velocidad * Time.deltaTime);
 
 
 
    }
 
 
}
 
}
 +
</syntaxhighlight>
  
</syntaxhighlight>
 
  
 +
: Este método está sobrecargado. Podemos hacer uso de una de sus variantes, en las que utiliza como como punto de referencia para moverse, no su posición local (donde se encuentra el GameObject) sino:
 +
:* Otro sistema de referencia de otro objeto (el transform de otro gameobject).
 +
:* El sistema de referencia del mundo 3D, haciendo uso de la constante: Space.World
  
:: Si ahora ejecutamos el juego, al mover el ratón el cubo irá hacia el mismo:
+
: ¿ Qué significa 'sistema de referencia' ? Pues que parte del punto indicado por el sistema de referencia para realizar la traslación del GameObject. Esto parece que no tiene sentido, ya que lo que hacemos es desplazar, por lo tanto, ¿ qué diferencia hay entre desplazar el GameObject 3 unidades hacia arriba tomando como referencia el propio GameObject (es decir, su posición actual) que tomar como referencia la coordenada (0,0,0) del mundo 3D ? Siguen siendo 3 unidades en los dos sistemas de referencia.
[[Imagen:Unity3d_mov_9.JPG|600px|center]]
+
: La diferencia <u>está en la rotación</u>. En el sistema de referencia del mundo no se tiene en cuenta la rotación, por lo tanto, desplazar 3 unidades hacia arriba conlleva modificar la posición del GameObject en el eje Y 3 unidades arriba.
  
 +
: Sin embargo, si utilizamos como sistema de referencia un objeto rotado, digamos 45 grados en el eje X, y lo movemos hacia arriba, se desplazará hacia arriba-atrás (eje Y-Z).
  
<br />
+
: Veamos un ejemplo.
=====Método Lerp=====
+
: Modificar la escena anterior y añadir una esfera a la posición (0,0,0).
 +
: Modificar el cubo de la escena, cambiarle el nombre por el de 'Cube-Self' (cuidado las mayúsculas-minúsculas) y posicionarlo en la posición (-2,0,0). Rotar el cubo 45 grados en el eje X.
 +
[[Imagen:Unity3d_mov_12.JPG|500px|center]]
  
* [https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html Método Lerp]: Es parecido al anterior, también mueve un GameObject de un origen a un destino, pero varía en el tercer parámetro, el cual indica el porcentaje de cuan cerca está del punto destino. Es decir, el tercer parámetro va de 0 a 1, siendo 0 el valor que indica que está en el origen y 1 el valor que indica que está en el destino. Si por ejemplo tuviéramos 0.5f querría decir que estaríamos en el punto intermedio entre el origen y destino.
+
: Ahora vamos a duplicar el gameobject 'Cube-Self' pulsando el botón derecho sobre él y escogiendo la opción '''Duplicate''':
:: Al igual que en el método anterior, no modifica los vectores que le pasamos como parámetro y devuelve un Vector3 con la nueva posición.
+
[[Imagen:Unity3d_mov_13.JPG|500px|center]]
:: Hace uso [https://es.wikipedia.org/wiki/Interpolaci%C3%B3n de la interpolación] para obtener los puntos intermedios desde el origen al destino.
+
: Cambiamos el nombre del GameObject por el de 'Cube-World' (cuidado las mayúsculas-minúsculas) y lo posiciones en la coordenada (2,0,0)
 +
[[Imagen:Unity3d_mov_14.JPG|500px|center]]
  
 +
: Modificamos el código del script 'UD_Movim_basico_1':
  
 +
<syntaxhighlight lang="java" line highlight="13-24">
 +
public class UD_Movim_basico_1 : MonoBehaviour {
  
:: Debemos de tener en cuenta que aquí no se puede pasar como tercer parámetro Velocidad*Time.deltaTime ya que este valor nunca será igual a uno y por tanto nunca llegaría al punto destino.
+
    public float velocidad = 2f;
  
:: Un uso que se le puede dar a Lerp (entre otros) es el de recorrer una determinada distancia en un tiempo concreto.
+
    // Use this for initialization
:: Podemos utilizar 'Time.deltaTime' como un reloj y poner como tercer parámetro: reloj / tiempo_total, siendo tiempo_total el número de segundo que queremos que tarde en alcanzar el punto de destino.
+
    void Start () {
 +
    }
  
 +
    // Update is called once per frame
 +
    void Update()
 +
    {
  
:: Modificamos el script anterior para que cubo se mueva a la posición del ratón cuando pulsamos el botón del mismo en un tiempo (por defecto 1 segundo). Vamos a hacer que el tiempo que tarda pueda ser modificado desde el inspector haciendo pública la propiedad:
+
        if (transform.position.x < 5 && transform.position.y < 5 && transform.position.z < 5)
 +
        {
 +
            if (gameObject.name.Equals("Cube_Self"))
 +
            {
 +
                transform.Translate(Vector3.up * velocidad * Time.deltaTime, Space.Self);
 +
            }
 +
            else if (gameObject.name.Equals("Cube_World"))
 +
            {
 +
                transform.Translate(Vector3.up * velocidad * Time.deltaTime, Space.World);
 +
            }
  
 +
        }
 +
    }
 +
}
 +
</syntaxhighlight>
  
::<syntaxhighlight lang="java" line highlight="7-10,24-36">
+
: Lo que hace el script es trasladar el 'Cube_Self' utilizando como sistema de referencia su propio transform(se podría omitir este tercer parámetro y tendría el mismo efecto) y trasladar el cubo 'Cube_World' utilizando como sistema de referencia el mundo 3D (Space.World).
public class UD_Movim_basico_1 : MonoBehaviour {
 
  
    public Vector3 posInicial = new Vector3(0, 0, 0);
+
: Al ejecutar el script podemos ver como el 'Cube_World' se traslada hacia arriba mientras que el 'Cube_Self' se traslada hacia arriba-atrás, debido a que está rotado:
    public Vector3 rotInicial = new Vector3(0, 0, 0);
+
<gallery caption="Translate con diferentes sistemas de referencia" widths="350" heights="300px" perrow="2">
    public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
+
Image:Unity3d_mov_15.JPG| En vista 'Game'.
 +
Image:Unity3d_mov_16.JPG| En vista 'Scene'.
 +
</gallery>
  
    public float tiempoTotal = 1f;    // Tiempo que tarda el cubo en llegar al destino.
 
    private float cronometro = 0f;
 
  
    private Vector3 posFinal = new Vector3(0,0,0);       // Posición de destino
+
'''NOTA IMPORTANTE:''' En este método es mejor <u>utilizar siempre los vectores Vector.up, Vector.forward,...</u>, es decir, los vectores de referencia deberían ser los del mundo, teniendo en cuenta que el segundo parámetro del método nos indica el sistema de referencia, si queremos que un objeto se mueva hacia adelante con respecto a sus ejes (estando rotado) deberemos de poner: transform.Translate('''Vector3.forward''' * velocidad * Time.deltaTime, '''Space.self''');
  
    // Use this for initialization
 
    void Start () {
 
        transform.position = posInicial;
 
        transform.Rotate(rotInicial);
 
        transform.localScale = escalaInicial;
 
  
    }
+
<br />
 
    // Update is called once per frame
 
    void Update () {
 
  
 +
=====Método MoveTowards=====
  
        if (Input.GetMouseButtonDown(0))    // Presionamos el botón del ratón
+
:Dentro de la [https://docs.unity3d.com/ScriptReference/Vector3.html clase Vector3].
        {
 
            cronometro = 0;
 
            posInicial = transform.position;
 
  
            Vector3 posRaton = Input.mousePosition;
 
            posRaton.z = 8;        // Plano donde se encuentra el cubo con respecto a la cámara. Adaptarlo a vuestro caso si la cámara se encuentra en otra posición.
 
            posFinal = Camera.main.ScreenToWorldPoint(posRaton);
 
  
        }
+
* [https://docs.unity3d.com/ScriptReference/Vector3.MoveTowards.html Método MoveTowards]: Mueve el GameObject desde la posición indicada por el parámetro 'current' hasta la posición indicada por el parámetro 'target' en la distancia indicada por el parámetro step.
  
        cronometro += Time.deltaTime;
+
::* Normalmente 'step' tiene como valor: Velocidad * Time.deltaTime
        transform.position = Vector3.Lerp(posInicial, posFinal, cronometro / tiempoTotal);
 
  
}
+
:: Debo aclarar que 'mover' no es la definición exacta, ya que no modifica los vectores que le pasamos como parámetros, sino que '''devuelve un Vector3''' con la nueva posición (la que va de origen a destino) movida un 'poquito' en cada frame.
}
+
:: Lo que debemos hacer es igualar ese vector a la posición de lo que queramos mover, de la forma: transform.position=Vector.moveTowards(origen, destino, velocidad*delta).
</syntaxhighlight>
 
  
[[Imagen:Unity3d_mov_10.JPG|600px|center]]
 
  
 +
:: Veamos un ejemplo, modificando el código del script 'UD_Movim_basico_1' para que el cubo se mueva hacia donde se encuentre el ratón en la pantalla:
 +
<u>Nota:</u> Recordar que [http://wiki.cifprodolfoucha.es/index.php?title=UNITY_C%C3%A1mara_en_perspectiva#Paso_de_coordenadas_desde_ScreenSpace_a_WorldSpace vimos anteriormente como proyectar un punto desde la pantalla al espacio 3D].
  
<br />
+
::<syntaxhighlight lang="java" line highlight="21-25">
=====Método SmoothDamp=====
+
public class UD_Movim_basico_1 : MonoBehaviour {
  
:Dentro de la [https://docs.unity3d.com/ScriptReference/Vector3.html clase Vector3].
+
    public Vector3 posInicial = new Vector3(0, 0, 0);
 +
    public Vector3 rotInicial = new Vector3(0, 0, 0);
 +
    public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
  
 +
    public float posFinalX = 5f;
 +
    public float velocidad = 1f;
  
* [https://docs.unity3d.com/ScriptReference/Vector3.SmoothDamp.html Método SmoothDamp]: Tiene el mismo efecto que el método anterior pero realiza el movimiento de una form más 'suave' ya que va aumentando la velocidad progresivamente y cuando se va acercando al destino va disminuyendo de velocidad. Se suele utilizar para seguir a una cámara en el juego.
+
    // Use this for initialization
 +
    void Start () {
 +
        transform.position = posInicial;
 +
        transform.Rotate(rotInicial);
 +
        transform.localScale = escalaInicial;
  
<u>Nota:</u> Tener en cuenta que el método está sobrecargado y existen varias formas de realizar la misma acción.
+
    }
  
:: Veamos un ejemplo modificando el código del script.
+
    // Update is called once per frame
:: Al pulsar el botón de ratón, el cubo se moverá a dicha posición en el tiempo indicado por la variable 'tiempoTotal'.
+
    void Update () {
:: Aquí no necesitamos cronómetro ya que el método hace los cálculos para llegar en el tiempo indicado, ajustando la velocidad.
 
:: Dispone de un parámetro '''currentVelocity'''. Es un parámetro que va a hacer referencia a un atributo de tipo Vector3 el cual va a modificar en cada frame, ajustando la velocidad. Podemos ver como dicha velocidad se modifica...
 
  
 +
        Vector3 posRaton = Input.mousePosition;
 +
        posRaton.z = 8;        // Plano donde se encuentra el cubo con respecto a la cámara. Adaptarlo a vuestro caso si la cámara se encuentra en otra posición.
 +
        Vector3 posMundo = Camera.main.ScreenToWorldPoint(posRaton);
  
::<syntaxhighlight lang="java" line highlight="10,24,33">
+
        transform.position = Vector3.MoveTowards(transform.position, posMundo, velocidad * Time.deltaTime);
public class UD_Movim_basico_1 : MonoBehaviour {
 
  
     public Vector3 posInicial = new Vector3(0, 0, 0);
+
     }
    public Vector3 rotInicial = new Vector3(0, 0, 0);
+
}
    public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 
  
    public float tiempoTotal = 1f;    // Tiempo que tarda el cubo en llegar al destino.
+
</syntaxhighlight>
    private Vector3 posFinal = new Vector3(0,0,0);      // Posición de destino
 
  
    private Vector3 velocidadActual = Vector3.zero;
 
  
    // Use this for initialization
+
:: Si ahora ejecutamos el juego, al mover el ratón el cubo irá hacia el mismo:
    void Start () {
+
[[Imagen:Unity3d_mov_9.JPG|600px|center]]
        transform.position = posInicial;
 
        transform.Rotate(rotInicial);
 
        transform.localScale = escalaInicial;
 
  
        posFinal = transform.position;
 
    }
 
 
    // Update is called once per frame
 
    void Update () {
 
  
        Debug.Log("Velocidad:" + velocidadActual);
+
<br />
        if (Input.GetMouseButtonDown(0))    // Presionamos el botón del ratón
+
=====Método Lerp=====
        {
 
            Vector3 posRaton = Input.mousePosition;
 
            posRaton.z = 8;        // Plano donde se encuentra el cubo con respecto a la cámara. Adaptarlo a vuestro caso si la cámara se encuentra en otra posición.
 
            posFinal = Camera.main.ScreenToWorldPoint(posRaton);
 
  
        }
+
* [https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html Método Lerp]: Es parecido al anterior, también mueve un GameObject de un origen a un destino, pero varía en el tercer parámetro, el cual indica el porcentaje de cuan cerca está del punto destino. Es decir, el tercer parámetro va de 0 a 1, siendo 0 el valor que indica que está en el origen y 1 el valor que indica que está en el destino. Si por ejemplo tuviéramos 0.5f querría decir que estaríamos en el punto intermedio entre el origen y destino.
 +
:: Al igual que en el método anterior, no modifica los vectores que le pasamos como parámetro y devuelve un Vector3 con la nueva posición.
 +
:: Hace uso [https://es.wikipedia.org/wiki/Interpolaci%C3%B3n de la interpolación] para obtener los puntos intermedios desde el origen al destino.
  
        transform.position = Vector3.SmoothDamp(transform.position, posFinal,ref velocidadActual, tiempoTotal);
 
  
}
 
}
 
</syntaxhighlight>
 
  
:* Línea 10: Vamos a guardar la velocidad que se va a modificar automáticamente al llamar al método 'SmoothDamp'.
+
:: Debemos de tener en cuenta que aquí no se puede pasar como tercer parámetro Velocidad*Time.deltaTime ya que este valor nunca será igual a uno y por tanto nunca llegaría al punto destino.
:* Línea 24: Por la consola vamos a poder ver como la velocidad se modifica.
 
:* Línea 33: Fijarse que el parámetro de la velocidad lleva la palabra reservada 'ref'.
 
  
 +
:: Un uso que se le puede dar a Lerp (entre otros) es el de recorrer una determinada distancia en un tiempo concreto.
 +
:: Podemos utilizar 'Time.deltaTime' como un reloj y poner como tercer parámetro: reloj / tiempo_total, siendo tiempo_total el número de segundo que queremos que tarde en alcanzar el punto de destino.
  
[[Imagen:Unity3d_mov_11.JPG|600px|center]]
 
  
 +
:: Modificamos el script anterior para que cubo se mueva a la posición del ratón cuando pulsamos el botón del mismo en un tiempo (por defecto 1 segundo). Vamos a hacer que el tiempo que tarda pueda ser modificado desde el inspector haciendo pública la propiedad:
  
 
  
<br />
+
::<syntaxhighlight lang="java" line highlight="7-10,24-36">
 
 
=====Método Translate=====
 
 
 
* [https://docs.unity3d.com/ScriptReference/Transform.Translate.html Método 'translate'] de la clase Transform: Modifica la posición de un gameobject desplazándola en los valores indicados. Es equivalente a poner: position = position+valor. Es decir, '''traslada''' el objeto desde su posición a una nueva añadiendo un nuevo valor. No hace como la propiedad position, que 'mueve' el gameobject a la posición indicada.
 
 
 
<u>Nota:</u> Tener en cuenta que el método está sobrecargado y existen varias formas de realizar la misma acción.
 
 
 
 
 
 
 
: Veamos un ejemplo sobre el script anterior:
 
 
 
<syntaxhighlight lang="java" line highlight="20">
 
 
public class UD_Movim_basico_1 : MonoBehaviour {
 
public class UD_Movim_basico_1 : MonoBehaviour {
  
Línea 914: Línea 965:
 
     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 
     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
  
     public float velocidad = 2f;
+
     public float tiempoTotal = 1f;   // Tiempo que tarda el cubo en llegar al destino.
 +
    private float cronometro = 0f;
 +
 
 +
    private Vector3 posFinal = new Vector3(0,0,0);      // Posición de destino
  
 
     // Use this for initialization
 
     // Use this for initialization
Línea 924: Línea 978:
 
     }
 
     }
 
 
// Update is called once per frame
+
    // Update is called once per frame
void Update () {
+
    void Update () {
 +
 
 +
 
 +
        if (Input.GetMouseButtonDown(0))    // Presionamos el botón del ratón
 +
        {
 +
            cronometro = 0;
 +
            posInicial = transform.position;
 +
 
 +
            Vector3 posRaton = Input.mousePosition;
 +
            posRaton.z = 8;        // Plano donde se encuentra el cubo con respecto a la cámara. Adaptarlo a vuestro caso si la cámara se encuentra en otra posición.
 +
            posFinal = Camera.main.ScreenToWorldPoint(posRaton);
 +
 
 +
        }
  
         transform.Translate(Vector3.up* velocidad * Time.deltaTime);
+
        cronometro += Time.deltaTime;
 +
         transform.position = Vector3.Lerp(posInicial, posFinal, cronometro / tiempoTotal);
  
 
}
 
}
 
}
 
}
</syntaxhighlight>
+
</syntaxhighlight>  
  
 +
[[Imagen:Unity3d_mov_10.JPG|600px|center]]
  
: Este método está sobrecargado. Podemos hacer uso de una de sus variantes, en las que utiliza como como punto de referencia para moverse, no su posición local (donde se encuentra el GameObject) sino:
 
:* Otro sistema de referencia de otro objeto (el transform de otro gameobject).
 
:* El sistema de referencia del mundo 3D, haciendo uso de la constante: Space.World
 
  
: ¿ Qué significa 'sistema de referencia' ? Pues que parte del punto indicado por el sistema de referencia para realizar la traslación del GameObject. Esto parece que no tiene sentido, ya que lo que hacemos es desplazar, por lo tanto, ¿ qué diferencia hay entre desplazar el GameObject 3 unidades hacia arriba tomando como referencia el propio GameObject (es decir, su posición actual) que tomar como referencia la coordenada (0,0,0) del mundo 3D ? Siguen siendo 3 unidades en los dos sistemas de referencia.
+
<br />
: La diferencia <u>está en la rotación</u>. En el sistema de referencia del mundo no se tiene en cuenta la rotación, por lo tanto, desplazar 3 unidades hacia arriba conlleva modificar la posición del GameObject en el eje Y 3 unidades arriba.
+
=====Método SmoothDamp=====
 +
 
 +
:Dentro de la [https://docs.unity3d.com/ScriptReference/Vector3.html clase Vector3].
 +
 
  
: Sin embargo, si utilizamos como sistema de referencia un objeto rotado, digamos 45 grados en el eje X, y lo movemos hacia arriba, se desplazará hacia arriba-atrás (eje Y-Z).
+
* [https://docs.unity3d.com/ScriptReference/Vector3.SmoothDamp.html Método SmoothDamp]: Tiene el mismo efecto que el método anterior pero realiza el movimiento de una form más 'suave' ya que va aumentando la velocidad progresivamente y cuando se va acercando al destino va disminuyendo de velocidad. Se suele utilizar para seguir a una cámara en el juego.
  
: Veamos un ejemplo.
+
<u>Nota:</u> Tener en cuenta que el método está sobrecargado y existen varias formas de realizar la misma acción.
: Modificar la escena anterior y añadir una esfera a la posición (0,0,0).
 
: Modificar el cubo de la escena, cambiarle el nombre por el de 'Cube-Self' (cuidado las mayúsculas-minúsculas) y posicionarlo en la posición (-2,0,0). Rotar el cubo 45 grados en el eje X.
 
[[Imagen:Unity3d_mov_12.JPG|500px|center]]
 
  
: Ahora vamos a duplicar el gameobject 'Cube-Self' pulsando el botón derecho sobre él y escogiendo la opción '''Duplicate''':
+
:: Veamos un ejemplo modificando el código del script.
[[Imagen:Unity3d_mov_13.JPG|500px|center]]
+
:: Al pulsar el botón de ratón, el cubo se moverá a dicha posición en el tiempo indicado por la variable 'tiempoTotal'.
: Cambiamos el nombre del GameObject por el de 'Cube-World' (cuidado las mayúsculas-minúsculas) y lo posiciones en la coordenada (2,0,0)
+
:: Aquí no necesitamos cronómetro ya que el método hace los cálculos para llegar en el tiempo indicado, ajustando la velocidad.
[[Imagen:Unity3d_mov_14.JPG|500px|center]]
+
:: Dispone de un parámetro '''currentVelocity'''. Es un parámetro que va a hacer referencia a un atributo de tipo Vector3 el cual va a modificar en cada frame, ajustando la velocidad. Podemos ver como dicha velocidad se modifica...
  
: Modificamos el código del script 'UD_Movim_basico_1':
 
  
<syntaxhighlight lang="java" line highlight="13-24">
+
::<syntaxhighlight lang="java" line highlight="10,24,33">
 
public class UD_Movim_basico_1 : MonoBehaviour {
 
public class UD_Movim_basico_1 : MonoBehaviour {
  
     public float velocidad = 2f;
+
    public Vector3 posInicial = new Vector3(0, 0, 0);
 
+
    public Vector3 rotInicial = new Vector3(0, 0, 0);
 +
    public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 +
 
 +
     public float tiempoTotal = 1f;    // Tiempo que tarda el cubo en llegar al destino.
 +
    private Vector3 posFinal = new Vector3(0,0,0);      // Posición de destino
 +
 
 +
    private Vector3 velocidadActual = Vector3.zero;
 +
 
 
     // Use this for initialization
 
     // Use this for initialization
 
     void Start () {
 
     void Start () {
 +
        transform.position = posInicial;
 +
        transform.Rotate(rotInicial);
 +
        transform.localScale = escalaInicial;
 +
 +
        posFinal = transform.position;
 
     }
 
     }
 
+
 
     // Update is called once per frame
 
     // Update is called once per frame
     void Update()
+
     void Update () {
    {
 
  
         if (transform.position.x < 5 && transform.position.y < 5 && transform.position.z < 5)
+
        Debug.Log("Velocidad:" + velocidadActual);
 +
         if (Input.GetMouseButtonDown(0))   // Presionamos el botón del ratón
 
         {
 
         {
             if (gameObject.name.Equals("Cube_Self"))
+
             Vector3 posRaton = Input.mousePosition;
             {
+
             posRaton.z = 8;        // Plano donde se encuentra el cubo con respecto a la cámara. Adaptarlo a vuestro caso si la cámara se encuentra en otra posición.
                transform.Translate(Vector3.up * velocidad * Time.deltaTime, Space.Self);
+
             posFinal = Camera.main.ScreenToWorldPoint(posRaton);
            }
 
             else if (gameObject.name.Equals("Cube_World"))
 
            {
 
                transform.Translate(Vector3.up * velocidad * Time.deltaTime, Space.World);
 
            }
 
  
 
         }
 
         }
    }
+
 
 +
        transform.position = Vector3.SmoothDamp(transform.position, posFinal,ref velocidadActual, tiempoTotal);
 +
 
 +
}
 
}
 
}
</syntaxhighlight>
+
</syntaxhighlight>  
  
: Lo que hace el script es trasladar el 'Cube_Self' utilizando como sistema de referencia su propio transform(se podría omitir este tercer parámetro y tendría el mismo efecto) y trasladar el cubo 'Cube_World' utilizando como sistema de referencia el mundo 3D (Space.World).
+
:* Línea 10: Vamos a guardar la velocidad que se va a modificar automáticamente al llamar al método 'SmoothDamp'.
 +
:* Línea 24: Por la consola vamos a poder ver como la velocidad se modifica.
 +
:* Línea 33: Fijarse que el parámetro de la velocidad lleva la palabra reservada 'ref'.
  
: Al ejecutar el script podemos ver como el 'Cube_World' se traslada hacia arriba mientras que el 'Cube_Self' se traslada hacia arriba-atrás, debido a que está rotado:
 
<gallery caption="Translate con diferentes sistemas de referencia" widths="350" heights="300px" perrow="2">
 
Image:Unity3d_mov_15.JPG| En vista 'Game'.
 
Image:Unity3d_mov_16.JPG| En vista 'Scene'.
 
</gallery>
 
  
 +
[[Imagen:Unity3d_mov_11.JPG|600px|center]]
  
  
<br />
+
  
 +
<br />
 
=====Distancia y dirección=====
 
=====Distancia y dirección=====
  
Línea 1157: Línea 1230:
 
:* transform.up (con minúscula) retorna el vector dirección del eje Y del gameobject <u>estando rotado</u>.
 
:* transform.up (con minúscula) retorna el vector dirección del eje Y del gameobject <u>estando rotado</u>.
 
:* Vector3.up retorna el vector (0,1,0) siempre.
 
:* Vector3.up retorna el vector (0,1,0) siempre.
 +
 +
 +
 +
<br />
 +
=====Método RotateTowards=====
 +
 +
:Dentro de la [https://docs.unity3d.com/ScriptReference/Vector3.html clase Vector3].
 +
 +
* [https://docs.unity3d.com/ScriptReference/Vector3.RotateTowards.html Método RotateTowards(Vector3 current, Vector3 target, float maxRadiansDelta, float maxMagnitudeDelta)]: Rota un GameObject en función de otro GameObject para seguirlo.
 +
:* Parámetro maxRadiansDelta es la velocidad de rotación, normalmente se pone Time.Delta*velocidad
 +
:* Parámetro (maxMagnitudeDelta) normalmente se pone de valor 0.0f.
 +
 +
* Es parecido al [https://docs.unity3d.com/ScriptReference/Vector3.MoveTowards.html método MoveTowards] con la diferencia que no nos movemos, sino que rotamos un vector.
 +
 +
: Como pasaba en métodos anteriores, realmente no rota nada, sino que dicho método devuelve un Vector3 con los datos necesarios para rotar un GameObject.
 +
: Debemos de tener en cuenta que este método <u>trabaja con direcciones</u> no con posiciones.
 +
: Es decir, el Vector3 current indica '''la dirección en la que nos encontramos''', y el Vector3 target '''indica la dirección a la que queremos ir'''.
 +
: Además solo trabaja con los ejes Z (vector3 forward) e Y (vector3 up) (es decir, solo rota esas direcciones).
 +
 +
 +
  
 
<br />
 
<br />
Línea 1163: Línea 1257:
  
 
* Para rotar, al igual que en el caso del movimiento, disponemos de varios métodos.
 
* Para rotar, al igual que en el caso del movimiento, disponemos de varios métodos.
 +
 +
 +
* '''Nota Importante:''' Debemos de tener cuidado con las animaciones (las veremos [http://wiki.cifprodolfoucha.es/index.php?title=Unity_Animaciones en este punto de la Wiki] que tenga asociado un modelo, ya que si estas tienen movimientos de rotación, sobre-escribirán las rotaciones que tengamos puesto a nivel de programación.
 +
: Para evitarlo podemos:
 +
:* Crear un GameObject vacío y hacer que el modelo sea 'hijo' de este GameObject, asociando nuestro script al GameObject vacío.
 +
:* Cambiar el orden de los componentes en la Inspector Window y hacer que el script se ejecute antes, por lo que primero realizará nuestra rotación y después la rotación de la animación.
 +
  
  
 
<br />
 
<br />
===Método rotate===
+
===Método Transform.Rotate===
  
* [https://docs.unity3d.com/ScriptReference/Transform.Rotate.html Método rotate] de las clase Transform.
+
* [https://docs.unity3d.com/ScriptReference/Transform.Rotate.html Método Rotate] de las clase Transform.
 
: Rota un GameObject en base a los ángulos suministrados en el método (ángulos de Euler).
 
: Rota un GameObject en base a los ángulos suministrados en el método (ángulos de Euler).
  
Línea 1185: Línea 1286:
  
 
: Veamos un ejemplo de código de rotación.
 
: Veamos un ejemplo de código de rotación.
: Crear un script de nombre '''UD_Movim_Rotac_1''' y asociarlo a un cubo de nombre '''Cube_Self''' posicionado en (-2,0,0) y con un ángulo de rotación de 45 grados en el eje Y. Crear otro cubo de nombre '''Cube_World''' posicionado en (2,0,0) y con un ángulo de rotación de 45 grados en el eje Y.
+
: Crear un script de nombre '''UD2_Movim_Rotac_1''' y asociarlo a un cubo de nombre '''Cube_Self''' posicionado en (-2,0,0) y con un ángulo de rotación de 45 grados en el eje Y. Crear otro cubo de nombre '''Cube_World''' posicionado en (2,0,0) y con un ángulo de rotación de 45 grados en el eje Y.
 
: La cámara mirando hacia ambos.
 
: La cámara mirando hacia ambos.
 
: Asociar a los dos cubos el script siguiente:
 
: Asociar a los dos cubos el script siguiente:
  
: Código de '''UD_Movim_Rotac_1''':
+
: Código de '''UD2_Movim_Rotac_1''':
 
<syntaxhighlight lang="java" line highlight="">
 
<syntaxhighlight lang="java" line highlight="">
public class UD_Movim_Rotac_1 : MonoBehaviour {
+
public class UD2_Movim_Rotac_1 : MonoBehaviour {
  
 
// Use this for initialization
 
// Use this for initialization
Línea 1226: Línea 1327:
  
 
* Al igual que hicimos en Translate, podemos rotar los objetos dentro del método Update para que giren de forma continuada y combinar diferentes operaciones a la vez.
 
* Al igual que hicimos en Translate, podemos rotar los objetos dentro del método Update para que giren de forma continuada y combinar diferentes operaciones a la vez.
: Crear un nuevo script de nombre '''UD_Movim_Rotac_2'''.
+
: Crear un nuevo script de nombre '''UD2_Movim_Rotac_2'''.
  
 
<syntaxhighlight lang="java" line highlight="">
 
<syntaxhighlight lang="java" line highlight="">
public class UD_Movim_Rotac_2 : MonoBehaviour {
+
public class UD2_Movim_Rotac_2 : MonoBehaviour {
 
     public float velocidadTraslac = 2f;
 
     public float velocidadTraslac = 2f;
 
     public float velocidadRotac = 30f; // 30 grados por segundo
 
     public float velocidadRotac = 30f; // 30 grados por segundo
Línea 1268: Línea 1369:
 
</gallery>
 
</gallery>
  
 +
 +
 +
* Este método está sobrecargardo y podemos emplearlo para rotar en algún eje concreto de la forma:
 +
<syntaxhighlight lang="java" line highlight="">
 +
    transform.Rotate(Vector3.up * Time.deltaTime*velocidad);
 +
</syntaxhighlight>
 +
: En el ejemplo estaríamos rotando el GameObject en el eje Y del mundo (sin tener en cuenta la rotación local del GameObject)
  
 
<br />
 
<br />
===Método RotateTowards===
 
  
:Dentro de la [https://docs.unity3d.com/ScriptReference/Vector3.html clase Vector3].
+
===Método Transform.RotateAround===
  
* [https://docs.unity3d.com/ScriptReference/Vector3.RotateTowards.html Método RotateTowards(Vector3 current, Vector3 target, float maxRadiansDelta, float maxMagnitudeDelta)]: Rota un GameObject en función de otro GameObject para seguirlo. Muy útil en juegos donde el objeto 3D necesite girar para perseguir a un enemigo o rotar el cuerpo para mirar a un objeto.
+
* [https://docs.unity3d.com/ScriptReference/Transform.RotateAround.html Método RotateAround] de la clase Transform: Modifica la transform de un objeto rotando dicho objeto sobre un eje alrededor de un punto determinado.
:* Parámetro maxRadiansDelta es la velocidad de rotación, normalmente se pone Time.Delta*velocidad
 
:* Parámetro (maxMagnitudeDelta) lleva el valor 0.0f.
 
  
: Como pasaba en métodos anteriores, realmente no rota nada, sino que dicho método devuelve un Vector3 con los datos necesarios para rotar un GameObject.
+
: Un ejemplo clásico sería el de un planeta rotando alrededor de un sol.
: Debemos de tener en cuenta que este método <u>trabaja con direcciones</u> no con posiciones.
 
: Además solo trabaja con los ejes Z (vector3 forward) e Y (vector3 up).
 
  
  
 +
* Veamos  un ejemplo:
 +
: Vamos a crear dos esferas de diferentes tamaños.
 +
:* Esfera 1: Cambiamos el nombre por 'Sol'. Posicionado en (0,0,0) y escala (2,2,2)
 +
:* Esfera 2: Cambiamos el nombre por 'Planeta'. Posicionado en (-15,0,0)
 +
:* Cámara: Posicionada en (0,6,-18) (podéis ponerla donde queráis, es una referencia). Rotada (23,0,0).
  
: Sino lo hemos hecho antes, hacer el [http://wiki.cifprodolfoucha.es/index.php?title=Unity_Recursos:_Asset_Store punto Recursos:Asset Store] de esta Wiki.
 
  
 +
: Tendréis algo parecido a esto:
 +
[[Imagen:Unity3d_mov_rot_12.JPG|500px|center]]
  
: El ejercicio que vamos a desarrollar va a consistir en rotar una nave con las teclas del cursor, y que en el centro de la pantalla (posición (0,0,0)) haya un cilindro que 'mire' constantemente a la nave que va rotando.
+
: Creamos un script de nombre '''UD_Movim_Rotac_5''':
 +
<syntaxhighlight lang="java" line highlight="13">
 +
public class UD_Movim_Rotac_5 : MonoBehaviour {
  
: Como una cápsula vamos a tener que rotarlo 90 grados en el eje-X para que 'apunte' a la nave, eje que vamos a utilizar para 'seguir' a la nave será el eje Y(Vector3.up) no el eje Z:
+
    private float angulo = 10f;
[[Imagen:Unity3d_mov_rot_6.JPG|400px|center]]
 
  
 +
    // Use this for initialization
 +
    void Start () {
 +
 +
    }
 +
 +
    // Update is called once per frame
 +
    void Update () {
  
* Preparación del ejercicio:
+
        transform.RotateAround(Vector3.zero, Vector3.up, angulo*Time.deltaTime);
 +
    }
 +
}
 +
</syntaxhighlight>
  
: Arrastra una de las [http://wiki.cifprodolfoucha.es/index.php?title=Unity_Recursos:_Asset_Store naves espaciales] al entorno 3D y sitúala en la posición (-12,0,-2).
 
: Crea un objeto 3D de tipo '''Capsule''' situado en la posición (0,0,0) y rotado 90 grados en el eje X.
 
: Sitúa la cámara en la posición (0,4,-20) y rotada 13 grados en el eje X.
 
: Deberéis tener algo parecido a esto:
 
[[Imagen:Unity3d_mov_rot_7.JPG|500px|center]]
 
  
 +
: Lo que hace el script es rotar 10' por segundo el gameobject alrededor del punto (0,0,0)
 +
: '''Asociar el script al GameObject planeta'''
  
: Ahora vamos a crear un script de nombre '''UD_Movim_Rotac_3A''' con el siguiente código:
+
: Si ahora ejecutáis el juego podéis observar como el planeta 'gira' alrededor del sol:
<syntaxhighlight lang="java" line highlight="">
+
[[Imagen:Unity3d_mov_rot_13.JPG|500px|center]]
public class UD_Movim_Rotac_3A : MonoBehaviour {
 
  
    public float velocidad = 2f;  // Velocidad hacia adelante
 
    public float velocidadRotac = 30f;  // 30 grados por segundo
 
  
// Use this for initialization
 
void Start () {
 
 
}
 
 
// Update is called once per frame
 
void Update () {
 
  
        transform.Translate(Vector3.forward * 5 * Time.deltaTime);  // Movemos siempre hacia adelante
+
<br />
        if (Input.GetKey(KeyCode.RightArrow))
 
        {
 
            transform.Rotate(Vector3.up * velocidadRotac * Time.deltaTime);
 
        }
 
        if (Input.GetKey(KeyCode.LeftArrow))
 
        {
 
            transform.Rotate(Vector3.down * velocidadRotac * Time.deltaTime);
 
        }
 
  
    }
+
===Método Quaternion.LookRotation===
}
 
</syntaxhighlight>
 
  
: '''Asociaremos el script a la nave espacial''' (arrastramos el script al GameObject a la ventana Inspector).
+
: Pertenece a la [https://docs.unity3d.com/ScriptReference/Quaternion.html clase Quaternion].
: Lo que hace básicamente es rotar la nave a derecha/izquierda (debemos rotar el eje Y) al pulsar las teclas de curso derecha-izquierda.
 
: Podéis comprobar que funcione.
 
  
 +
* [https://docs.unity3d.com/ScriptReference/Quaternion.LookRotation.html Método Quaternion LookRotation(Vector3 forward, Vector3 upwards = Vector3.up)]: Devuelve un objeto de la clase Quaternion con la información necesaria para rotar un GameObject.
 +
: Dicho método espera dos parámetros:
 +
:* Vector3 forward: Es el vector dirección de hacia donde queremos seguir.
 +
:* Vector3 upwards: Es el vector que indica la dirección del vector 'up'.  Normalmente pondremos Vector3.up
 +
:: El vector que falta (right), se obtiene de multiplicar los dos vectores anteriores.
 +
: La clase Quaternion debe guardar la información de rotación de los tres ejes.
  
: Creamos ahora un nuevo script de nombre '''UD_Movim_Rotac_3B''' con el siguiente código:
 
<syntaxhighlight lang="java" line highlight="2,3,13,15-16,18">
 
public class UD_Movim_Rotac_3B : MonoBehaviour {
 
    public Transform transformDestino;
 
    public float velocidadRotacion = 1f;  // 30 grados por segundo
 
  
// Use this for initialization
+
* Recordar que para rotar un GameObject haciendo uso de un objeto de la clase Quaternion, solamente tenemos que igualarlo a su propiedad 'rotation' de la forma:
void Start () {
+
:<syntaxhighlight lang="java" line enclose="div" highlight="" >
+
  gameObject.transform.rotation = quaternion;
}
+
</syntaxhighlight>
+
: Siendo quaternion un objeto de la clase Quaternion.
    // Update is called once per frame
 
    void Update () {
 
  
        Vector3 dir = transformDestino.position - transform.position;
 
  
        Vector3 dirDestino = Vector3.RotateTowards(transform.up, dir, velocidadRotacion*Time.deltaTime, 0.0f);
+
* Veamos un ejemplo práctico y conjunto con el método anterior (RotateAround).
        Debug.DrawRay(transform.position, dirDestino*100, Color.red);
+
: Vamos a realizar un ejercicio en el que tendremos un cañón que irá 'apuntando' a una esfera que se moverá alrededor del cañón.
  
        transform.rotation = Quaternion.LookRotation(transform.forward,dirDestino);
+
[[Imagen:Unity3d_mov_rotac_3.jpg|500px|center]]
 
    }
 
  
}
 
</syntaxhighlight>
 
  
:* Línea 2: Declaramos una propiedad de tipo Transform. Lo que va a representar esta propiedad es el GameObject al cual vamos a seguir (rotando, no moviendo, como si giramos la cabeza siguiendo un coche al pasar). Veremos que '''vamos a arrastrar''' la nave espacial a esta propiedad desde el inspector.
+
* En el diseño vamos a aprovechar una de las ventajas de utilizar la '''jerarquía''' para hacer que el cañón sea 'hijo' del cubo. De esa forma, al rotar el cubo también rotará el cañón.
:* Línea 3: Velocidad de rotación de la cápsula al girar.
 
:* Línea 12: Obtener el vector dirección (ya visto anteriormente).
 
:* Línea 13: Obtenemos el <u>vector dirección</u> rotado de acuerdo a la nueva dirección que queremos alcanzar.
 
::* El primer parámetro es el vector que queremos rotar, el cual puede ser transform.up o transform.forward. Utilizamos el vector 'transform.up' en vez del 'transform.forward' ya que la esfera está rotada 90 grados en X y por tanto el vector que 'sigue' a la nave es el vector Y.
 
::* El segundo parámetro es el vector dirección que queremos obtener.
 
::* El tercer parámetro es la velocidad de rotación.
 
::* El cuarto parámetro pondremos 0,0f (tiene que ver con las magnitudes (distancias) entre los dos vectores enviados como parámetros, cuando esta es diferente).
 
:* Línea 14: Esto es una ayuda que nos proporciona Unity. Lo que hace es dibujar un rayo (una línea) desde la posición actual de la esfera hasta 100 unidades de distancia en la dirección de destino, de color rojo. Podremos verlo en la pestaña '''Scene''' cuando estemos en ejecución.
 
:* Línea 16: Para asociar el nuevo vector dirección ya rotado debemos hacer uso del [https://docs.unity3d.com/ScriptReference/Quaternion.LookRotation.html método LookRotation] de la clase Quaternion.
 
  
 +
<gallery caption="Creando el cañón" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_mov_rotac_4.jpg| Antes de crear el cañón debemos de fijarnos en la posición del eje 'forward' ya que será este el que rote. Por tanto, cuando creemos el cañón (que es un cilindro) este debe ir en la misma dirección que dicho eje.
 +
Image:Unity3d_mov_rotac_5.jpg| Ahora creamos un cilindro sobre el GameObject Canhon (o lo creamos fuera y lo arrastramos sobre el GameObject). Fijarse que está rotado 90 grados en el eje X para 'acostarlo' y la escala está modificada para darle esa forma.
 +
</gallery>
  
: '''Asociamos el script al GameObject Capsule'''.
 
: '''Arrastramos desde el Unity, el GameObject de la nave espacial a la propiedad <u>Transform Destino</u> de la ventana Inspector:
 
[[Imagen:Unity3d_mov_rot_8.JPG|500px|center]]
 
  
: Ahora si ejecutamos el juego y mantenemos pulsada la tecla del cursor derecha-izquierda la nave girará y la cápsula girará con la nave.
+
* Ahora necesitamos crear un script que se asociará a la esfera que y la moverá alrededor del cañón.
 +
* Nombre script: '''UD2_Mov_Rotac_6A'''
 +
:<syntaxhighlight lang="java" line enclose="div" highlight="12" >
 +
using UnityEngine;
  
<gallery caption="Método RotateTowards" widths="350" heights="300px" perrow="2">
+
/**
Image:Unity3d_mov_rot_9.JPG| Ejecutamos el juego y rotamos la nave. Vemos como rota la cápsula.
+
* Rota la esfera alrededor del cañon
Image:Unity3d_mov_rot_10.JPG| Si en ejecución o con el juego en 'pause' vamos a la pestaña de '''Scene''' podemos ver como el rayo sale de la cápsula en la dirección de la nave.
+
*
</gallery>
+
*/
 +
public class UD2_Mov_Rotac_6A : MonoBehaviour
 +
{
 +
    [SerializeField]
 +
    private float angulo = 30;
 +
    [SerializeField]
 +
    private Transform posCanhon;
  
 +
   
 +
    // Start is called before the first frame update
 +
    void Start()
 +
    {
 +
       
 +
    }
  
 +
    // Update is called once per frame
 +
    void Update()
 +
    {
 +
        transform.RotateAround(posCanhon.position, Vector3.up, angulo * Time.deltaTime);
 +
    }
 +
}
  
<br />
+
</syntaxhighlight>
 +
:* Línea 12: El código es el mismo que el ejercicio anterior con la diferencia que estamos haciendo uso de una variable de tipo 'Transform' la cual va a guardar la posición del cañón ya que la idea es que la esfera gire alrededor de donde se encuentre el cañón.
  
===Método LookAt===
 
  
* [https://docs.unity3d.com/ScriptReference/Transform.LookAt.html Método LookAt] de la clase Transform.
+
<br />
: Es parecido al anterior pero está un poco más limitado, en el sentido que siempre va a 'mirar' utilizando el eje 'forward' (el eje de color azul en Unity).
+
<gallery caption="Haciendo uso de un script con referencias a Transform" widths="350" heights="300px" perrow="2">
: Normalmente se suele utilizar para que la cámara siga al protagonista.
+
Image:Unity3d_mov_rotac_6.jpg| Con la '''esfera seleccionada''' arrastramos el script a la ventana Inspector (o pulsamos el botón Add Component de la ventana Inspector). Al hacerlo vemos que aparece una propiedad de nombre 'Pos Canhon' que espera un dato de tipo Transform.
: Cualquier rotación que tengamos la va a obviar y lo único que hará será rotar el objeto de acuerdo al 'vector up' que le digamos (por defecto es el eje Y).
+
Image:Unity3d_mov_rotac_7.jpg| Ahora, con la '''esfera seleccionada''', arrastramos el GameObject 'Canhon' a la propiedad 'Pos Canhon' (también lo podemos hacer seleccionado el GameObject pulsando la rueda dentada que está al lado de la propiedad). Al hacerlo, dentro del script, la variable posCanhon tendrá de valor el 'transform' del GameObject 'Canhon'.
 +
</gallery>
  
: El 'vector up' es un vector dirección que normalmente se asocia a cámaras.
 
<center>
 
: [Imagen:http://macedoniamagazine.frodrig.com/jpg/fig3g7.jpg]
 
</center>
 
<center><small>Imagen obtenida de [http://macedoniamagazine.frodrig.com/opengl7.htm macedoniamagazine.frodrig.com]</small></center>
 
  
 +
* Ahora creamos el script que hará que el cañón rote, persiguiendo a la esfera.
 +
* Nombre script: '''UD2_Mov_Rotac_6B'''
 +
:<syntaxhighlight lang="java" line enclose="div" highlight="10,21-23" >
 +
using UnityEngine;
  
: Este vector sirve para 'rotar' la cámara (o un objeto en este caso). Imaginar que claváis un palo en una cámara y que la atraviesa de arriba-abajo (vector up). Ahora coger la cámara y moverla a izquierda-derecha hasta que apunte a objeto destino. Si el objeto destino no está en la línea de la cámara el palo sube-baja hasta situarlo en la misma.
+
/** Ejemplo de script que muestra un ejemplo de uso del
 +
*  método Quaternion.LookRotation
 +
 +
*/
 +
public class UD2_Mov_Rotac_6B : MonoBehaviour
 +
{
 +
    [SerializeField]
 +
    private Transform posicionRotar;
  
: En nuestro caso, el palo que queremos mover será el que atraviesa la cápsula de arriba-abajo, pero que al estar rotada es el vector Up, en nuestro caso es Vector3.forward.
+
    // Start is called before the first frame update
: Lo que va a pasar es que el objeto rotará pero siempre apuntará con el vector forward a la nave y por eso lo que va a apuntar será la parte media de la cápsula, es decir, la cápsula rotará desde el punto medio de la misma que es de donde parte el vector forward.
+
    void Start()
 +
    {
 +
       
 +
    }
  
: Veamos un ejemplo...
 
: Modificamos el script anterior:'''UD_Movim_Rotac_3B'''
 
<syntaxhighlight lang="java" line highlight="15">
 
public class UD_Movim_Rotac_3B : MonoBehaviour {
 
 
    public Transform transformDestino;
 
    public float velocidadRotacion = 1f;  // 30 grados por segundo
 
 
// Use this for initialization
 
void Start () {
 
 
}
 
 
 
     // Update is called once per frame
 
     // Update is called once per frame
     void Update () {
+
     void Update()
 
+
    {
 
+
        Vector3 dir = posicionRotar.position - transform.position;    // No hace falta normalizar el vector3 ya que no lo vamos a usar para movernos
         transform.LookAt(transformDestino,Vector3.forward);
+
         Quaternion quaternion = Quaternion.LookRotation(dir, Vector3.up);
 +
        transform.rotation = quaternion;
  
 +
       
 
     }
 
     }
 +
}
  
}
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
: Como vemos el código es mucho más sencillo.
+
:* Línea 10: Este script se va a asociar al cañón, el cual queremos que 'rote' persiguiendo a la esfera. Por tanto necesitamos saber cual es la posición de la esfera.
: El segundo parámetro indica cual es el vector '''up'''.  
+
:: Definimos por tanto una propiedad que va a guardar esa información.
 +
:* Línea 21: Obtenemos el vector dirección que apunta a la esfera.
 +
:* Línea 22: Guardamos en el objeto quaternion la información de rotación.
 +
:* Línea 23: Rotamos el cañón.
  
: Si ejecutamos el script podemos ver como la cápsula sigue a la nave en su eje Z:
 
[[Imagen:Unity3d_mov_rot_11.JPG|500px|center]]
 
  
  
: Recordar que 'mirar hacia algo' no implica hacer lo mismo que el objeto al que se está mirando. Así, el objeto podría rotar 360 grados y nosotros quedaríamos igual mirando hacia el mismo.
+
<br />
 +
<gallery caption="Haciendo uso de un script con referencias a Transform" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_mov_rotac_8.jpg| Con el '''cañón seleccionado''' arrastramos el script a la ventana Inspector (o pulsamos el botón Add Component de la ventana Inspector). Al hacerlo vemos que aparece una propiedad de nombre 'Posicion Rotar' que espera un dato de tipo Transform.
 +
Image:Unity3d_mov_rotac_9.jpg| Ahora, con el '''cañón seleccionado''', arrastramos el GameObject 'Sphere' a la propiedad 'Posicion Rotar' (también lo podemos hacer seleccionado el GameObject pulsando la rueda dentada que está al lado de la propiedad). Al hacerlo, dentro del script, la variable posicionRotar tendrá de valor el 'transform' del GameObject 'Sphere'.
 +
</gallery>
 +
 
 +
 
 +
* Si ahora ejecutáis el juego podéis ver como la esfera rota alrededor del cañón y el cañón apunta a la esfera.
  
  
Línea 1445: Línea 1554:
 
<br />
 
<br />
  
===Método RotateAround===
+
===Método Quaternion.Lerp===
 +
 
 +
: Pertenece a la [https://docs.unity3d.com/ScriptReference/Quaternion.html clase Quaternion].
  
* [https://docs.unity3d.com/ScriptReference/Transform.RotateAround.html Método RotateAround] de la clase Transform: Modifica la transform de un objeto rotando dicho objeto sobre un eje alrededor de un punto determinado.
+
* El [https://docs.unity3d.com/ScriptReference/Quaternion.Lerp.html método Quaternion Lerp(Quaternion a, Quaternion b, float t)] devuelve el objeto Quaternion que representa la rotación desde 'a' hasta 'b' a una velocidad 't'.
 +
: Esto permite que la rotación del objeto vaya 'retrasada' con respecto a la rotación del objeto que estamos siguiendo.
 +
: Es lo que sucede en la realidad. Cuando un tanque gira para perseguir a un enemigo no lo hace inmediatamente, sino que gira a una velocidad determinada.
  
: Un ejemplo clásico sería el de un planeta rotando alrededor de un sol.
+
: Si lo aplicamos al ejemplo anterior:
  
 +
* Modificamos el script '''UD2_Mov_Rotac_6B'''
 +
:<syntaxhighlight lang="java" line enclose="div" highlight="13,20" >
 +
using UnityEngine;
  
* Veamos  un ejemplo:
+
/** Ejemplo de script que muestra un ejemplo de uso del
: Vamos a crear dos esferas de diferentes tamaños.
+
*   método Quaternion.LookRotation
:* Esfera 1: Cambiamos el nombre por 'Sol'. Posicionado en (0,0,0) y escala (2,2,2)
+
*
:* Esfera 2: Cambiamos el nombre por 'Planete'. Posicionado en (-15,0,0)
+
*/
:* Cámara: Posicionada en (0,6,-18) (podéis ponerla donde queráis, es una referencia). Rotada (23,0,0).
+
public class UD2_Mov_Rotac_6B : MonoBehaviour
 +
{
 +
    [SerializeField]
 +
    private Transform posicionRotar;
  
 +
    [SerializeField]
 +
    private float velocidad;
  
: Tendréis algo parecido a esto:
+
    // Update is called once per frame
[[Imagen:Unity3d_mov_rot_12.JPG|500px|center]]
+
    void Update()
 
+
    {
: Creamos un script de nombre '''UD_Movim_Rotac_5''':
+
        Vector3 dir = posicionRotar.position - transform.position;    // No hace falta normalizar el vector3 ya que no lo vamos a usar para movernos
<syntaxhighlight lang="java" line highlight="13">
+
        Quaternion quaternion = Quaternion.LookRotation(dir, Vector3.up);
public class UD_Movim_Rotac_5 : MonoBehaviour {
+
        transform.rotation = Quaternion.Lerp(transform.rotation,quaternion,velocidad*Time.deltaTime);
 
 
    private float angulo = 10f;
 
  
    // Use this for initialization
 
    void Start () {
 
 
    }
 
 
    // Update is called once per frame
 
    void Update () {
 
  
        transform.RotateAround(Vector3.zero, Vector3.up, angulo*Time.deltaTime);
 
 
     }
 
     }
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
:* Línea 13: Definimos la velocidad a la que el cañón va a rotar siguiendo la esfera.
 +
:* Línea 20: Creamos un objeto Quaternion con la función Lerp, la cual irá rotando desde la posición del cañón a la posición rotada que apunta a la esfera.
  
  
: Lo que hace el script es rotar 10' por segundo el gameobject alrededor del punto (0,0,0)
+
[[Imagen:Unity3d_mov_rotac_10.jpg|600px|center]]
: '''Asociar el script al GameObject planeta'''
 
  
: Si ahora ejecutáis el juego podéis observar como el planeta 'gira' alrededor del sol:
+
===Método LookAt===
[[Imagen:Unity3d_mov_rot_13.JPG|500px|center]]
+
 
 +
* [https://docs.unity3d.com/ScriptReference/Transform.LookAt.html Método LookAt] de la clase Transform.
 +
: Rota un GameObject pero siempre va a rotar el eje 'forward' (el eje de color azul en Unity). No podemos indicar que rote otro eje. El eje 'forward' siempre va a mirar al enemigo que queramos perseguir.
 +
: Normalmente se suele utilizar para que la cámara mire al protagonista cuando lo va siguiendo.
 +
: Cualquier rotación que tengamos la va a obviar y lo único que hará será rotar el objeto de acuerdo al 'vector up' que le digamos (por defecto es el eje Y).
 +
 
 +
: El 'vector up' es un vector dirección que normalmente se asocia a cámaras.
 +
<center>
 +
: [Imagen:http://macedoniamagazine.frodrig.com/jpg/fig3g7.jpg]
 +
</center>
 +
<center><small>Imagen obtenida de [http://macedoniamagazine.frodrig.com/opengl7.htm macedoniamagazine.frodrig.com]</small></center>
 +
 
 +
 
 +
: Este vector sirve para 'rotar' la cámara (o un objeto en este caso). Imaginar que claváis un palo en una cámara y que la atraviesa de arriba-abajo (vector up). Ahora coger la cámara y moverla a izquierda-derecha hasta que apunte a objeto destino. Si el objeto destino no está en la línea de la cámara el palo sube-baja hasta situarlo en la misma. En Unity, el Vector up siempre es perpendicular en 90 grados al vector Forward, por lo que irá rotando el eje Y para que esté siempre a esa distancia.
 +
 
 +
 
 +
* Vamos a construir la siguiente escena (después podemos aplicar lo aprendido en el punto anterior):
 +
[[Imagen:Unity3d_mov_rotac_11.jpg|700px|center]]
 +
: Nos vamos a la 'Asset Store' y descargamos un tanque gratuito (free).
 +
: Arrastramos dos tanques de la carpeta Models del tanque importado a la escena (puede ser necesario cambiar la escala del modelo, recuerda que es un parámetro que se cambia en el modelo de nombre '''Scale Factor).
 +
: Les cambiamos el nombre a los tanques y los colocamos como en la imagen.
 +
: Creamos un plano para dar la sensación de que haya un suelo.
 +
 
 +
: Creamos el script '''UD_Movim_Rotac_7'''
 +
<syntaxhighlight lang="java" line highlight="9,15" enclose="div">
 +
using UnityEngine;
 +
 
 +
/**
 +
* Ejemplo de uso del método Transform.LookAt
 +
*/
 +
public class UD2_Mov_Rotac_7 : MonoBehaviour {
 +
 
 +
    [SerializeField]
 +
    private Transform tanqueObjetivo;
 +
 
 +
 +
// Update is called once per frame
 +
void Update () {
 +
 
 +
        transform.LookAt(tanqueObjetivo);
 +
 
 +
}
 +
}
 +
</syntaxhighlight>
 +
: Como vemos el código es mucho más sencillo.
 +
 
 +
:* Línea 9: Necesitamos saber cual es la posición del tanque que queremos perseguir, por lo que creamos un objeto de la clase Transform que sea público.
 +
:* Línea 15: Vamos a hacer que la torreta del tanque mire siempre hacia el tanque objetivo.
 +
 
 +
 
 +
 
 +
* Ahora colocamos el script en la torreta del tanque atacante:
 +
 
 +
<gallery caption="Rotando con LookAt" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_mov_rotac_12.jpg| Arrastramos el script creado a la torreta del tanque atacante.
 +
Image:Unity3d_mov_rotac_13.jpg| Recordar que el script espera recibir como parámetro la posición del tanque. Por lo tanto lo arrastramos a la propiedad transform.
 +
</gallery>
 +
 
 +
 
 +
* Si ejecutáis el juego puede ser que os llevéis una desagradable sorpresa :(
 +
[[Imagen:Unity3d_mov_rotac_14.jpg|600px|center]]
 +
: La torreta está rotada !!!!
 +
 
 +
 
 +
: Esto es debido a que el método LookAt siempre va a 'mirar' con el eje forward (el de color azul) y resulta que el modelo importado, sus ejes por defecto, están rotados y no se corresponden con los ejes x-y-z de Unity:
 +
[[Imagen:Unity3d_mov_rotac_15.jpg|600px|center]]
 +
 
 +
: Si tenéis suerte y el modelo importado está correcto, no tendréis que hacer nada, pero de todas formas leeros lo siguiente por si os paso con otro modelo...
 +
 
 +
* Podemos hacer varias cosas para solucionarlo:
 +
:* Editar el dibujo 3D y cambiar su rotación para hacer que la posición se corresponda con la posición de los ejes de Unity.
 +
:* Hacer uso de un GameObject vacío. Hacer la torreta 'hijo' de este GameObject y llevar el script de rotación al GameObject vacío.
 +
:: Vamos a hacer esto segundo.
 +
 
 +
 
 +
<gallery caption="Rotando un GameObject vacío con los ejes correctos" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_mov_rotac_16.jpg| Creamos un GameObject vacío sobre el GameObject '''TanqueAtacante'''. Le cambiamos el nombre por TorretaSinRotar
 +
Image:Unity3d_mov_rotac_17.jpg| Ahora '''arrastramos''' el GameObject '''tower''' al GameObject vacío. Si aparece un mensaje indicando que se 'rompe' la relación con el Prefab presionar continuar.
 +
Image:Unity3d_mov_rotac_21.jpg| Como lo que vamos a rotar va a ser el eje forward (el de color azul) del GameObject vacío tenemos que hacer que se corresponda con la orientación de la torreta. Por ello lo rotamos 90 grados en su eje-Y.
 +
Image:Unity3d_mov_rotac_18.jpg| Después <u>arrastramos el script</u> que hemos creado '''al GameObject vacío, de nombre TorretaSinRotar'''. Cuando el script rote el GameObject, también rotará todos los GameObject hijos, y por tanto también la torreta.
 +
Image:Unity3d_mov_rotac_19.jpg| Con el GameObject 'TorretaSinRotar' seleccionado, arrastramos el TanqueObjetivo al parámetro Transform del script.
 +
Image:Unity3d_mov_rotac_20.jpg| Recordar que debemos de eliminar el script del GameObject 'Tower' ya que era el que no funcionaba.
 +
</gallery>
 +
 
 +
 
 +
* Si ejecutáis el juego ahora sí que funciona :)
 +
 
 +
[[Imagen:Unity3d_mov_rotac_22.jpg|600px|center]]
 +
 
 +
 
 +
 
 +
* <u>Nota:</u> Recordar que 'mirar hacia algo' no implica hacer lo mismo que el objeto al que se está mirando. Así, el objeto podría rotar 360 grados y nosotros quedaríamos igual mirando hacia el mismo.
 +
* Como <u>ejercicio propuesto</u> podríais intentar hacer rotar el tanque en vez de la torreta.
 +
: Pista: Podéis rotar los componentes del tanque (torreta y cuerpo) para que coincida con la orientación de los ejes de Unity.
 +
 
 +
<br />
 +
 
 +
==Programando la Escala==
 +
 
 +
* Para modificar la escala debemos de hacer uso de la propiedad 'localscale' del transform asociado como ya vimos al principio de esta entrada.
 +
: Para modificar dicha escala podemos hacer uso del vector3: [https://docs.unity3d.com/ScriptReference/Vector3-one.html Vector3.one] que representa el vector (1,1,1).
 +
 
 +
: Así podríamos poner: transform.localscale = Vector3.one * 5;  (estaríamos asignando una escala de 5 unidades al GameObject).
 +
 
 +
 
 +
<br />
 +
===Método Scale===
 +
 
 +
* [https://docs.unity3d.com/ScriptReference/Vector3.Scale.html Método Scale] de la clase Vector3: Multiplica dos vectores.
 +
: Vamos a aplicarlo a los GameObject.
 +
 
 +
 
 +
* Creamos una esfera en al punto (0,0,0) con la cámara mirando hacia ella a cierta distancia.
 +
: Creamos el script de nombre '''UD_Movim_Escala_1'''.
 +
<syntaxhighlight lang="java" line highlight="3,4,9">
 +
public class UD_Movim_Escala_1 : MonoBehaviour {
 +
 
 +
    public Vector3 nuevaEscala = new Vector3(2, 2, 2);  // Multiplica la escala por estos valores
 +
    private Vector3 reducirEscala;
 +
 
 +
// Use this for initialization
 +
    void Start () {
 +
 
 +
        reducirEscala = new Vector3(1/nuevaEscala.x,1/nuevaEscala.y,1/nuevaEscala.z);
 +
    }
 +
 +
    // Update is called once per frame
 +
    void Update () {
 +
 
 +
        if (Input.GetKeyDown(KeyCode.UpArrow))
 +
        {
 +
            transform.localScale = Vector3.Scale(transform.localScale, nuevaEscala);
 +
 
 +
        }
 +
        if (Input.GetKeyDown(KeyCode.DownArrow))
 +
        {
 +
            transform.localScale = Vector3.Scale(transform.localScale, reducirEscala);
 +
 
 +
        }
 +
 
 +
    }
 +
}
 +
 
 +
</syntaxhighlight>
 +
 
 +
: Lo que hace el script es definir dos vectores:
 +
:* Uno public de nombre 'nuevaEscala' (línea 3) que multiplicará la escala local del GameObject por estos valores en caso de querer aumentar la escala.
 +
:* Una private de nombre 'reducirEscala' (línea 4 y línea 9) que es un Vector3 invertido al anterior, para el caso de querer reducir la escala.
 +
: Después en función de si presionamos la tecla 'cursor arriba' o 'cursor abajo' modifica la escala del GameObject asociado.
 +
 
 +
 
 +
* '''Asocia el script anterior a la esfera creada'''.
 +
: Ejecuta el juego y comprueba como cambia la escala:
 +
 
 +
<gallery caption="Cambiando la escala" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_mov_escala_1.JPG| Ejecutamos el juego. El script está asociado a la esfera.
 +
Image:Unity3d_mov_escala_2.JPG| Nueva escala después de pulsar dos veces la flecha arriba.
 +
</gallery>
 +
 
 +
 
 +
<u>Nota:</u> Recordar que cuando vimos el punto del [http://wiki.cifprodolfoucha.es/index.php?title=UNITY_Operaciones_sobre_objetos_3D#Optimizando_el_uso_de_la_memoria 'rendimiento'] indicamos que siempre que se pueda se deben de quitar instrucciones del método Update. Por eso la instanciación del Vector3 'reducirEscala' está en el método Start().
  
  
  
 
<br />
 
<br />
==Programando la Escala==
+
=Constraint=
  
* Para modificar la escala debemos de hacer uso de la propiedad 'localscale' del transform asociado como ya vimos al principio de esta entrada.
+
* Las constraint son componentes que se añaden a un GameObject y sirven para 'enlazar' las propiedades de posición, escala y rotación de un GameObject con otros, de tal forma que la modificación en los valores de cualquiera de las propiedades anteriores, modifica las mismas propiedades en los GameObjects enlazados.
: Para modificar dicha escala podemos hacer uso del vector3: [https://docs.unity3d.com/ScriptReference/Vector3-one.html Vector3.one] que representa el vector (1,1,1).
 
  
: Así podríamos poner: transform.localscale = Vector3.one * 5;  (estaríamos asignando una escala de 5 unidades al GameObject).
+
* Más información: https://docs.unity3d.com/Manual/Constraints.html
  
  
 
<br />
 
<br />
===Método Scale===
+
=Métodos útiles=
 
 
* [https://docs.unity3d.com/ScriptReference/Vector3.Scale.html Método Scale] de la clase Vector3: Multiplica dos vectores.
 
: Vamos a aplicarlo a los GameObject.
 
 
 
 
 
* Creamos una esfera en al punto (0,0,0) con la cámara mirando hacia ella a cierta distancia.
 
: Creamos el script de nombre '''UD_Movim_Escala_1'''.
 
<syntaxhighlight lang="java" line highlight="3,4,9">
 
public class UD_Movim_Escala_1 : MonoBehaviour {
 
 
 
    public Vector3 nuevaEscala = new Vector3(2, 2, 2);  // Multiplica la escala por estos valores
 
    private Vector3 reducirEscala;
 
 
 
// Use this for initialization
 
    void Start () {
 
 
 
        reducirEscala = new Vector3(1/nuevaEscala.x,1/nuevaEscala.y,1/nuevaEscala.z);
 
    }
 
 
    // Update is called once per frame
 
    void Update () {
 
  
        if (Input.GetKeyDown(KeyCode.UpArrow))
+
:* [https://docs.unity3d.com/ScriptReference/Vector3.Cross.html Vector3 Vector3.Cross(Vector3 lhs, Vector3 rhs)]: Devuelve el vector 'perpendicular' a los dos vectores enviados como parámetros. Sigue la [http://wiki.cifprodolfoucha.es/index.php?title=UNITY_Figuras_3D#Ejes_de_coordenadas_en_UNITY_.2F_Blender regla de la mano derecha] para determinar la dirección del vector resultante.
        {
+
:* [https://docs.unity3d.com/ScriptReference/Vector3.Angle.html Vector3 Vector3.Angle(Vector3 from, Vector3 to)]: Devuelve la diferencia en grados entre el vector 'from' y el vector 'to'.
            transform.localScale = Vector3.Scale(transform.localScale, nuevaEscala);
 
  
        }
 
        if (Input.GetKeyDown(KeyCode.DownArrow))
 
        {
 
            transform.localScale = Vector3.Scale(transform.localScale, reducirEscala);
 
 
        }
 
 
    }
 
}
 
  
</syntaxhighlight>
+
:* [https://docs.unity3d.com/ScriptReference/Mathf.html Clase Mathf]: Clase con múltiples métodos útiles:
 +
::* [https://docs.unity3d.com/ScriptReference/Mathf.Clamp.html float Mathf.Clamp(float value, float min, float max)]: Método muy útil para cuando queremos 'limitar' el valor de una variable entre dos límites.
 +
::* [https://docs.unity3d.com/ScriptReference/Mathf.Lerp.html float Lerp(float a, float b, float t)]: Vimos su significado y aplicación [http://wiki.cifprodolfoucha.es/index.php?title=UNITY_Operaciones_sobre_objetos_3D#M.C3.A9todo_Lerp en esta misma sección], pero esta vez es aplicado a un número, no a un vector3.
 +
::: Basado en esta operación tenemos varias variantes:
 +
:::* [https://docs.unity3d.com/ScriptReference/Mathf.LerpAngle.html LerpAngle]: Hace lo mismo que el anterior pero sus 'valores' son interpretados como ángulos entre 0 y 360 grados.
 +
:::* [https://docs.unity3d.com/ScriptReference/Mathf.InverseLerp.html InverseLerp]: Devuelve el 'porcentaje' entre los valores [0-1] en que se encuentra un punto con respecto a dos dados, siendo el valor 0 el punto inicial, y el valor 1 el punto final.
 +
::* [https://docs.unity3d.com/ScriptReference/Mathf.MoveTowards.html  float MoveTowards(float current, float target, float maxDelta)]: El mismo significado que el visto [http://wiki.cifprodolfoucha.es/index.php?title=UNITY_Operaciones_sobre_objetos_3D#M.C3.A9todo_MoveTowards en esta sección], pero a aplicado a un número y no a un Vector3.
  
: Lo que hace el script es definir dos vectores:
 
:* Uno public de nombre 'nuevaEscala' (línea 3) que multiplicará la escala local del GameObject por estos valores en caso de querer aumentar la escala.
 
:* Una private de nombre 'reducirEscala' (línea 4 y línea 9) que es un Vector3 invertido al anterior, para el caso de querer reducir la escala.
 
: Después en función de si presionamos la tecla 'cursor arriba' o 'cursor abajo' modifica la escala del GameObject asociado.
 
  
  
* '''Asocia el script anterior a la esfera creada'''.
 
: Ejecuta el juego y comprueba como cambia la escala:
 
  
<gallery caption="Cambiando la escala" widths="350" heights="300px" perrow="2">
 
Image:Unity3d_mov_escala_1.JPG| Ejecutamos el juego. El script está asociado a la esfera.
 
Image:Unity3d_mov_escala_2.JPG| Nueva escala después de pulsar dos veces la flecha arriba.
 
</gallery>
 
  
  
<u>Nota:</u> Recordar que cuando vimos el punto del [http://wiki.cifprodolfoucha.es/index.php?title=UNITY_Operaciones_sobre_objetos_3D#Optimizando_el_uso_de_la_memoria 'rendimiento'] indicamos que siempre que se pueda se deben de quitar instrucciones del método Update. Por eso la instanciación del Vector3 'reducirEscala' está en el método Start().
 
  
  
 
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] -- (2018).
 
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] -- (2018).

Revisión del 08:02 4 jun 2019

Introducción

  • A todo objeto 3D se le pueden aplicar tres operaciones:
  • Escalado: Hacerlo más grande. Podemos hacerlo más grande en todos los ejes (x,y,z) o solo en alguno de ellos, provocando deformaciones.
  • Rotación: Podemos rotarlo en cualquiera de los tres ejes (x,y,z)
  • Traslación: Podemos trasladar el objeto en cualquiera de los tres ejes (x,y,z). Esto es, moverlo.


  • Como comentamos anteriormente, toda figura 3D se dibuja en el punto (0,0,0) y después se le aplican unas modificaciones para visualizarlo en la posición y forma correctas.
Esto es posible gracias a una serie de operaciones matemáticas con matrices.
Cada uno de los objetos 3D tiene asociado una matriz de modelado (una matriz matemática de 4x4).
Inicialmente, cuando dibujamos un objeto 3D se dibuja en el punto 0,0,0 y sin rotación ya que su matriz de modelado es la matriz identidad. Dicha matriz tiene unos en una de las diagonales.
Cuando indicamos que el objeto se tiene que desplazar, rotar o escalar, estamos modificando su matriz de modelado (para nosotros es transparente) y una vez modificada, se aplica dicha matriz sobre cada uno de los puntos del objeto 3D y por eso se dibuja en la posición correcta, con la escala y con la rotación indicada.



LIBGDX UD4 Animacion 4.jpg



  • En Unity3D accedemos a esta matriz de forma gráfica en la ventana de Inspector, bajo la sección Transform:
Unity3d operaciones 1.jpg



  • En Unity3D accedemos a esta matriz en los scripts teniendo una referencia a un gameobject y sobre él, accediendo a la propiedad transform.
Si queremos acceder al transform del gameobject asociado al script (el script se encuentra gráficamente asociado a un gameobject) podemos poner: transform (en minúscula).
Unity3d operaciones 1B.jpg


Nota: En el ejemplo se está creando una variable de tipo Transform para guardar el transform local del GameObject asociado al script. Esto no es necesario ya que siempre vamos a poder acceder al mismo escribiendo transform con minúsculas.
Crear una variable de tipo Transform es necesario cuando queremos referenciar el transform de otros GameObhects diferentes.




Escalado

  • En la siguiente imagen podéis observar un cubo que mide 1x1x1 unidades (fijaros en la cuadrícula).
Unity3d escala 1.jpg


  • Al pulsar sobre el cubo aparece en el Window Inspector las propiedades del gameobject seleccionado (en este caso el cubo).
Unity3d escala 2.jpg


  • Ahora podemos modificar la escala. Lo podemos hacer de varias formas:
  • Tecleando la nueva escala en la caja de texto correspondiente.
  • Poniendo el ratón en la ventana Inspector, encima del eje que queramos cambiar la escala y manteniendo presionado el botón izquierdo mover a derecha o izquierda.


  • Seleccionado en la barra de herramientas la opción de escala (o pulsando la tecla E) y después, gráficamente sobre la figura, presionar el botón izquierdo del ratón sobre uno de los ejes de colores y sin soltar, mover el ratón.


  • Si pulsamos la tecla Ctrl' mientras escalamos, iremos escalando de una décima de unidad en una décima de unidad.


Rotación

  • Podemos rotar una figura en cualquiera de los tres ejes.
La rotación viene determinada por un número que representa la rotación en grados.
Así, 90 grados sería un cuarto de circunferencia de rotación, 180 sería la mitad y 360 una vuelta entera.


  • Al pulsar sobre el cubo aparece en el Window Inspector las propiedades del gameobject seleccionado (en este caso el cubo).
Unity3d rotacion 11.jpg


  • Para saber como se rota, debemos de tener en cuenta la siguiente regla.
Debemos imaginar que nuestra cabeza está atravesada por un palo, en cada uno de los ejes:



  • Veamos en ejemplo con una figura de un tigre:


  • Para rotar un objeto debemos de seleccionar en la Ventana de Escena (Scene Window).


  • Podemos rotar de varias formas:
  • Tecleando el valor en la caja de texto correspondiente.
Unity3d rotacion 10.jpg


  • Poniendo el ratón en la ventana Inspector, encima del eje que queramos aplicar la rotación y manteniendo presionado el botón izquierdo mover a derecha o izquierda.


  • Seleccionado en la barra de herramientas la opción de rotación (o pulsando la tecla E) y después, gráficamente sobre la figura, presionar el botón izquierdo del ratón sobre uno de los ejes de colores y sin soltar, mover el ratón.
Nota: No os extrañéis sin al modificar uno de los ejes veis que se modifica la posición. Eso es normal, ya que estamos moviendo la figura.


Pivot

  • Como comenté en otro punto, los objetos que arrastramos o creamos en la Hierarchy Window son GameObjects los cuales pueden ser dispuestos de forma jerárquica, unos 'dentro' de otros, como si fueran carpetas dentro de carpetas.
Al hacer esto, todas las propiedades 'transform' del GameObject hijo tienen unos valores relativos con respecto al GameObject padre.
Además, cualquier modificación que hagamos sobre el padre (rotar, trasladar o escala) también se reflejará en el hijo de la misma forma.
  • En todos los objetos 3D existe un punto denominado Pivot que indica al punto de referencia para rotar dicho objeto.
Normalmente este punto se encuentra en el centro del objeto 3D, pero no siempre es así.
Cuando importamos modelos complejos formados por múltiples figuras 3D puede ser que cada figura individual (imaginar un tanque con su torreta, o un avión con sus hélices, ruedas y ametralladoras) tenga su pivot en otro punto. En ese caso, cuando intentemos rotar, la rotación no la hará con respecto al punto que pensamos, sino con respecto a su pivot.
El pivot lo establece el diseñador gráfico con el programa 3D que haya utilizado.



  • Nota: Recordar por tanto que cuando trabéis con modelos importados, si cuando vayáis a realizar una rotación, el modelo no rota según lo esperado, comprobar donde se encuentra su pivot.



Ejes locales / globales de rotación

  • Otro aspecto ya comentado anteriormente es de los ejes X-Y-Z asociados a un GameObject.
Cuando rotamos un GameObject, sus ejes también son rotados.
Unity3d rotac 10f.jpg
Podemos comprobar como el cubo, al rotarlo -45º en el eje Z, sus ejes Y (color amarillo) y X (color rojo) también son rotados.


  • En cualquier momento podemos 'ver' cuales son los ejes del 'mundo' (los no rotados) si pulsamos sobre el botón que pone Local en la ToolBar (el texto del botón pasará a poner Global, para indicar que estamos viendo los ejes del mundo, no los ejes locales del GameObject):
Unity3d rotac 10g.jpg
Se puede observar como los ejes coinciden con los de Unity.


  • Veremos que cuando programemos por medio de 'scripts', podemos acceder:
  • A los ejes locales de la forma: transform.up (eje Y) / transform.forward (eje Z) / transform.right (eje X)
  • A los ejes globales (los no rotados): Vector3.up (eje Y) / Vector3.forward (eje Z) / Vector3.right (eje X)



  • Si pulsamos la tecla Ctrl' mientras rotamos, rotaremos de 15º en 15º.


Traslación

  • Podemos trasladar (mover) un objeto 3D en cualquiera de sus tres ejes.
Lo que haremos será desplazarlo un número de unidades siendo cada unidad uno de los cuadrados de la rejilla que conforman el 'suelo' del entorno de Unity3D.


  • Al pulsar sobre el cubo aparece en el Window Inspector las propiedades del gameobject seleccionado (en este caso el cubo).
Unity3d trasladar 2.jpg


  • Podemos trasladarnos de varias formas:
  • Tecleando el valor en la caja de texto correspondiente.
Unity3d trasladar 1.jpg


  • Poniendo el ratón en la ventana Inspector, encima del eje sobre el que queramos aplicar la traslación y manteniendo presionado el botón izquierdo mover a derecha o izquierda.


  • Seleccionado en la barra de herramientas la opción de traslación (o pulsando la tecla W) y después, gráficamente sobre la figura, presionar el botón izquierdo del ratón sobre uno de los ejes de colores y sin soltar, mover el ratón.



  • Si pulsamos la tecla Ctrl' mientras nos trasladamos, iremos moviendo la figura de unidad en unidad.



Ajustando los planos

  • Cuando tenemos varias figuras 3D y queremos 'colocar' unas adyacentes a otras, en cualquiera de sus 3 ejes (x,y,z) puede llegar a ser una operación 'complicada'.
Veamos un ejemplo:



Avanzado: Conceptos para programadores

  • Lo que viene a continuación son unas explicaciones teóricas para los alumnos que realizan el ciclo de Programación de aplicaciones multiplataforma.
  • Como comentamos anteriormente, las figuras 3D están formadas por múltiples triángulos.


  • Cuando se dibuja una figura 3D, la estamos dibujando siempre a partir del punto (0,0,0).
Posteriormente, se utiliza una matriz denominada matriz de modelado que se aplica sobre cada uno de los vértices de la figura para obtener el valor x-y-z una vez movida la figura, rotada y escalada.
Las operaciones anteriores (traslación, rotación y escalado) modifican la matriz de modelado.
  • Una vez la figura se encuentra en la posición correcta en nuestro mundo 3D es necesario dibujarla. Quien decide como se dibuja es una cámara (viene a ser nuestros ojos en el mundo real). Esta cámara posee otra matriz denominada matriz de proyección, la cual se aplica a cada uno de los vértices y de esa forma obtenemos los puntos x-y para dibujar nuestra figura en nuestro monitor.



Programando la Posición

  • Como ya comentamos anteriormente, las figuras están compuestas por múltiples triángulos están formados por 3 vértices.
  • Cuando se define una figura 3D (Mesh) indicamos la posición 3D (x,y,z) de cada vértice de cada triángulo (existen otras formas de aprovechar los vértices comunes y disminuir el número total de vértices).
LIBGDX UD4 Animacion 2.jpg


Esta posición se define con respecto al punto (0,0,0) Esto quiere decir que todas las figuras 3D se dibujan en dicho punto. Después dependiendo de la posición que queramos darles, se multiplican por unas matrices (matriz de modelado) para obtener las posiciones x-y-z de nuestro mundo 3D.


  • Como vimos anteriormente, las operaciones que podemos realizar sobre una figura 3D son las de: Traslación - Rotación - Escala.
Dichas operaciones van a modificar una matriz denominada matriz de modelado:
LIBGDX UD4 Animacion 4.jpg
La imagen anterior muestra la matriz de identidad (matriz con todos 1 en su diagonal) y que viene a ser como el número 1 en la multiplicación (no afecta al modelo).




Ejemplo de código utilizado en el framework LIBGDX

Ejemplo de definición de matriz:

1 public Matrix4 matriz;


Para cargar la matriz de identidad tenemos que llamar al método idt():

1 public Matrix4 matriz;
2 ..........
3 
4 matriz.idt();

Ahora será necesario aplicar los 3 tipos de operaciones:

  • Traslación:
Método translate:
Ejemplo: matriz.translate(posicion);
Siendo 'posicion' un Vector3 que indica hacia donde se tiene que trasladar.
Es importante comprender que siempre debemos de partir de la matriz identidad y aplicar la nueva posición a la que queremos llegar. No lo podemos hacer a partir de la última posición guardada.

Ejemplo de código:

1 public Matrix4 matriz;
2 ..........
3 matriz.idt();
4 matriz.translate(10,1,0);
  • Rotación:
Método rotate:
Este método está sobrecargado e tenemos varias posibilidades.

Un ejemplo:

1 public Matrix4 matriz;
2 ..........
3 matriz.idt();
4 matriz.translate(10,1,0);
5 matriz.rotate(1,0,0,90);

En este caso estaremos rotando en el eje X la figura 90º. En este caso o método rotate lleva cuatro parámetros:

  • param1: Indica se o eje X debe rotarse. Si tiene valor 1 lo rota y si tiene 0 no hace nada en ese eje.
  • param2: Indica se o eje Y debe rotarse. Si tiene valor 1 lo rota y si tiene 0 no hace nada en ese eje.
  • param3: Indica se o eje Z debe rotarse. Si tiene valor 1 lo rota y si tiene 0 no hace nada en ese eje.
  • param4: Indica el número de grados de la rotación.

Aviso: Es importante primero hacer la traslación y después la rotación, ya que si lo hacemos al revés, el punto está rotado cuando aplicamos la traslación y por tanto se moverá a otro lugar diferente al que queremos. Si lo aplicamos a una esfera, tendríamos un efecto de traslación alrededor del punto (0,0,0).


  • Escala:
Método scale:

Podemos modificar la escala en cada uno de los ejes (X-Y-Z).

Por exemplo:

1 public Matrix4 matriz;
2 ..........
3 matriz.idt();
4 matriz.translate(10,1,0);
5 matriz.rotate(1,0,0,90);
6 matriz.scale(2,1,1);

En este ejemplo indicamos que el tamaño X de la figura debe de ser el doble que el tamaño Y y Z.


  • Consultar ahora como realizar una animación en LIBGDX (sobre todo entender los conceptos de la matriz de proyección-modelado) y como se 'construye' un Mesh a partir de los vértices de sus triángulos: Enlace a animación 3D con LIBDX.




Ejemplo de código utilizado en el UNITY

  • Este es un ejemplo de código de un script de nombre PruebaMatrix4x4 asociado a una esfera:
 1 public class PruebaMatrix4x4 : MonoBehaviour {
 2     public Vector3 translation = new Vector3(5, 2, 3);
 3     public Vector3 eulerAngles = new Vector3(30, 45, 25);
 4     public Vector3 scale = new Vector3(1, 1, 1);
 5     private MeshFilter mf;
 6     private Vector3[] origVerts;
 7     private Vector3[] newVerts;
 8 
 9     void Start()
10     {
11         mf = GetComponent<MeshFilter>();
12         origVerts = mf.mesh.vertices;
13         newVerts = new Vector3[origVerts.Length];
14     }
15 
16     void Update()
17     {
18         Quaternion rotation = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
19         Matrix4x4 m = Matrix4x4.identity;
20         m.SetTRS(translation, rotation, scale);
21         int i = 0;
22         while (i < origVerts.Length)
23         {
24             newVerts[i] = m.MultiplyPoint3x4(origVerts[i]);
25             i++;
26         }
27         mf.mesh.vertices = newVerts;
28     }
29 }
Líneas 2-4: Son propiedades públicas y por tanto van a aparecer en la ventana 'Inspector' de Unity. Se definen los vectores para la posición, rotación y escala.
La rotación se define con ángulos Euler que es la forma más 'humana' de entenderlo. Internamente se guardan en un objeto de la clase Quaternion.
Línea 18: Definimos un objeto de la clase Quaternion para guardar la rotación del objeto en forma de ángulos de Euler.
Línea 19: Definimos la matriz de modelado igualándola a la matriz identidad.
Línea 20: Asignamos los tres vectores a la matriz.
Después de eso, el siguiente código aplica a cada uno del vértices del GameObject asociado los datos de la matriz.
Si ejecutáis el programa podéis ver como aparecen las propiedades public en el inspector y podéis modificar sus valores viendo como cambia la posición-escala-rotación de la esfera.




Manipular la posición en Unity

  • Nota: Vamos a partir de que a nivel de diseño tenéis un Cubo en la posición (0,0,0) con la cámara mirando hacia él.
Asociado a dicho cubo vamos a tener un script de nombre 'UD_Movim_basico_1'.


Unity3d mov 1.JPG



  • Todo visto en los puntos anteriores sirve como información de qué es lo que hace realmente Unity.
  • Pero existe una forma mucho más sencilla (que es la que usaremos nosotros) para manejar los objetos.
Es haciendo uso de la clase Transform.
Todo objeto de Unity tiene un objeto de la clase Transform asociado.
Dicho objeto nos va a permitir modificar la rotación-escala-posición del objeto.


1         Vector3 posicion = gameObject.transform.position;   // Aparece en Unity en la ventana Inspector debajo de la sección 'Transform'. No hace falta poner gameObject
2         Quaternion rotacion = gameObject.transform.rotation;   // Aparece en Unity en la ventana Inspector debajo de la sección 'Transform'. No hace falta poner gameObject
3         Vector3 escala = transform.localScale;              // No hace falta poner gameObject
  • IMPORTANTE: Lo que estamos guardando es una copia de los valores que guarda el GameObject. Por lo tanto si realizamos modificaciones tendríamos que volver a asignarlas a las propiedades del GameObject.
Recordar: Cada vez que hagáis uso de transform.position, transform.rotation y transform.localScale estáis trabajando sobre una copia y por tanto hacer esto: transform.position.Set(5,5,5) no modifica la posición del GameObject.


  • Si quisiéramos manipularlas, podemos hacerlo directamente de la forma:
(Escribir este código en el script 'UD_Movim_basico_1' creado anteriormente)
 1     // Use this for initialization
 2     void Start () {
 3 
 4         transform.position = new Vector3(2,2,2);    // Mueve el gameobject a la posición (2,2,2)
 5         transform.Rotate(30, 45, 90);       // Rota 30º en X, 45º en Y 90º en Z
 6         transform.localScale = new Vector3(2, 2, 5);  // Escala por 2 en X e Y y por 5 en Z
 7     }
 8 
 9     // Update is called once per frame
10     void Update () {
11 
12     }


Unity3d mov 2.JPG


Como vemos hemos movido el GameObject cubo a la posición indicada.


  • Podemos hacer que cualquier propiedad aparezca gráficamente en Unity estableciendo el tipo de acceso a public. Así, en nuestro caso podemos establecer las tres propiedades para que gráficamente puedan ser modificadas de la forma:
 1 public class UD_Movim_basico_1 : MonoBehaviour {
 2 
 3     public Vector3 posInicial = new Vector3(0, 0, 0);
 4     public Vector3 rotInicial = new Vector3(0, 0, 0);
 5     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 6 
 7     // Use this for initialization
 8     void Start () {
 9         transform.position = posInicial;
10         transform.Rotate(rotInicial);
11         transform.localScale = escalaInicial;
12     }
13 	
14     // Update is called once per frame
15     void Update () {
16 		
17     }
18 }


Unity3d mov 3.JPG


Ahora podemos modificar los valores iniciales.
  • Nota: El recurso de hacer public a propiedades se puede aplicar a cualquier propiedad en Unity. Por ejemplo, podríamos tener una propiedad que fuera un GameObject o un objeto de la clase Transform. De esta forma podríamos arrastrar gráficamente cualquier objeto y llevarlo al Inspector, copiándose todas sus propiedades.
Si queremos que una propiedad aparezca en el Inspector pero no queremos que se pueda acceder a ella desde otros GameObject mediante una referencia, podemos anteponer la palabra clave [SerializedField] en su definición de la forma:
1 public class UD_Movim_basico_1 : MonoBehaviour {
2 
3     [SerializeFiled]private Vector3 posInicial = new Vector3(0, 0, 0);
4     [SerializeFiled]private Vector3 rotInicial = new Vector3(0, 0, 0);
5     [SerializeFiled]private Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
6 
7 }
Así si 'arrastramos' este GameObject a otro diferente, no podremos acceder a las propiedades anteriores al estar definidas como private.




  • Ahora ha llegado el momento de hacer que el cubo se mueva. Como suponéis, debemos de modificar el valor de uno de los ejes.
El método donde se debe de escribir el código es el método Update.
A dicho método se llama de forma continuada desde que comienza el juego.
 1 public class UD_Movim_basico_1 : MonoBehaviour {
 2 
 3     public Vector3 posInicial = new Vector3(0, 0, 0);
 4     public Vector3 rotInicial = new Vector3(0, 0, 0);
 5     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 6 
 7     public float posFinalX = 5f;
 8 
 9     // Use this for initialization
10     void Start () {
11         transform.position = posInicial;
12         transform.Rotate(rotInicial);
13         transform.localScale = escalaInicial;
14     }
15 	
16     // Update is called once per frame
17     void Update () {
18 
19         if (transform.position.x <= posFinalX)
20         {
21             Vector3 temp = transform.position;
22             temp.x += .5f;
23             transform.position = temp;
24         }
25 
26 		
27     }
28 }
  • Si ejecutáis el código veréis como el cubo se mueve rápidamente hacia posición indicada por la variable 'posFinalX'.
Unity3d mov 3.JPG



Parámetro delta

  • El problema que tenemos es que no tenemos control de la velocidad.
Necesitamos comprender el concepto del parámetro delta


  • Lo que tenemos que tener que claro que cualquier movimiento se produce borrando toda la pantalla y volviéndola a dibujar.
Lo que pasa es que se hace tan rápido que para nuestros ojos solo se tiene la impresión del movimiento.
  • La rapidez con que se borra todo y se vuelve a dibujar la establecemos mediante un parámetro denominado Fotogramas Por Segundo (fps).
Este parámetro va a depender de la máquina donde se ejecute el juego (capacidades hardware de la misma, como procesador, tarjeta gráfica,...) y de lo optimizado que esté nuestro juego.


  • Al depender de la máquina, no podemos hacer que los elementos del juego se muevan sin ningún control, ya que con el código anterior, en una máquina a 60fps se movería mucho más rápido que en una máquina a 30fps. Debemos hacer que la velocidad del movimiento sea independiente del tipo de máquina.
Eso lo conseguimos con el parámetro delta.
  • delta es el tiempo que transcurre desde que se llama a un método y vuelve a llamarse al mismo método.
La forma de obtener dicho valor es mediante la clase Time de la forma Time.deltaTime.


  • Imaginemos que tenemos dos dispositivos Android, uno funcionando a 50fps y otro a 25fps.
Esto quiere decir que (más o menos) se va a llamar al método Update ese número de veces por segundo.
Si ejecutamos este código para mover el cubo:
1     void Update () {
2        if (transform.position.x<100){
3         Vector3 temp = transform.position;
4         temp.x += 1f;
5         transform.position = temp;
6        }	
7     }
  • Como vemos se va a mover mientras X sea menor que 100 unidades (en Unity, cada cuadrado en el entorno tridimensional tiene un tamaño 1 metro x 1 metro (a tener en cuenta si vamos a aplicar el motor de físicas a nuestro juego).



Lo que va a pasar:
Máquina1:
50fps => se mueve a 50 unidades por segundo => Tarda 100/50 en recorrer toda la pantalla => 2 segundos.
Máquina2:
25fps => se mueve a 25 unidades por segundo => Tarda 100/25 en recorrer toda la pantalla => 4 segundos.


  • Por lo tanto se movería más rápido en la primera máquina y eso no parece muy justo :)
¿ Cómo podemos hacer para que el movimiento sea independiente del hardware de la máquina ? Multiplicando la velocidad por delta siendo delta el tiempo que tarda en llamar al método update que actualiza la posición.
  • Por ejemplo, siguiendo con el ejemplo anterior:
Máquina1: a 50 fps. Quiere decir que llama 50 veces en un segundo al método update.
Delta = 1/50
temp.x = temp.x + (1 * delta) :multiplicamos la velocidad por delta (en este caso la velocidad es una unidad por segundo). En nuestro caso se moverá durante 100 unidades.
¿ Cuanto tiempo tarda en recorrer las 100 unidades ?
En un segundo recorre:
50 (fps = veces por segundo) * 1/50 (es delta) * 1 (la velocidad) = 1.
Por lo tanto va a sumar una unidad a la posición X cada segundo.


Máquina2: a 25 fps. Quiere decir que llama 25 veces en un segundo al método update.
Delta = 1/25
temp.x = temp.x + (1 * delta) :multiplicamos la velocidad por delta (en este caso la velocidad es una unidad por segundo). En nuestro caso se moverá durante 100 unidades.
¿ Cuanto tiempo tarda en recorrer las 100 unidades ?
En un segundo recorre:
25 (fps = veces por segundo) * 1/25 (es delta) * 1 (la velocidad) = 1.
Por lo tanto va a sumar una unidad a la posición X cada segundo.


  • Como vemos tarda lo mismo. Lo que está haciendo es ajustar, disminuyendo o aumentando la velocidad en función del parámetro delta. Si delta es más grande (llama muchas veces por segundo) hace que se mueva más lentamente ya que lo multiplica por 1/delta.
  • Aplicando estos conceptos a nuestro ejemplo el código quedaría así:
 1 public class UD_Movim_basico_1 : MonoBehaviour {
 2 
 3     public Vector3 posInicial = new Vector3(0, 0, 0);
 4     public Vector3 rotInicial = new Vector3(0, 0, 0);
 5     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 6 
 7     public float posFinalX = 5f;
 8     public float velocidad = 0.5f;
 9 
10     // Use this for initialization
11     void Start () {
12         transform.position = posInicial;
13         transform.Rotate(rotInicial);
14         transform.localScale = escalaInicial;
15     }
16 	
17     // Update is called once per frame
18     void Update () {
19 
20         if (transform.position.x <= posFinalX)
21         {
22             Vector3 temp = transform.position;
23             temp.x = temp.x + (velocidad*Time.deltaTime);
24             transform.position = temp;
25         }
26     }
27 }


Si ejecutáis el código anterior y en ejecución os vais a la pestaña 'Scene' podéis comprobar como el cubo de mueve a la velocidad indicada y recorre (en este ejemplo) 5 unidades:
Unity3d mov 6.JPG

Optimizando el uso de la memoria

  • Más información:


  • Debemos de tener en cuenta que el método Update se está llamando muchas veces por segundo, por lo que tenemos que tener cuidado con las operaciones que reservan memoria y que se ejecuten dentro de dicho método.
  • Concatenación de cadenas: Es algo que consume memoria, ya que cada concatenación haciendo uso del símbolo más crea una nueva cadena en memoria. Se debe hacer uso de la clase StringBuielder.
  • Cualquier operación que lleve consigo el uso de un 'new', deberíamos intentar 'sacarla' fuera del método Update o que si se ejecuta bajo ciertas condiciones, establecerlas previamente con un if.


  • Por ejemplo, el siguiente código comprueba que ha habido un cambio en el marcador para mostrarlo, en vez de llamar al método mostrarMarcador() sin condición:
 1     ............
 2     private bool cambiaMarcador = false;
 3 
 4 
 5     private void mostrarMarcador()
 6     {
 7         // Código para mostrar el marcador
 8     }
 9 
10     // Update is called once per frame
11     void Update () {
12 
13         if (cambiaMarcador)
14         {
15             mostrarMarcador();
16         }
17 
18         .........
19    }



Variables y métodos de ayuda para el movimiento


Vectores de movimiento
  • Vectores que se pueden utilizar para el movimiento:
Unity3d mov 8.JPG


  • up, down, left, right ,back, forward:
Así, si quiero desplazarme a la derecha tendré que poner este código:
1   void Update () {
2 
3         if (transform.position.x <= posFinalX)
4         {
5             transform.position += Vector3.right*velocidad*Time.deltaTime;
6         }
7   }
Y lo mismo para todas las demás direcciones.
Si quisiéramos movernos 'arriba a la derecha':
1 void Update () {
2 
3         if (transform.position.x <= posFinalX)
4         {
5             transform.position += (Vector3.right+Vector3.up)*velocidad*Time.deltaTime;
6         }
7 }


  • Nota Importante:
Debemos de tener en cuenta que estos vectores siempre devuelven los mismo valores. Por ejemplo, el Vector3.forward es el vector (0,0,1).
Sin embargo, a nivel de programación podemos acceder a los vectores: up, forward,right del gameobject, de la forma: gameObject.transform.up (o también transform.up). Fijarse que 'transform' es con minúsculas, indicando que se refiere al gameobject asociado.
Estos vectores son los vectores que 'apuntan' hacia arriba, adelante y derecha estando rotados si el objeto fue rotado previamente.
Se ve claramente en la siguiente imagen:

[Imagen:15B_direction_vecs.jpg]

Respuesta de gabs en este enlace
Los colores claros son los vectores de los objetos que han sido rotados. Los colores oscuros son los vectores del mundo (Vector3.up,Vector3.right,....) que siempre 'apuntan' hacia la misma dirección, no se rotan.



En Unity podemos 'ver' los ejes globales o los ejes locales (los rotados) de un objeto 3D presionando el botón



Vector cero
Dentro de la clase Vector3.
  • Vector zero: Utilizado para inicializar un Vector3 o para situar un objeto en la posición (0,0,0).



Vectores infinitos
Dentro de la clase Vector3.


  • Vector negativeInfinity, positiveInfinity: Devuelven un vector3 con valores 'infinitos' en (x,y,z). Se puede utilizar para determinar cuando no te encuentras dentro de un área de visualización, asignando dichos valores al vector correspondiente.



Método Translate
  • Método 'translate' de la clase Transform: Modifica la posición de un gameobject desplazándola en los valores indicados. Es equivalente a poner: position = position+valor. Es decir, traslada el objeto desde su posición a una nueva añadiendo un nuevo valor. No hace como la propiedad position, que 'mueve' el gameobject a la posición indicada.

Nota: Tener en cuenta que el método está sobrecargado y existen varias formas de realizar la misma acción.


Veamos un ejemplo sobre el script anterior:
 1 public class UD_Movim_basico_1 : MonoBehaviour {
 2 
 3     public Vector3 posInicial = new Vector3(0, 0, 0);
 4     public Vector3 rotInicial = new Vector3(0, 0, 0);
 5     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 6 
 7     public float velocidad = 2f;
 8 
 9     // Use this for initialization
10     void Start () {
11         transform.position = posInicial;
12         transform.Rotate(rotInicial);
13         transform.localScale = escalaInicial;
14 
15     }
16 	
17 	// Update is called once per frame
18 	void Update () {
19 
20         transform.Translate(Vector3.up* velocidad * Time.deltaTime);
21 
22 	}
23 }


Este método está sobrecargado. Podemos hacer uso de una de sus variantes, en las que utiliza como como punto de referencia para moverse, no su posición local (donde se encuentra el GameObject) sino:
  • Otro sistema de referencia de otro objeto (el transform de otro gameobject).
  • El sistema de referencia del mundo 3D, haciendo uso de la constante: Space.World
¿ Qué significa 'sistema de referencia' ? Pues que parte del punto indicado por el sistema de referencia para realizar la traslación del GameObject. Esto parece que no tiene sentido, ya que lo que hacemos es desplazar, por lo tanto, ¿ qué diferencia hay entre desplazar el GameObject 3 unidades hacia arriba tomando como referencia el propio GameObject (es decir, su posición actual) que tomar como referencia la coordenada (0,0,0) del mundo 3D ? Siguen siendo 3 unidades en los dos sistemas de referencia.
La diferencia está en la rotación. En el sistema de referencia del mundo no se tiene en cuenta la rotación, por lo tanto, desplazar 3 unidades hacia arriba conlleva modificar la posición del GameObject en el eje Y 3 unidades arriba.
Sin embargo, si utilizamos como sistema de referencia un objeto rotado, digamos 45 grados en el eje X, y lo movemos hacia arriba, se desplazará hacia arriba-atrás (eje Y-Z).
Veamos un ejemplo.
Modificar la escena anterior y añadir una esfera a la posición (0,0,0).
Modificar el cubo de la escena, cambiarle el nombre por el de 'Cube-Self' (cuidado las mayúsculas-minúsculas) y posicionarlo en la posición (-2,0,0). Rotar el cubo 45 grados en el eje X.
Unity3d mov 12.JPG
Ahora vamos a duplicar el gameobject 'Cube-Self' pulsando el botón derecho sobre él y escogiendo la opción Duplicate:
Unity3d mov 13.JPG
Cambiamos el nombre del GameObject por el de 'Cube-World' (cuidado las mayúsculas-minúsculas) y lo posiciones en la coordenada (2,0,0)
Unity3d mov 14.JPG
Modificamos el código del script 'UD_Movim_basico_1':
 1 public class UD_Movim_basico_1 : MonoBehaviour {
 2 
 3     public float velocidad = 2f;
 4 
 5     // Use this for initialization
 6     void Start () {
 7     }
 8 
 9     // Update is called once per frame
10     void Update()
11     {
12 
13         if (transform.position.x < 5 && transform.position.y < 5 && transform.position.z < 5)
14         {
15             if (gameObject.name.Equals("Cube_Self"))
16             {
17                 transform.Translate(Vector3.up * velocidad * Time.deltaTime, Space.Self);
18             }
19             else if (gameObject.name.Equals("Cube_World"))
20             {
21                 transform.Translate(Vector3.up * velocidad * Time.deltaTime, Space.World);
22             }
23 
24         }
25     }
26 }
Lo que hace el script es trasladar el 'Cube_Self' utilizando como sistema de referencia su propio transform(se podría omitir este tercer parámetro y tendría el mismo efecto) y trasladar el cubo 'Cube_World' utilizando como sistema de referencia el mundo 3D (Space.World).
Al ejecutar el script podemos ver como el 'Cube_World' se traslada hacia arriba mientras que el 'Cube_Self' se traslada hacia arriba-atrás, debido a que está rotado:


NOTA IMPORTANTE: En este método es mejor utilizar siempre los vectores Vector.up, Vector.forward,..., es decir, los vectores de referencia deberían ser los del mundo, teniendo en cuenta que el segundo parámetro del método nos indica el sistema de referencia, si queremos que un objeto se mueva hacia adelante con respecto a sus ejes (estando rotado) deberemos de poner: transform.Translate(Vector3.forward * velocidad * Time.deltaTime, Space.self);



Método MoveTowards
Dentro de la clase Vector3.


  • Método MoveTowards: Mueve el GameObject desde la posición indicada por el parámetro 'current' hasta la posición indicada por el parámetro 'target' en la distancia indicada por el parámetro step.
  • Normalmente 'step' tiene como valor: Velocidad * Time.deltaTime
Debo aclarar que 'mover' no es la definición exacta, ya que no modifica los vectores que le pasamos como parámetros, sino que devuelve un Vector3 con la nueva posición (la que va de origen a destino) movida un 'poquito' en cada frame.
Lo que debemos hacer es igualar ese vector a la posición de lo que queramos mover, de la forma: transform.position=Vector.moveTowards(origen, destino, velocidad*delta).


Veamos un ejemplo, modificando el código del script 'UD_Movim_basico_1' para que el cubo se mueva hacia donde se encuentre el ratón en la pantalla:

Nota: Recordar que vimos anteriormente como proyectar un punto desde la pantalla al espacio 3D.

 1 public class UD_Movim_basico_1 : MonoBehaviour {
 2 
 3     public Vector3 posInicial = new Vector3(0, 0, 0);
 4     public Vector3 rotInicial = new Vector3(0, 0, 0);
 5     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 6 
 7     public float posFinalX = 5f;
 8     public float velocidad = 1f;
 9 
10     // Use this for initialization
11     void Start () {
12         transform.position = posInicial;
13         transform.Rotate(rotInicial);
14         transform.localScale = escalaInicial;
15 
16     }
17 
18     // Update is called once per frame
19     void Update () {
20 
21         Vector3 posRaton = Input.mousePosition;
22         posRaton.z = 8;         // Plano donde se encuentra el cubo con respecto a la cámara. Adaptarlo a vuestro caso si la cámara se encuentra en otra posición.
23         Vector3 posMundo = Camera.main.ScreenToWorldPoint(posRaton);
24 
25         transform.position = Vector3.MoveTowards(transform.position, posMundo, velocidad * Time.deltaTime);
26 
27     }
28 }


Si ahora ejecutamos el juego, al mover el ratón el cubo irá hacia el mismo:
Unity3d mov 9.JPG



Método Lerp
  • Método Lerp: Es parecido al anterior, también mueve un GameObject de un origen a un destino, pero varía en el tercer parámetro, el cual indica el porcentaje de cuan cerca está del punto destino. Es decir, el tercer parámetro va de 0 a 1, siendo 0 el valor que indica que está en el origen y 1 el valor que indica que está en el destino. Si por ejemplo tuviéramos 0.5f querría decir que estaríamos en el punto intermedio entre el origen y destino.
Al igual que en el método anterior, no modifica los vectores que le pasamos como parámetro y devuelve un Vector3 con la nueva posición.
Hace uso de la interpolación para obtener los puntos intermedios desde el origen al destino.


Debemos de tener en cuenta que aquí no se puede pasar como tercer parámetro Velocidad*Time.deltaTime ya que este valor nunca será igual a uno y por tanto nunca llegaría al punto destino.
Un uso que se le puede dar a Lerp (entre otros) es el de recorrer una determinada distancia en un tiempo concreto.
Podemos utilizar 'Time.deltaTime' como un reloj y poner como tercer parámetro: reloj / tiempo_total, siendo tiempo_total el número de segundo que queremos que tarde en alcanzar el punto de destino.


Modificamos el script anterior para que cubo se mueva a la posición del ratón cuando pulsamos el botón del mismo en un tiempo (por defecto 1 segundo). Vamos a hacer que el tiempo que tarda pueda ser modificado desde el inspector haciendo pública la propiedad:


 1 public class UD_Movim_basico_1 : MonoBehaviour {
 2 
 3     public Vector3 posInicial = new Vector3(0, 0, 0);
 4     public Vector3 rotInicial = new Vector3(0, 0, 0);
 5     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 6 
 7     public float tiempoTotal = 1f;    // Tiempo que tarda el cubo en llegar al destino.
 8     private float cronometro = 0f;
 9 
10     private Vector3 posFinal = new Vector3(0,0,0);       // Posición de destino
11 
12     // Use this for initialization
13     void Start () {
14         transform.position = posInicial;
15         transform.Rotate(rotInicial);
16         transform.localScale = escalaInicial;
17 
18     }
19 	
20     // Update is called once per frame
21     void Update () {
22 
23 
24         if (Input.GetMouseButtonDown(0))    // Presionamos el botón del ratón
25         {
26             cronometro = 0;
27             posInicial = transform.position;
28 
29             Vector3 posRaton = Input.mousePosition;
30             posRaton.z = 8;         // Plano donde se encuentra el cubo con respecto a la cámara. Adaptarlo a vuestro caso si la cámara se encuentra en otra posición.
31             posFinal = Camera.main.ScreenToWorldPoint(posRaton);
32 
33         }
34 
35         cronometro += Time.deltaTime;
36         transform.position = Vector3.Lerp(posInicial, posFinal, cronometro / tiempoTotal);
37 
38 	}
39 }
Unity3d mov 10.JPG



Método SmoothDamp
Dentro de la clase Vector3.


  • Método SmoothDamp: Tiene el mismo efecto que el método anterior pero realiza el movimiento de una form más 'suave' ya que va aumentando la velocidad progresivamente y cuando se va acercando al destino va disminuyendo de velocidad. Se suele utilizar para seguir a una cámara en el juego.

Nota: Tener en cuenta que el método está sobrecargado y existen varias formas de realizar la misma acción.

Veamos un ejemplo modificando el código del script.
Al pulsar el botón de ratón, el cubo se moverá a dicha posición en el tiempo indicado por la variable 'tiempoTotal'.
Aquí no necesitamos cronómetro ya que el método hace los cálculos para llegar en el tiempo indicado, ajustando la velocidad.
Dispone de un parámetro currentVelocity. Es un parámetro que va a hacer referencia a un atributo de tipo Vector3 el cual va a modificar en cada frame, ajustando la velocidad. Podemos ver como dicha velocidad se modifica...


 1 public class UD_Movim_basico_1 : MonoBehaviour {
 2 
 3     public Vector3 posInicial = new Vector3(0, 0, 0);
 4     public Vector3 rotInicial = new Vector3(0, 0, 0);
 5     public Vector3 escalaInicial = new Vector3(1, 1, 1);    // La escala no debería ser 0
 6 
 7     public float tiempoTotal = 1f;    // Tiempo que tarda el cubo en llegar al destino.
 8     private Vector3 posFinal = new Vector3(0,0,0);       // Posición de destino
 9 
10     private Vector3 velocidadActual = Vector3.zero;
11 
12     // Use this for initialization
13     void Start () {
14         transform.position = posInicial;
15         transform.Rotate(rotInicial);
16         transform.localScale = escalaInicial;
17 
18         posFinal = transform.position;
19     }
20 	
21     // Update is called once per frame
22     void Update () {
23 
24         Debug.Log("Velocidad:" + velocidadActual);
25         if (Input.GetMouseButtonDown(0))    // Presionamos el botón del ratón
26         {
27             Vector3 posRaton = Input.mousePosition;
28             posRaton.z = 8;         // Plano donde se encuentra el cubo con respecto a la cámara. Adaptarlo a vuestro caso si la cámara se encuentra en otra posición.
29             posFinal = Camera.main.ScreenToWorldPoint(posRaton);
30 
31         }
32 
33         transform.position = Vector3.SmoothDamp(transform.position, posFinal,ref velocidadActual, tiempoTotal);
34 
35 	}
36 }
  • Línea 10: Vamos a guardar la velocidad que se va a modificar automáticamente al llamar al método 'SmoothDamp'.
  • Línea 24: Por la consola vamos a poder ver como la velocidad se modifica.
  • Línea 33: Fijarse que el parámetro de la velocidad lleva la palabra reservada 'ref'.


Unity3d mov 11.JPG




Distancia y dirección
  • Durante el desarrollo de juegos suele ser bastante habitual la necesidad de determinar la distancia a la que se encuentra un gameobject de otro, así como determinar el vector dirección entre los dos.



Distancia: Método magnitude
  • Para calcular la distancia entre dos puntos de nuestro espacio 3D tenemos que hacer uso del método magnitude de la clase Vector3.
Veamos un ejemplo, modificando el script anterior:
 1 public class UD_Movim_basico_1 : MonoBehaviour {
 2 
 3     public float velocidad = 2f;
 4 
 5     private GameObject esfera;
 6 
 7     // Use this for initialization
 8     void Start () {
 9 
10         esfera = GameObject.Find("Sphere"); // Buscamos el GameObject por su nombre. No debe de ponerse en el método Update
11     }
12 
13     // Update is called once per frame
14     void Update()
15     {
16 
17         if (transform.position.x < 5 && transform.position.y < 5 && transform.position.z < 5)
18         {
19             if (gameObject.name.Equals("Cube_Self"))
20             {
21                 transform.Translate(Vector3.up * velocidad * Time.deltaTime, Space.Self);
22             }
23             else if (gameObject.name.Equals("Cube_World"))
24             {
25                 transform.Translate(Vector3.up * velocidad * Time.deltaTime, Space.World);
26                 Vector3 distancia = transform.position - esfera.transform.position;
27                 Debug.Log(distancia.magnitude);
28             }
29 
30         }
31 
32 
33     }
34 }
Unity3d mov 17.JPG


IMPORTANTE:' El llamar al método magnitude lleva consigo realizar una operación de raíz cuadrada sobre (x*x+y*y+z*z).

Si queremos saber la distancia entre Varios gameobject para saber cual está más cerca con respecto a otro gameobject es más eficiente hacer uso del método sqrMagnitude que devuelve la distancia al cuadrado (y por tanto no tiene que realizar la operación de raíz cuadrada).
Por lo tanto, si queremos controlar la distancia a un gameobject haciendo uso de este método debemos de tener algo parecido a esto:
1 Vector3 distancia = GameObject1.position - GameObject2.position;
2 if (distancia.sqrMagnitude < maxDistancia*maxDistancia) {
3 
4 }
Como vemos debemos de multiplicar la distancia a controlar consigo mismo.



Vector dirección
  • Normalmente en todos los juegos vais a tener algún personaje que se mueva hacia otro, lo persiga, un misil teledirigido...
Unity ya ofrece métodos que nos permite realizar estas acciones (como el método Lerp visto anteriormente), pero en ciertas ocasiones puede ser necesario obtener la dirección de un GameObject con respecto a otro.
Para saberlo es necesario obtener lo que se denomina Vector dirección.


  • Primero debemos de saber que es un Vector. De forma resumida para nosotros un Vector va a representar una dirección que tiene que seguir un GameObject para llegar a un punto de destino.
Si dicho punto de destino no varía en el tiempo, el vector de dirección se calcula al principio (método Start) y no variará con el tiempo. Si queremos seguir a otro GameObject que se mueve en el tiempo, debemos de recalcular el vector dirección cada cierto tiempo (método Update).
La forma más sencilla es verlo con un ejemplo:
Nota: Los conceptos aprendidos serán igualmente aplicables a modelos 3D añadiendo la coordenada Z.


Imaginemos que estamos en la coordenada (1,1) y queremos dirigirnos a la coordenada (4,2).
Lo que tendríamos que hacer es calcular cual es el vector de dirección, es decir, que números (x,y) sumados a (1,1) nos llevan hasta (4,2).


LIBGDX UD2 7 graficos 4.jpg
Lo más lógico es restar el punto de destino menos el punto de origen de la forma:
  • Vector Dirección = Punto_Destino – Punto_Origen = (4,2)-(1,1) = (3,1).


  • En Unity, podemos restar los vectores directamente (utilizando Vector3, sería igual si usáramos Vector2 pero sin la coordenada Z):
  • Vector3 direccion= posDestino - posOrigen;
Nota: Recordar que si podemos evitarlo, es mejor no poner esta operación en el método Update. Si no varía el punto destino ni origen puede ir en el Start o bien dentro del Update cuando se produzca alguna variación en la posición (iría con un if).


En el ejemplo anterior tendríamos un Vector de dirección con los valores (3,1)
Ahora podríamos llegar la punto destino en un único 'salto' ya que si sumamos de la forma:
posicion = posicion + direccion= (1,1)+(3,1) = (4,2).
Para que vaya moviéndose 'poco a poco' es necesario 'normalizar' dicho vector para que tenga una magnitud de 1.
Podemos hacerlo de dos formas:
  • Opción a) Dividiendo la distancia total por su magnitud.
  • Vector3 direccion = direccion / direccion.magnitude;
  • Vector3 direccion = direccion.normalized;
Una vez normalizado, podríamos hacer uso de dicho vector para llegar desde el punto origen al punto destino de la forma ya vista:
  • posicion += direccion*velocidad*Time.deltaTime;


Veamos un ejemplo, moviendo la esfera detrás de uno de los cubos.
 1 public class UD_Movim_basico_1 : MonoBehaviour {
 2 
 3     public float velocidad = 2f;
 4 
 5     private GameObject esfera;
 6 
 7     // Use this for initialization
 8     void Start () {
 9 
10         esfera = GameObject.Find("Sphere"); // Buscamos el GameObject por su nombre. No debe de ponerse en el método Update
11     }
12 
13     // Update is called once per frame
14     void Update()
15     {
16 
17         if (transform.position.x < 5 && transform.position.y < 5 && transform.position.z < 5)
18         {
19             if (gameObject.name.Equals("Cube_Self"))
20             {
21                 transform.Translate(Vector3.up * velocidad * Time.deltaTime, Space.Self);
22             }
23             else if (gameObject.name.Equals("Cube_World"))
24             {
25                 transform.Translate(Vector3.up * velocidad * Time.deltaTime, Space.World);
26                 Vector3 direccion = transform.position - esfera.transform.position;
27                 esfera.transform.Translate(direccion.normalized * velocidad * Time.deltaTime);
28             }
29 
30         }
31 
32 
33     }
34 }



  • Para obtener el vector dirección de un objeto (hacia donde está apuntando) podemos hacer uso de los vectores: up, forward y right del transform del gameobject.
Es decir:
  • transform.up (con minúscula) retorna el vector dirección del eje Y del gameobject estando rotado.
  • Vector3.up retorna el vector (0,1,0) siempre.



Método RotateTowards
Dentro de la clase Vector3.
  • Parámetro maxRadiansDelta es la velocidad de rotación, normalmente se pone Time.Delta*velocidad
  • Parámetro (maxMagnitudeDelta) normalmente se pone de valor 0.0f.
  • Es parecido al método MoveTowards con la diferencia que no nos movemos, sino que rotamos un vector.
Como pasaba en métodos anteriores, realmente no rota nada, sino que dicho método devuelve un Vector3 con los datos necesarios para rotar un GameObject.
Debemos de tener en cuenta que este método trabaja con direcciones no con posiciones.
Es decir, el Vector3 current indica la dirección en la que nos encontramos, y el Vector3 target indica la dirección a la que queremos ir.
Además solo trabaja con los ejes Z (vector3 forward) e Y (vector3 up) (es decir, solo rota esas direcciones).




Programando la Rotación

  • Para rotar, al igual que en el caso del movimiento, disponemos de varios métodos.


  • Nota Importante: Debemos de tener cuidado con las animaciones (las veremos en este punto de la Wiki que tenga asociado un modelo, ya que si estas tienen movimientos de rotación, sobre-escribirán las rotaciones que tengamos puesto a nivel de programación.
Para evitarlo podemos:
  • Crear un GameObject vacío y hacer que el modelo sea 'hijo' de este GameObject, asociando nuestro script al GameObject vacío.
  • Cambiar el orden de los componentes en la Inspector Window y hacer que el script se ejecute antes, por lo que primero realizará nuestra rotación y después la rotación de la animación.



Método Transform.Rotate

Rota un GameObject en base a los ángulos suministrados en el método (ángulos de Euler).
Nota: Debemos de tener en cuenta que cualquier rotación que hagamos es acumulativa. Esto quiere decir que sumáis a la rotación que ya tengáis, la nueva. Por eso, en la sección anterior donde teníamos un script en el que podíamos asignar una 'rotación inicial' en la ventana de Inspector, si tuviéramos alguna rotación previa en las propiedades del Transform, se sumaría.
Nota: Al igual que en el caso del método Translate podemos hacer la rotación con respecto a diferentes sistemas de referencia. Por defecto será Space.Self, es decir, el propio GameObject. Al igual que en el caso de Translate, si tomamos como referencia Space.World, la rotación de referencia serán los ejes (x,y,z) de nuestro mundo (los cuales no están rotados). En otro caso, se tomará como referencia la rotación del transform que utilicemos como parámetro, en los que los ejes pueden estar previamente rotados.


Para inicializar la rotación de un objeto tendremos que poner el siguiente código:
1         transform.rotation = Quaternion.identity;


Veamos un ejemplo de código de rotación.
Crear un script de nombre UD2_Movim_Rotac_1 y asociarlo a un cubo de nombre Cube_Self posicionado en (-2,0,0) y con un ángulo de rotación de 45 grados en el eje Y. Crear otro cubo de nombre Cube_World posicionado en (2,0,0) y con un ángulo de rotación de 45 grados en el eje Y.
La cámara mirando hacia ambos.
Asociar a los dos cubos el script siguiente:
Código de UD2_Movim_Rotac_1:
 1 public class UD2_Movim_Rotac_1 : MonoBehaviour {
 2 
 3 	// Use this for initialization
 4 	void Start () {
 5         if (gameObject.name.Equals("Cube_Self"))
 6         {
 7             transform.Rotate(45, 0, 0, Space.Self);
 8         }
 9         else
10         {
11             transform.Rotate(45, 0, 0, Space.World);
12         }
13     }
14 
15 	
16 	// Update is called once per frame
17 	void Update () {
18 
19 
20 		
21 	}
22 }



  • Al igual que hicimos en Translate, podemos rotar los objetos dentro del método Update para que giren de forma continuada y combinar diferentes operaciones a la vez.
Crear un nuevo script de nombre UD2_Movim_Rotac_2.
 1 public class UD2_Movim_Rotac_2 : MonoBehaviour {
 2     public float velocidadTraslac = 2f;
 3     public float velocidadRotac = 30f; // 30 grados por segundo
 4 
 5     // Use this for initialization
 6     void Start()
 7     {
 8     }
 9 
10 
11     // Update is called once per frame
12     void Update()
13     {
14         if (gameObject.name.Equals("Cube_Self"))
15         {
16             transform.Rotate(Vector3.right * velocidadRotac * Time.deltaTime);
17             transform.Translate(Vector3.left * velocidadTraslac * Time.deltaTime);
18         }
19         else
20         {
21             transform.Rotate(Vector3.right * velocidadRotac * Time.deltaTime, Space.World);
22             transform.Translate(Vector3.right * velocidadTraslac * Time.deltaTime, Space.World);
23         }
24 
25     }
26 
27 }


En Unity podemos asociar varios scripts al mismo GameObject simplemente arrastrándalos hacia el mismo o añadiendo un componente como ya vimos. En este caso vamos a quitar el script anterior y asociar el nuevo:


  • Este método está sobrecargardo y podemos emplearlo para rotar en algún eje concreto de la forma:
1     transform.Rotate(Vector3.up * Time.deltaTime*velocidad);
En el ejemplo estaríamos rotando el GameObject en el eje Y del mundo (sin tener en cuenta la rotación local del GameObject)


Método Transform.RotateAround

  • Método RotateAround de la clase Transform: Modifica la transform de un objeto rotando dicho objeto sobre un eje alrededor de un punto determinado.
Un ejemplo clásico sería el de un planeta rotando alrededor de un sol.


  • Veamos un ejemplo:
Vamos a crear dos esferas de diferentes tamaños.
  • Esfera 1: Cambiamos el nombre por 'Sol'. Posicionado en (0,0,0) y escala (2,2,2)
  • Esfera 2: Cambiamos el nombre por 'Planeta'. Posicionado en (-15,0,0)
  • Cámara: Posicionada en (0,6,-18) (podéis ponerla donde queráis, es una referencia). Rotada (23,0,0).


Tendréis algo parecido a esto:
Unity3d mov rot 12.JPG
Creamos un script de nombre UD_Movim_Rotac_5:
 1 public class UD_Movim_Rotac_5 : MonoBehaviour {
 2 
 3     private float angulo = 10f;
 4 
 5     // Use this for initialization
 6     void Start () {
 7 		
 8     }
 9 	
10     // Update is called once per frame
11     void Update () {
12 
13         transform.RotateAround(Vector3.zero, Vector3.up, angulo*Time.deltaTime);
14     }
15 }


Lo que hace el script es rotar 10' por segundo el gameobject alrededor del punto (0,0,0)
Asociar el script al GameObject planeta
Si ahora ejecutáis el juego podéis observar como el planeta 'gira' alrededor del sol:
Unity3d mov rot 13.JPG



Método Quaternion.LookRotation

Pertenece a la clase Quaternion.
Dicho método espera dos parámetros:
  • Vector3 forward: Es el vector dirección de hacia donde queremos seguir.
  • Vector3 upwards: Es el vector que indica la dirección del vector 'up'. Normalmente pondremos Vector3.up
El vector que falta (right), se obtiene de multiplicar los dos vectores anteriores.
La clase Quaternion debe guardar la información de rotación de los tres ejes.


  • Recordar que para rotar un GameObject haciendo uso de un objeto de la clase Quaternion, solamente tenemos que igualarlo a su propiedad 'rotation' de la forma:
1    gameObject.transform.rotation = quaternion;
Siendo quaternion un objeto de la clase Quaternion.


  • Veamos un ejemplo práctico y conjunto con el método anterior (RotateAround).
Vamos a realizar un ejercicio en el que tendremos un cañón que irá 'apuntando' a una esfera que se moverá alrededor del cañón.
Unity3d mov rotac 3.jpg


  • En el diseño vamos a aprovechar una de las ventajas de utilizar la jerarquía para hacer que el cañón sea 'hijo' del cubo. De esa forma, al rotar el cubo también rotará el cañón.


  • Ahora necesitamos crear un script que se asociará a la esfera que y la moverá alrededor del cañón.
  • Nombre script: UD2_Mov_Rotac_6A
 1  using UnityEngine;
 2 
 3 /**
 4  * Rota la esfera alrededor del cañon
 5  * 
 6  */
 7 public class UD2_Mov_Rotac_6A : MonoBehaviour
 8 {
 9     [SerializeField]
10     private float angulo = 30;
11     [SerializeField]
12     private Transform posCanhon;
13 
14     
15     // Start is called before the first frame update
16     void Start()
17     {
18         
19     }
20 
21     // Update is called once per frame
22     void Update()
23     {
24         transform.RotateAround(posCanhon.position, Vector3.up, angulo * Time.deltaTime);
25     }
26 }
  • Línea 12: El código es el mismo que el ejercicio anterior con la diferencia que estamos haciendo uso de una variable de tipo 'Transform' la cual va a guardar la posición del cañón ya que la idea es que la esfera gire alrededor de donde se encuentre el cañón.




  • Ahora creamos el script que hará que el cañón rote, persiguiendo a la esfera.
  • Nombre script: UD2_Mov_Rotac_6B
 1 using UnityEngine;
 2 
 3 /** Ejemplo de script que muestra un ejemplo de uso del
 4  *   método Quaternion.LookRotation
 5  *   
 6  */
 7 public class UD2_Mov_Rotac_6B : MonoBehaviour
 8 {
 9     [SerializeField]
10     private Transform posicionRotar;
11 
12     // Start is called before the first frame update
13     void Start()
14     {
15         
16     }
17 
18     // Update is called once per frame
19     void Update()
20     {
21         Vector3 dir = posicionRotar.position - transform.position;     // No hace falta normalizar el vector3 ya que no lo vamos a usar para movernos
22         Quaternion quaternion = Quaternion.LookRotation(dir, Vector3.up);
23         transform.rotation = quaternion;
24 
25         
26     }
27 }
  • Línea 10: Este script se va a asociar al cañón, el cual queremos que 'rote' persiguiendo a la esfera. Por tanto necesitamos saber cual es la posición de la esfera.
Definimos por tanto una propiedad que va a guardar esa información.
  • Línea 21: Obtenemos el vector dirección que apunta a la esfera.
  • Línea 22: Guardamos en el objeto quaternion la información de rotación.
  • Línea 23: Rotamos el cañón.




  • Si ahora ejecutáis el juego podéis ver como la esfera rota alrededor del cañón y el cañón apunta a la esfera.



Método Quaternion.Lerp

Pertenece a la clase Quaternion.
Esto permite que la rotación del objeto vaya 'retrasada' con respecto a la rotación del objeto que estamos siguiendo.
Es lo que sucede en la realidad. Cuando un tanque gira para perseguir a un enemigo no lo hace inmediatamente, sino que gira a una velocidad determinada.
Si lo aplicamos al ejemplo anterior:
  • Modificamos el script UD2_Mov_Rotac_6B
 1 using UnityEngine;
 2 
 3 /** Ejemplo de script que muestra un ejemplo de uso del
 4  *   método Quaternion.LookRotation
 5  *  
 6  */
 7 public class UD2_Mov_Rotac_6B : MonoBehaviour
 8 {
 9     [SerializeField]
10     private Transform posicionRotar;
11 
12     [SerializeField]
13     private float velocidad;
14 
15     // Update is called once per frame
16     void Update()
17     {
18         Vector3 dir = posicionRotar.position - transform.position;     // No hace falta normalizar el vector3 ya que no lo vamos a usar para movernos
19         Quaternion quaternion = Quaternion.LookRotation(dir, Vector3.up);
20         transform.rotation = Quaternion.Lerp(transform.rotation,quaternion,velocidad*Time.deltaTime);
21 
22 
23     }
24 }
  • Línea 13: Definimos la velocidad a la que el cañón va a rotar siguiendo la esfera.
  • Línea 20: Creamos un objeto Quaternion con la función Lerp, la cual irá rotando desde la posición del cañón a la posición rotada que apunta a la esfera.


Unity3d mov rotac 10.jpg

Método LookAt

Rota un GameObject pero siempre va a rotar el eje 'forward' (el eje de color azul en Unity). No podemos indicar que rote otro eje. El eje 'forward' siempre va a mirar al enemigo que queramos perseguir.
Normalmente se suele utilizar para que la cámara mire al protagonista cuando lo va siguiendo.
Cualquier rotación que tengamos la va a obviar y lo único que hará será rotar el objeto de acuerdo al 'vector up' que le digamos (por defecto es el eje Y).
El 'vector up' es un vector dirección que normalmente se asocia a cámaras.
[Imagen:fig3g7.jpg]
Imagen obtenida de macedoniamagazine.frodrig.com


Este vector sirve para 'rotar' la cámara (o un objeto en este caso). Imaginar que claváis un palo en una cámara y que la atraviesa de arriba-abajo (vector up). Ahora coger la cámara y moverla a izquierda-derecha hasta que apunte a objeto destino. Si el objeto destino no está en la línea de la cámara el palo sube-baja hasta situarlo en la misma. En Unity, el Vector up siempre es perpendicular en 90 grados al vector Forward, por lo que irá rotando el eje Y para que esté siempre a esa distancia.


  • Vamos a construir la siguiente escena (después podemos aplicar lo aprendido en el punto anterior):
Unity3d mov rotac 11.jpg
Nos vamos a la 'Asset Store' y descargamos un tanque gratuito (free).
Arrastramos dos tanques de la carpeta Models del tanque importado a la escena (puede ser necesario cambiar la escala del modelo, recuerda que es un parámetro que se cambia en el modelo de nombre Scale Factor).
Les cambiamos el nombre a los tanques y los colocamos como en la imagen.
Creamos un plano para dar la sensación de que haya un suelo.
Creamos el script UD_Movim_Rotac_7
 1 using UnityEngine;
 2 
 3 /**
 4  * Ejemplo de uso del método Transform.LookAt
 5  */
 6 public class UD2_Mov_Rotac_7 : MonoBehaviour {
 7 
 8     [SerializeField]
 9     private Transform tanqueObjetivo;
10 
11 	
12 	// Update is called once per frame
13 	void Update () {
14 
15         transform.LookAt(tanqueObjetivo);
16 
17 	}
18 }
Como vemos el código es mucho más sencillo.
  • Línea 9: Necesitamos saber cual es la posición del tanque que queremos perseguir, por lo que creamos un objeto de la clase Transform que sea público.
  • Línea 15: Vamos a hacer que la torreta del tanque mire siempre hacia el tanque objetivo.


  • Ahora colocamos el script en la torreta del tanque atacante:


  • Si ejecutáis el juego puede ser que os llevéis una desagradable sorpresa :(
Unity3d mov rotac 14.jpg
La torreta está rotada !!!!


Esto es debido a que el método LookAt siempre va a 'mirar' con el eje forward (el de color azul) y resulta que el modelo importado, sus ejes por defecto, están rotados y no se corresponden con los ejes x-y-z de Unity:
Unity3d mov rotac 15.jpg
Si tenéis suerte y el modelo importado está correcto, no tendréis que hacer nada, pero de todas formas leeros lo siguiente por si os paso con otro modelo...
  • Podemos hacer varias cosas para solucionarlo:
  • Editar el dibujo 3D y cambiar su rotación para hacer que la posición se corresponda con la posición de los ejes de Unity.
  • Hacer uso de un GameObject vacío. Hacer la torreta 'hijo' de este GameObject y llevar el script de rotación al GameObject vacío.
Vamos a hacer esto segundo.



  • Si ejecutáis el juego ahora sí que funciona :)
Unity3d mov rotac 22.jpg


  • Nota: Recordar que 'mirar hacia algo' no implica hacer lo mismo que el objeto al que se está mirando. Así, el objeto podría rotar 360 grados y nosotros quedaríamos igual mirando hacia el mismo.
  • Como ejercicio propuesto podríais intentar hacer rotar el tanque en vez de la torreta.
Pista: Podéis rotar los componentes del tanque (torreta y cuerpo) para que coincida con la orientación de los ejes de Unity.


Programando la Escala

  • Para modificar la escala debemos de hacer uso de la propiedad 'localscale' del transform asociado como ya vimos al principio de esta entrada.
Para modificar dicha escala podemos hacer uso del vector3: Vector3.one que representa el vector (1,1,1).
Así podríamos poner: transform.localscale = Vector3.one * 5; (estaríamos asignando una escala de 5 unidades al GameObject).



Método Scale

Vamos a aplicarlo a los GameObject.


  • Creamos una esfera en al punto (0,0,0) con la cámara mirando hacia ella a cierta distancia.
Creamos el script de nombre UD_Movim_Escala_1.
 1 public class UD_Movim_Escala_1 : MonoBehaviour {
 2 
 3     public Vector3 nuevaEscala = new Vector3(2, 2, 2);  // Multiplica la escala por estos valores
 4     private Vector3 reducirEscala;
 5 
 6 	// Use this for initialization
 7      void Start () {
 8 
 9         reducirEscala = new Vector3(1/nuevaEscala.x,1/nuevaEscala.y,1/nuevaEscala.z);
10      }
11 	
12      // Update is called once per frame
13      void Update () {
14 
15         if (Input.GetKeyDown(KeyCode.UpArrow))
16         {
17             transform.localScale = Vector3.Scale(transform.localScale, nuevaEscala);
18 
19         }
20         if (Input.GetKeyDown(KeyCode.DownArrow))
21         {
22             transform.localScale = Vector3.Scale(transform.localScale, reducirEscala);
23 
24         }
25 
26     }
27 }
Lo que hace el script es definir dos vectores:
  • Uno public de nombre 'nuevaEscala' (línea 3) que multiplicará la escala local del GameObject por estos valores en caso de querer aumentar la escala.
  • Una private de nombre 'reducirEscala' (línea 4 y línea 9) que es un Vector3 invertido al anterior, para el caso de querer reducir la escala.
Después en función de si presionamos la tecla 'cursor arriba' o 'cursor abajo' modifica la escala del GameObject asociado.


  • Asocia el script anterior a la esfera creada.
Ejecuta el juego y comprueba como cambia la escala:


Nota: Recordar que cuando vimos el punto del 'rendimiento' indicamos que siempre que se pueda se deben de quitar instrucciones del método Update. Por eso la instanciación del Vector3 'reducirEscala' está en el método Start().



Constraint

  • Las constraint son componentes que se añaden a un GameObject y sirven para 'enlazar' las propiedades de posición, escala y rotación de un GameObject con otros, de tal forma que la modificación en los valores de cualquiera de las propiedades anteriores, modifica las mismas propiedades en los GameObjects enlazados.



Métodos útiles


Basado en esta operación tenemos varias variantes:
  • LerpAngle: Hace lo mismo que el anterior pero sus 'valores' son interpretados como ángulos entre 0 y 360 grados.
  • InverseLerp: Devuelve el 'porcentaje' entre los valores [0-1] en que se encuentra un punto con respecto a dos dados, siendo el valor 0 el punto inicial, y el valor 1 el punto final.






-- Ángel D. Fernández González -- (2018).