Diferencia entre revisiones de «Unity Animaciones»

De MediaWiki
Ir a la navegación Ir a la búsqueda
 
(No se muestran 29 ediciones intermedias de 2 usuarios)
Línea 86: Línea 86:
  
  
<gallery caption="Importando modelos animados a Unity" widths="350" heights="300px" perrow="2">
+
<gallery caption="Exportando modelos y animaciones de Maximo" widths="350" heights="300px" perrow="2">
 
Image:Unity3d_animacion_1A.jpg| Vamos a alguna página de descarga de modelos 3D. En el ejemplo es https://www.turbosquid.com. Al elegir el modelo mejor es que tenga un rigged asociado. En caso contrario, Mixamo puede intentar crear uno, pero algunas veces no funciona. Depende del modelo.
 
Image:Unity3d_animacion_1A.jpg| Vamos a alguna página de descarga de modelos 3D. En el ejemplo es https://www.turbosquid.com. Al elegir el modelo mejor es que tenga un rigged asociado. En caso contrario, Mixamo puede intentar crear uno, pero algunas veces no funciona. Depende del modelo.
 
Image:Unity3d_animacion_1C.jpg| Descargamos el archivo. Preferiblemente que se '''.fbx''', aunque Mixamo admite .obj.
 
Image:Unity3d_animacion_1C.jpg| Descargamos el archivo. Preferiblemente que se '''.fbx''', aunque Mixamo admite .obj.
Línea 96: Línea 96:
 
Image:Unity3d_animacion_6.jpg| Escogemos ahora otra animación diferente (walking).
 
Image:Unity3d_animacion_6.jpg| Escogemos ahora otra animación diferente (walking).
 
Image:Unity3d_animacion_7.jpg| Como el modelo 3D lo vamos a mover nosotros desde Unity, escogemos la opción de '''In Place'''. Podemos ajustar el resto de parámetros a nuestras necesidades.
 
Image:Unity3d_animacion_7.jpg| Como el modelo 3D lo vamos a mover nosotros desde Unity, escogemos la opción de '''In Place'''. Podemos ajustar el resto de parámetros a nuestras necesidades.
Image:Unity3d_animacion_8.jpg| Como el modelo ya lo tenemos descargado con la animación anterior, esta vez solo descargamos la animación por lo que escogemos la opción '''Without Skin'''.
+
Image:Unity3d_animacion_8.jpg| Como el modelo ya lo tenemos descargado con la animación anterior, esta vez solo descargamos la animación por lo que escogemos la opción '''Without Skin'''. '''Nota:''' Algunas veces, cuando descargamos una animación 'without skin' también descarga el 'avatar' (explicado a continuación). En ese caso debemos de dejar la opción de 'Create from this Model'.
 
Image:Unity3d_animacion_9.jpg| Escogemos otra animación, esta vez la de '''Zombie Death'''.
 
Image:Unity3d_animacion_9.jpg| Escogemos otra animación, esta vez la de '''Zombie Death'''.
 
Image:Unity3d_animacion_10.jpg| Como en el caso anterior solo descargamos la animación por lo que escogemos la opción '''Without Skin'''.
 
Image:Unity3d_animacion_10.jpg| Como en el caso anterior solo descargamos la animación por lo que escogemos la opción '''Without Skin'''.
 
Image:Unity3d_animacion_11.jpg| Escogemos otra animación, esta vez la de '''Zombie Atack'''.
 
Image:Unity3d_animacion_11.jpg| Escogemos otra animación, esta vez la de '''Zombie Atack'''.
 
Image:Unity3d_animacion_12.jpg| Como en el caso anterior solo descargamos la animación por lo que escogemos la opción '''Without Skin'''.
 
Image:Unity3d_animacion_12.jpg| Como en el caso anterior solo descargamos la animación por lo que escogemos la opción '''Without Skin'''.
 +
</gallery>
 +
 +
 +
 +
<br />
 +
* Ahora los importamos a Unity:
 +
<gallery caption="Importando modelos animados a Unity" widths="350" heights="300px" perrow="2">
 
Image:Unity3d_animacion_12A.jpg| Ahora debemos importar los archivos descargados a Unity. Para ello los podemos arrastrar desde un explorador de archivos a la carpeta '''Assets''' o podemos ir por el menú principal '''Assets''' => '''Import new assets'''.
 
Image:Unity3d_animacion_12A.jpg| Ahora debemos importar los archivos descargados a Unity. Para ello los podemos arrastrar desde un explorador de archivos a la carpeta '''Assets''' o podemos ir por el menú principal '''Assets''' => '''Import new assets'''.
 
Image:Unity3d_animacion_13B.jpg| Ahora '''seleccionando los cuatro modelos''' desmarcamos las opciones indicadas en la sección '''Model''' de la ventana Inspector.
 
Image:Unity3d_animacion_13B.jpg| Ahora '''seleccionando los cuatro modelos''' desmarcamos las opciones indicadas en la sección '''Model''' de la ventana Inspector.
 
Image:Unity3d_animacion_13C.jpg| Pulsamos el botón '''Apply''' para guardar los cambios.
 
Image:Unity3d_animacion_13C.jpg| Pulsamos el botón '''Apply''' para guardar los cambios.
 +
</gallery>
 +
 +
 +
<br />
 +
* En este punto ya disponemos de 4 modelos con 4 animaciones ('''Clips''').
 +
[[Imagen:Unity3d_animacion_19A3.jpg|800px|center]]
 +
 +
 +
: Fijarse que solo en una de ellas se previsualiza el modelo.
 +
: Si desplegáis el contenido de cada modelo (presionando la flecha hacia la derecha en cada uno) podéis ver como en todos ellos disponemos de un 'Clip' que viene a ser la animación asociada al mismo.
 +
[[Imagen:Unity3d_animacion_19A4B.jpg|1000px|center]] y un avatar.
 +
 +
* Podemos comprobar como en Zombie Idle se encuentra toda la información del modelo (texturas, materiales, animación, avatar y mesh).
 +
: En el resto de animaciones solo tenemos el avatar y la animación ya que las hemos descargado sin el Skin.
 +
 +
 +
 +
 +
<br />
 +
* El siguiente paso es configurar los modelos importados con las animaciones para indicar que son animaciones de tipo 'Humanoide'.
 +
: Para ello es necesario tener al menos en Avatar.
 +
: En el avatar es donde se guarda la información del esqueleto y las partes en las que se divide el modelo para poder realizar la animación. Estas partes son la cabeza, columna, brazos, piernas, manos,...
 +
 +
<gallery caption="Configurando modelos animados en Unity" widths="350" heights="300px" perrow="2">
 
Image:Unity3d_animacion_13.jpg| Ahora escogemos el modelo con la animación (Zombie Idle) y en la sección '''Rig''' escogemos como 'Animation Type' => '''Humanoid'''. Como 'Avatar Definition' => '''Create from this Model'''. Marcamos la opción '''optimized Game Object''' y pulsamos el botón '''Apply'''.
 
Image:Unity3d_animacion_13.jpg| Ahora escogemos el modelo con la animación (Zombie Idle) y en la sección '''Rig''' escogemos como 'Animation Type' => '''Humanoid'''. Como 'Avatar Definition' => '''Create from this Model'''. Marcamos la opción '''optimized Game Object''' y pulsamos el botón '''Apply'''.
Image:Unity3d_animacion_15.jpg| Con el Zombie Idle seleccionado, podemos ir a la sección '''Animation''' de la ventana Inspector. En ella podemos ver que dicho modelo tiene asociado '''un Clip''' que es la animación descargada. Podemos ver como es dicha animación en la parte baja de la ventana Inspector. Puede suceder que un modelo tenga guardadas varias animaciones y por tanto aparecería una lista de Clips. También es posible desde Unity 'trocear' un Clip en varias partes.
+
Image:Unity3d_animacion_15.jpg| Con el Zombie Idle seleccionado, podemos ir a la sección '''Animation''' de la ventana Inspector. En ella podemos ver que dicho modelo tiene asociado '''un Clip''' que es la animación descargada. Podemos ver como es dicha animación en la parte baja de la ventana Inspector. Puede suceder que un modelo tenga guardadas varias animaciones y por tanto aparecería una lista de Clips. También es posible desde Unity 'trocear' un Clip en varias partes. Recordar que si la versión de Unity que tenéis instalada no visualiza correctamente el zombie (aparece sin texturas) debéis ir a la sección 'Materials' y extraer las texturas y los materiales.
Image:Unity3d_animacion_16.jpg| Ahora seleccionamos otro de los modelos, por ejemplo 'Zombie Atack'. Si recordáis, cuando descargamos el modelo de Mixamo escogimos la opción '''Without Skin'''. Es por eso que ahora tenemos que informar a Unity que este modelo va a hacer uso de los datos del modelo 'Zombie Idle'. Recordar pulsar el botón '''Apply'''.
+
Image:Unity3d_animacion_16B.jpg| Ahora seleccionamos otro de los modelos, por ejemplo 'Zombie Atack' y repetimos la operación. Fijarse que podemos indicar la opción 'Create from this model' debido a que disponemos del avatar asociado. Si no fuera el caso, tendríamos que escoger la otra opción ('Copy from other avatar') y seleccionar el avatar asociado de otro modelo. Esto lo podremos hacer siembre que el avatar, que es el que el esqueleto, tenga todas las partes necesarias para poder aplicar la animación.
Image:Unity3d_animacion_17.jpg| Hacemos lo mismo con el modelo '''Zombie Death'''.
+
Image:Unity3d_animacion_17B.jpg| Hacemos lo mismo con el modelo '''Zombie Death'''.
Image:Unity3d_animacion_18.jpg| Hacemos lo mismo con el modelo '''Zombie Walking'''.
+
Image:Unity3d_animacion_18B.jpg| Hacemos lo mismo con el modelo '''Zombie Walking'''.
 +
 
 
</gallery>
 
</gallery>
  
  
  
 
+
<br />
 
* Indicar que los nombres de los Clips cuando lo descargamos de la web de Mixamo son siembre '''mixamo.com'''.
 
* Indicar que los nombres de los Clips cuando lo descargamos de la web de Mixamo son siembre '''mixamo.com'''.
 
: Aunque en el ejemplo que viene a continuación no están cambiados, para realizar operaciones posteriores va a ser necesario que tengan un nombre significativo.
 
: Aunque en el ejemplo que viene a continuación no están cambiados, para realizar operaciones posteriores va a ser necesario que tengan un nombre significativo.
Línea 119: Línea 151:
  
 
[[Imagen:Unity3d_animacion_62.jpg|600px|center]]
 
[[Imagen:Unity3d_animacion_62.jpg|600px|center]]
 +
 +
  
 
<br />
 
<br />
Línea 128: Línea 162:
 
==Creando un Animator Controller. Animación inicial==
 
==Creando un Animator Controller. Animación inicial==
  
* En este punto ya disponemos de 4 modelos con 4 animaciones ('''Clips''').
+
* En este punto ya tenemos las diferentes animaciones (clips) añadidas al proyecto de Unity.
[[Imagen:Unity3d_animacion_19A3.jpg|800px|center]]
 
 
 
 
 
: Fijarse que solo en una de ellas se previsualiza el modelo.
 
: Si desplegáis el contenido de cada modelo (presionando la flecha hacia la derecha en cada uno) podéis ver como en todos ellos disponemos de un 'Clip' que viene a ser la animación asociada al mismo.
 
[[Imagen:Unity3d_animacion_19A4.jpg|1000px|center]]
 
 
 
 
 
 
 
 
: Ahora es necesario gestionar dichas animaciones. Debemos de tener algún mecanismo que nos permite controlar cuando la figura 3D está en una animación o en otra.
 
: Ahora es necesario gestionar dichas animaciones. Debemos de tener algún mecanismo que nos permite controlar cuando la figura 3D está en una animación o en otra.
  
Línea 156: Línea 181:
 
* Al pulsar dos veces sobre el AnimationController (darle un nombre adecuado), accedemos a la máquina de estados:
 
* Al pulsar dos veces sobre el AnimationController (darle un nombre adecuado), accedemos a la máquina de estados:
 
<gallery caption="Gestionando un Animator Controller" widths="350" heights="300px" perrow="2">
 
<gallery caption="Gestionando un Animator Controller" widths="350" heights="300px" perrow="2">
Image:Unity3d_animacion_20.jpg| Como vemos aparece una serie de 'cajas' que indican estados. Por defecto tenemos tres estados: '''entry''' que indica el estado inicial cuando la figura 3D aparece en la escena, '''any state''' que representa cualquier estado y '''exit''' cuando queremos indicar que debe salir de la máquina de estados, pero lo que provoca es que vuelva al estado Entry. En la figura, estamos arrastrando <u>la animación del Zombie Idle al AnimationController. Al hacerlo se crea un nuevo estado y aparece una flecha que va desde el estado 'Entry' al estado 'mixamo_com' que es la animación del zombie estando idle.
+
Image:Unity3d_animacion_20.jpg| Como vemos aparece una serie de 'cajas' que indican estados. Por defecto tenemos tres estados: '''entry''' que indica el estado inicial cuando la figura 3D aparece en la escena, '''any state''' que representa cualquier estado y '''exit''' cuando queremos indicar que debe salir de la máquina de estados, pero lo que provoca es que vuelva al estado Entry. En la figura, estamos arrastrando <u>la animación del Zombie Idle al AnimationController</u>. Al hacerlo se crea un nuevo estado y aparece una flecha que va desde el estado 'Entry' al estado 'mixamo_com' que es la animación del zombie estando idle.
 
Image:Unity3d_animacion_21.jpg| Vamos a cambiar el nombre de la animación por algo más significativo.
 
Image:Unity3d_animacion_21.jpg| Vamos a cambiar el nombre de la animación por algo más significativo.
 
Image:Unity3d_animacion_22.jpg| Ahora arrastramos el modelo 'Zombie Idle' a nuestra escena (con ello se crea un nuevo GameObject) y <u>después arrastramos el Animator Controller a la propiedad del GameObject que hemos creado</u>.
 
Image:Unity3d_animacion_22.jpg| Ahora arrastramos el modelo 'Zombie Idle' a nuestra escena (con ello se crea un nuevo GameObject) y <u>después arrastramos el Animator Controller a la propiedad del GameObject que hemos creado</u>.
Línea 184: Línea 209:
 
Image:Unity3d_animacion_36.jpg| Por último tenemos que asociar el AnimatorController al GameObject creado en la escena. Seleccionamos el GameObject Zombie Girl de la Hierarchy Window y arrastramos el AnimatorController a la propiedad Controller del Component Animator.
 
Image:Unity3d_animacion_36.jpg| Por último tenemos que asociar el AnimatorController al GameObject creado en la escena. Seleccionamos el GameObject Zombie Girl de la Hierarchy Window y arrastramos el AnimatorController a la propiedad Controller del Component Animator.
 
Image:Unity3d_animacion_37.jpg| Podemos comprobar si ejecutamos el juego, como la ZombieGirl está atacando por defecto.
 
Image:Unity3d_animacion_37.jpg| Podemos comprobar si ejecutamos el juego, como la ZombieGirl está atacando por defecto.
 +
</gallery>
 +
 +
 +
 +
<br />
 +
===Caso especial: Modelo con un RIG diferente===
 +
 +
* Imaginemos que tenemos descargadas todas las animaciones anteriores y queremos aplicarlas a otro modelo diferente.
 +
: Veremos a continuación como hacerlo, pero para que funcione necesitamos que el modelo nuevo tenga 'las mismas partes' que el modelo original.
 +
 +
: Mirar el siguiente modelo de Mixamo:
 +
[[Imagen:Unity3d_animacion_63.jpg|500px|center]]
 +
 +
: ¿ Notáis que falta algo ? :)
 +
 +
* Realizar los siguientes pasos:
 +
:* Descargar dicho modelo (sin animaciones asocidas), en posición T.
 +
:* Importarlo a Unity.
 +
:* Ir a la sección Model y desmarcar las opciones indicadas anteriormente (recordar presionar el botón Apply).
 +
:* Si no veis correctamente el modelo recordar extraer los materiales y texturas en la sección 'Materials'.
 +
 +
:* Ir a la sección RIG e indicar que el modelo es de tipo '''Humanoid''' y el Avatar dejar la opción '''Create from this model'''. Os aparecerá un error al presionar el botón '''Apply'''.
 +
:: Ese error indica que falta el brazo derecho y que no tiene transform asociado.
 +
[[Imagen:Unity3d_animacion_64.jpg|500px|center]]
 +
 +
* Lo que hacen los avatares es estar divididos en partes (manos, pies, tronco cabeza,...) y asociar a cada parte un 'transform' el cual puede ser modificado para 'producir las animaciones'.
 +
: En este caso, el avatar no tiene el brazo derecho (lógico, para qué lo necesita el zombie :) )
 +
 +
* Para solucionar vamos a modificar el avatar y decirle que haga uso de transforms en esas partes, pero que no van a  'dibujar' nada.
 +
<gallery caption="Modificando el RIG de un Avatar" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_animacion_65.jpg| Pulsamos el botón '''Configura''' del Avatar.
 +
Image:Unity3d_animacion_66.jpg| Como vemos está marcado en rojo las partes del avatar que no vienen con el modelo. Los círculos que se dibujan con línea continua indican que es obligatorio que esas partes estén presentes (las de círculos a puntos indican que son optativas). En la parte izquierda aparece en forma de árbol todo el esqueleto con cada una de las partes.
 +
Image:Unity3d_animacion_67.jpg| Si pulsamos sobre la parte que no está presente podemos ver en la parte baja que '''no tiene transforma asociado'''.
 +
Image:Unity3d_animacion_68.jpg| Si pulsamos en la Hierarchy Window donde debería estar la parte que falta, vemos que marca el objeto transform, pero que no tiene ninguna parte del modelo 3D asociado.
 +
Image:Unity3d_animacion_69.jpg| Lo que tenemos que hacer es 'arrastrar' o 'seleccionar' este 'transform' a la propiedad que tiene el transform a none.
 +
Image:Unity3d_animacion_70.jpg| Lo mismo lo hacemos con la mano (RightHand). Podemos comprobar como el avatar ya está todo en verde.
 +
Image:Unity3d_animacion_71.jpg| Presionamos el botón '''Apply''' y '''Done''' para cerrar la ventana de diseño del Avatar. Ya podemos aplicar las animaciones anteriores a este modelo.
 
</gallery>
 
</gallery>
 +
 +
  
  
Línea 190: Línea 254:
  
 
<br />
 
<br />
 +
 
==Analizando la máquina de estados==
 
==Analizando la máquina de estados==
  
Línea 283: Línea 348:
  
 
* Ahora editamos el código del script.
 
* Ahora editamos el código del script.
<syntaxhighlight lang="java" line enclose="div" highlight="11,20">
+
<syntaxhighlight lang="java" line enclose="div" highlight="11,24">
 
using UnityEngine;
 
using UnityEngine;
  
Línea 289: Línea 354:
  
 
     [SerializeField]
 
     [SerializeField]
     public float velocidadMaxima;
+
     private float velocidadMaxima;
 
     [SerializeField]
 
     [SerializeField]
     public int vida;
+
     private int vida;
  
 
     private float velocidadActual = 0f;
 
     private float velocidadActual = 0f;
Línea 302: Línea 367:
 
         vida = 5;
 
         vida = 5;
 
         velocidadActual = 0;
 
         velocidadActual = 0;
 
 
     }
 
     }
  
Línea 366: Línea 430:
 
     }
 
     }
  
    // Update is called once per frame
 
    void Update() {
 
 
        transform.Translate(transform.forward * velocidadActual * Time.deltaTime);
 
 
    }
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
[[Imagen:Unity3d_animacion_53.jpg|700px|center]]
 
[[Imagen:Unity3d_animacion_53.jpg|700px|center]]
 
<center><small>El zombie se mueve...</small></center>
 
<center><small>El zombie se mueve...</small></center>
 
  
  
Línea 395: Línea 452:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
* '''Nota:''' Otra opción más fácil sería pasarle directamente al parámetro velocidad del animator, la velocidad del GameObject: animator.SetFloat("velocidad", velocidad)
  
  
Línea 404: Línea 462:
  
 
     [SerializeField]
 
     [SerializeField]
     public float velocidadMaxima;
+
     private float velocidadMaxima;
 
     [SerializeField]
 
     [SerializeField]
     public int vida;
+
     private int vida;
  
 
     private float velocidadActual = 0f;
 
     private float velocidadActual = 0f;
Línea 449: Línea 507:
 
:* Líneas 24-33: Dibuja el Slider y en función de su valor ajusta la velocidad del zombie
 
:* Líneas 24-33: Dibuja el Slider y en función de su valor ajusta la velocidad del zombie
 
::* Línea 16: Dibuja un label con texto speed.
 
::* Línea 16: Dibuja un label con texto speed.
::* Línea 19: Dibuja un Horizontal Slider con un rango entre 0.0f y velocidadMaxima. Devuelve el valor seleccionado gráficamente.
+
::* Línea 19: Dibuja un Horizontal Slider con un rango entre 0.0f y velocidadMaxima. Devuelve el valor seleccionado gráficamente.  
 
::* Línea 31: Asignamos la velocidad para la función translate del transform.
 
::* Línea 31: Asignamos la velocidad para la función translate del transform.
 
::* Línea 32: Cambiamos la velocidad del parámetro. No importa su valor, solo si es mayor que 0.1f
 
::* Línea 32: Cambiamos la velocidad del parámetro. No importa su valor, solo si es mayor que 0.1f
Línea 472: Línea 530:
 
         velocidadActual = m_MySliderValue;
 
         velocidadActual = m_MySliderValue;
 
         animator.SetFloat("velocidad", velocidadActual);
 
         animator.SetFloat("velocidad", velocidadActual);
         animator.speed = m_MySliderValue;
+
         animator.speed = (m_MySliderValue == 0) ? 1 : m_MySliderValue; // Al iniciar la ejecución el Slider vale 0. La velocidad de la animación no puede valer 0 ya que no se ejecutaría y por lo tanto no cambiaría de estado.
 
     }
 
     }
  
Línea 499: Línea 557:
 
             velocidadActual = m_MySliderValue;
 
             velocidadActual = m_MySliderValue;
 
             animator.SetFloat("velocidad", velocidadActual);
 
             animator.SetFloat("velocidad", velocidadActual);
             animator.speed = m_MySliderValue;
+
             animator.speed = (m_MySliderValue == 0) ? 1 : m_MySliderValue; // Al iniciar la ejecución el Slider vale 0. La velocidad de la animación no puede valer 0 ya que no se ejecutaría y por lo tanto no cambiaría de estado.
 
         }
 
         }
 
     }
 
     }
Línea 544: Línea 602:
  
 
* Ahora solo queda hacer que el zombie desaparezca.
 
* Ahora solo queda hacer que el zombie desaparezca.
: Para ello hay que hacer uso del [https://docs.unity3d.com/ScriptReference/Animator.GetCurrentAnimatorStateInfo.html método GetCurrentAnimatorStateInfo(int layerIndex);] el cual devuelve un objeto de la [https://docs.unity3d.com/ScriptReference/AnimatorStateInfo.html clase AnimatorStateInfo] y que nos da información sobre la animación en curso.
+
: Aquí tenemos dos posibilidades:
 +
:* Hacer un Destroy(gameobjecto, tiempo_en_segundos) y ponemos como tiempo el tiempo que tarda la animación de morir.
 +
:* Hacer uso del [https://docs.unity3d.com/ScriptReference/Animator.GetCurrentAnimatorStateInfo.html método GetCurrentAnimatorStateInfo(int layerIndex);] el cual devuelve un objeto de la [https://docs.unity3d.com/ScriptReference/AnimatorStateInfo.html clase AnimatorStateInfo] y que nos da información sobre la animación en curso.
 
:* Una de las propiedades es [https://docs.unity3d.com/ScriptReference/AnimatorStateInfo-normalizedTime.html normalizedTime] el cual devuelve un valor entre 0-1 indicando cuando llega a 1 que la animación terminó.
 
:* Una de las propiedades es [https://docs.unity3d.com/ScriptReference/AnimatorStateInfo-normalizedTime.html normalizedTime] el cual devuelve un valor entre 0-1 indicando cuando llega a 1 que la animación terminó.
 
:* Uno de sus métodos es [https://docs.unity3d.com/ScriptReference/AnimatorStateInfo.IsName.html isName(String Nombre_animación)] el cual devuelve un true/false si el animator está ejecutando la animación indicada en el parámetro.
 
:* Uno de sus métodos es [https://docs.unity3d.com/ScriptReference/AnimatorStateInfo.IsName.html isName(String Nombre_animación)] el cual devuelve un true/false si el animator está ejecutando la animación indicada en el parámetro.
Línea 598: Línea 658:
 
:* animator.GetCurrentAnimatorStateInfo(0).IsName("Zombie_Walk"): Devuelve true si el zombie se encuentra en la animación Zombie_Walk.
 
:* animator.GetCurrentAnimatorStateInfo(0).IsName("Zombie_Walk"): Devuelve true si el zombie se encuentra en la animación Zombie_Walk.
 
:: El (0) hace referencia a las capas, las cuales se usan para animar de forma diferente a diferentes partes del cuerpo.
 
:: El (0) hace referencia a las capas, las cuales se usan para animar de forma diferente a diferentes partes del cuerpo.
 +
 +
  
 
<br />
 
<br />
  
==Opciones no vistas==
+
==Parámetro Root Motion==
  
* [https://docs.unity3d.com/Manual/class-BlendTree.html Blend Tree]
+
* Más información:
 +
:* https://docs.unity3d.com/Manual/RootMotion.html
 +
:* https://docs.unity3d.com/Manual/AnimationRootMotionNodeOnImportedClips.html
 +
 
 +
 
 +
 
 +
[[Imagen:Unity3d_animacion_61b.jpg|600px|center]]
 +
 
 +
 
 +
* Este parámetro va a determinar si la animación 'mueve' el GameObject en nuestro mundo 3D.
 +
: Esto quiere decir que la animación produce un movimiento que hará que los ejes X,Y,Z del gameobject se modifiquen...
 +
: Si queremos que dicha animación no afecte a los ejes, no marcaremos esta opción.
 +
: Un ejemplo lo tenemos en la animación de la muerte. Al morir el Zombie 'cae' al suelo por la propia animación. Deberíamos marcar esta opción si queremos que se modifique la posición del zombie al morir. Sino, tendríamos que programarlo nosotros.
 +
 
 +
 
 +
* Esto es necesario si hacemos uso de un NavMesh Agent, el cual sirve para calcular rutas para llegar a un objetivo esquivando obstáculos que pueda haber por el camino.
 +
: Un ejemplo de uso podría ser un laberinto en el que queremos que los enemigos se muevan por el mismo...
 +
: Veremos el uso del componente NavMesh en [https://wiki.cifprodolfoucha.es/index.php?title=Unity_NavMesh otra sección de esta Wiki].
 +
 
 +
 
 +
 
 +
 
 +
 
 +
<br />
 +
 
 +
==Controlando el tiempo de la animación==
 +
 
 +
* Más información en [https://docs.unity3d.com/Manual/AnimationEventsOnImportedClips.html este enlace].
 +
 
 +
 
 +
* Como vimos anteriormente, podemos saber cual es la animación que se está ejecutando en cada momento:
 +
:: animator.GetCurrentAnimatorStateInfo(0).IsName("<b>Zombie_Death</b>") , siendo 'Zombie_Death' el nombre de la animación.
 +
 
 +
* También podemos controlar en que momento de la animación nos encontramos con el siguiente código:
 +
:: animator.GetCurrentAnimatorStateInfo(0).normalizedTime  , esta propiedad devuelve un valor entre 0-1 expresando el porcentaje de la animación que ya ha sido mostrada (1 => 100%).
 +
 
 +
 
 +
 
 +
* A nivel gráfico también podemos asociar una función a las animaciones de un modelo:
 +
<gallery caption="Añadiendo una llamada a una función a una animación" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_animacion_61c.jpg| Seleccionamos el modelo en el cual se encuentran las animaciones.
 +
Image:Unity3d_animacion_61d.jpg| Con la animación seleccionada, nos desplazamos hacia abajo en la ventana Inspector y desplegamos la sección 'Events'. Dentro de esta pulsamos sobre el botón '+' para añadir una nueva llamada a una función.
 +
Image:Unity3d_animacion_61e.jpg| Indicamos en qué momento de la animación se va a llamar a la función (recordar que 1 es el 100% de la animación). Se indica el nombre de la función ('''es necesario que el GameObject que lleve asociado esta animación tenga un script con el nombre de dicha función'''). Es posible enviar un dato a dicha función en forma de parámetros (podemos enviar un valor float, int, de cadena u objeto). Si no ponemos nada, la función se puede definir sin parámetros.
 +
Image:Unity3d_animacion_61f.jpg| Ahora creamos la función que va a ser llamada dentro de un script asociado al zombie (en este ejemplo).
 +
</gallery>
 +
 
 +
 
 +
 
 +
<br />
 +
 
 +
==Asociando scrips a cada estado de la máquina de estados==
 +
 
 +
* Más información [https://docs.unity3d.com/ScriptReference/StateMachineBehaviour.html en este enlace].
 +
 
 +
 
 +
* Es posible asociar <u>a cada estado de la máquina de estados</u> un script que derive de la [https://docs.unity3d.com/ScriptReference/StateMachineBehaviour.html clase StateMachineBehaviour].
 +
: Al alcanzar la máquina de estados el estado en el que se encuentra asociado el script, empezará a ejecutar una serie de métodos asociados.
 +
:* [https://docs.unity3d.com/ScriptReference/StateMachineBehaviour.OnStateEnter.html onStateEnter]: Cuando se inicia la animación.
 +
:* [https://docs.unity3d.com/ScriptReference/StateMachineBehaviour.OnStateExit.html OnStateExit]: Cuando sale de la animación.
 +
:* [https://docs.unity3d.com/ScriptReference/StateMachineBehaviour.OnStateIK.html OnStateIK]: Se llama en cada frame para implementar [https://docs.unity3d.com/es/2018.4/Manual/InverseKinematics.html Kinematics Inversa].
 +
:* [https://docs.unity3d.com/ScriptReference/StateMachineBehaviour.OnStateMove.html OnStateMove]: Llamada antes de OnStateIK en cada frame. Empleada para modificar la posición de [https://docs.unity3d.com/es/2019.4/Manual/RootMotion.html root motion].
 +
:* [https://docs.unity3d.com/ScriptReference/StateMachineBehaviour.OnStateUpdate.html OnStateUpdate]: Llamada en cada frame excepto el primer y último frame entre el StateEnter y el StateExit.
 +
 
 +
: Podemos emplearlo para gestionar acciones que queremos que se ejecuten cuando la máquina de estados alcanza dicho estado.
 +
: Por ejemplo, cuando un enemigo detecta al protagonista y cambia la variable de la máquina de estados para que siga al protagonista, podemos hacer que el script que deriva de StateMachineBehaviour lo siga y cuando salga de la animación deje de seguirlo.
 +
: En el ejemplo anterior, esta lógica la tendríamos codificada en el script que gestiona al enemigo, verificando la máquina de estado y haciendo que el enemigo siga al protagonista.
 +
 
 +
: Para añadir este tipo de script simplemente tenemos que seleccionar un estado de la máquina de estado y presionar el botón '''Add Behavior''':
 +
[[Imagen:Unity3d_animacion_61g.jpg|500px|center]]
  
* [https://docs.unity3d.com/Manual/NestedStateMachines.html SubStates Machines]
 
  
* [https://docs.unity3d.com/Manual/AnimationLayers.html Layers]
 
  
 +
* Podéis consultar un ejemplo de uso [https://www.youtube.com/watch?v=dYi-i83sq5g en este vídeo].
  
 
<br />
 
<br />
  
=Gestionando las animaciones. Animatior Override Controller=
+
=Gestionando las animaciones. Animator Override Controller=
  
 +
* Más información en https://docs.unity3d.com/Manual/AnimatorOverrideController.html
 +
 +
 +
* Este control permite tener un Animator Controller basado en otro ya hecho, pero permitiendo cambiar las animaciones asociadas a cada uno de los estados.
 +
: Tendrá las mismas condiciones y variables que el Animator Controller del que se basa.
 +
 +
* Veamos un ejemplo:
 +
<gallery caption="Ejemplo de uso de Animator Override Controller" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_animacion_72.jpg| Descargamos de máximo la animación 'Zombie Crawl'. Sin modelo asociado y con la opción que se quede en el sitio marcada.
 +
Image:Unity3d_animacion_73.jpg| Desmarcamos las opciones vistas anteriormente y pulsamos '''Apply'''.
 +
Image:Unity3d_animacion_74.jpg| Vamos a la sección RIG y modificamos las mismas opciones que el los ejercicios anteriores.
 +
Image:Unity3d_animacion_74A.jpg| En la sección de Animation, cambiamos el nombre y hacemos que se repita. Debéis hacer scroll hasta abajo en las propiedades y presionar el botón '''Apply'''.
 +
Image:Unity3d_animacion_74B.jpg| Al igual que en el caso del Animator Controller creamos un nuevo '''Animator Override Controller''' de cualquiera de las formas vistas.
 +
Image:Unity3d_animacion_75.jpg| Al pulsar sobre el animator override controller, en la ventana Inspector aparecen sus propiedades. Debemos de arrastrar el Animator Controller del que se basa o seleccionarlo con el icono de rueda dentada que aparece a su lado.
 +
Image:Unity3d_animacion_76.jpg| En este caso solo tenemos una animación, pero para que fuese real, tendríamos un ataque desde el suelo (con otra animación) y una muerte desde el suelo (con otra animación). Lo que vamos a hacer es que cuando ande, esté parado o se mueva haga uso de la nueva animación descargada, y cuando se muera que utilice la animación del Animator Controller original. Para ello tenemos que arrastrar la animación descargada (el Clip) a las propiedades del Animator Override Controller
 +
Image:Unity3d_animacion_77.jpg| Ahora solo tenemos que asociar este Animator Override Controller a un GameObject. Recodar asociarle el script de control.
 +
</gallery>
  
  
Línea 621: Línea 767:
  
 
=Creando animaciones propias=
 
=Creando animaciones propias=
 +
 +
* En Unity es posible crear nuestras propias animaciones, es decir, <u>crear nuestros propios clips</u>.
 +
 +
* El proceso sería el siguiente:
 +
:* Añadir a lo que va a ser el Clip aquellos GameObjects que queramos animar.
 +
:* Dentro de cada GameObject añadimos aquellas propiedades que van sufrir modificaciones durante la animación.
 +
:: Podemos modificar el componente Transform, el componente Mesh Renderer, Mesh Collider, RigidBody e incluso activar o desactivar scripts todo de forma dinámica.
 +
:* Establecemos el tiempo que va a dura el Clip.
 +
:* Iniciamos la grabación del Clip y establecemos 'puntos de referencia'. En cada uno de esos puntos modificamos alguno de los valores de alguno de los componentes indicados anteriormente. Unity ya se encarga de, por medio de la interpolación' modificar los valores de los componentes (por ejemplo la velocidad, posición, rotación,...) para llegar al punto destino con los valores indicados por el usuario.
 +
 +
 +
<gallery caption="Creando una " widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_animac_propia_1.jpg| Descargamos un https://assetstore.unity.com/packages/3d/environments/sci-fi/sci-fi-door-21813
 +
modelo de puerta del Asset Store]. Este modelo ya trae animaciones pero vamos a crear una nueva propia.
 +
Image:Unity3d_animac_propia_0.jpg| Arrastramos el Prefab de la puerta a la escena y como queremos crear nuestra propia animación, borramos el componente 'Animation' de la parte 'door' de la puerta.
 +
Image:Unity3d_animac_propia_2.jpg| Ahora <u>debemos seleccionar el GameObject</u> sobre el que queramos crear la animación. Fijarse que el GameObject está formado por partes y que nosotros vamos a 'animar' la puerta (subirla) pero la animación la tenemos que asociar al Gameobject.
 +
Image:Unity3d_animac_propia_3.jpg| Aparece una ventana 'flotante' que podemos 'anclar' a cualquiera de las zonas de Unity. Como no hay animación creada, pulsamos el botón '''Create'''.
 +
Image:Unity3d_animac_propia_4.jpg| Indicamos el directorio donde se va a crear el archivo 'anim' que va a ser el que guarde la animación.
 +
Image:Unity3d_animac_propia_5.jpg| Al crearla vemos que se crean dos archivos en el directorio elegido. Uno es el '''clip''' donde se va a guardar la animación (como el visto en la sección anterior) y otro es un AnimatorController que también ya vimos (la máquina de estados). Además, cuando seleccionemos el GameObject sobre el que creamos la animación veremos que se 'activa' el botón que pode '''Add Property'''.
 +
Image:Unity3d_animac_propia_6.jpg| Ahora debemos elegir que componentes queremos animar. Indicar que podemos elegir '''cualquier componente''' que esté asociado al GameObject, incluso un script, el cual podremos activar-desactivar a lo largo de la animación. En nuestro ejemplo, queremos animar la parte 'door' del GameObject Door_Prefab. Desplegamos dicho gameobject y podemos ver los componentes que podemos cambiar dinámicamente a lo largo de la animación.
 +
Image:Unity3d_animac_propia_7.jpg| Añadimos el componente '''transform => position'''.
 +
Image:Unity3d_animac_propia_8.jpg| Al hacerlo se añaden unas marcas denominadas '''keyframes''' que son las marcas que indican 'de que segundo a que segundo' se van a registrar las modificaciones. En nuestro caso vamos a hacer que la animación dure 5 segundos, por lo que estando situados en la parte derecha de la ventana 'Animation' giramos la rueda del ratón hasta que aparezcan 5 segundos.
 +
Image:Unity3d_animac_propia_9.jpg| Seleccionamos los dos KeyFrames y los arrastramos hasta el segundo 5 (también podemos eliminarlos con la tecla suprimir y pulsando sobre el segundo 5 (aparece una línea blanca) pulsa el botón 'Add KeyFrame' que se encuentra en la parte izquierda al lado de Samples 60).
 +
</gallery>
 +
 +
 +
* Ahora llega el momento de grabar la animación.
 +
<gallery caption="Grabando la animación" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_animac_propia_10.jpg| '''Pulsamos el botón de grabar''' y vemos que aparece una barra de color rojo sobre el tiempo. Ahora vamos a pulsa sobre esa barra en el segundo 1:30 más o menos. Al hacerlo, aparece una línea blanca. Vamos a mover la puerta hacia arriba hasta la mitad del recorrido.
 +
Image:Unity3d_animac_propia_11.jpg| Al hacerlo vemos que se crea una nueva marca de keyframe sobre ese segundo. Lo que hace Unity es guardar en ese punto la posición de la puerta y después mediante interpolación mueve la puerta desde la posición inicial a esa marca.
 +
Image:Unity3d_animac_propia_12.jpg| Ahora pulsamos sobre la marca de tiempo 5 segundos (el final del clip) y subimos la puerta de todo. Con esto tenemos finalizado la animación, podemos parar de grabar pulsando el botón de grabación otra vez. Aparecerá una banda de color azul en el tiempo al igual que la propiedad position del componente transform de la puerta.
 +
Image:Unity3d_animac_propia_13.jpg| Si ahora pulsamos el botón de 'Play' podemos ver como es la animación.
 +
</gallery>
 +
 +
 +
 +
* Si ejecutamos el juego podemos ver como la animación se pone en marcha.
 +
: Esto es así ya que se ha creado un AnimatorController y que ha sido asociado al GameObject puerta al crear la animación.
 +
: Podemos tratarla como cualquier otra animación como hicimos en el punto anterior.
 +
[[Imagen:Unity3d_animac_propia_14.jpg|500px|center]]
 +
 +
 +
 +
* Vamos a realizar una modificación para hacer que la escala de la puerta decrezca y que así no 'salga' por la parte de arriba.
 +
: Normalmente esto no sería necesario ya que si el juego es en primera persona, la puerta no se abriría hasta estar cerca de la misma y por tanto no veríamos la parte de arriba.
 +
<gallery caption="Modificando la animación" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_animac_propia_15.jpg| Añadimos la propiedad 'scale' del '''door'''.
 +
Image:Unity3d_animac_propia_16.jpg| <u>Pulsamos el botón grabar</u> y pulsamos en la línea de tiempo hasta el primer KeyFrame. En ese momento (aparece una línea blanca) cambiamos la escala y la posición de la puerta (la reducimos algo en su eje Y para que no sobresalga).
 +
Image:Unity3d_animac_propia_17.jpg| Repetimos la operación esta vez pulsando en el segundo cinco sobre la línea de tiempo. En ese punto queremos que la puerta no sobresalga por la parte de arriba. Cambiamos la escala y la movemos para ajustarla. Una vez hecho '''paramos de grabar'''. Si ejecutamos el juego podemos ver como la puerta 'se encoje' al subir.
 +
</gallery>
 +
 +
 +
* Ahora vamos a hacer que la puerta tenga un [http://wiki.cifprodolfoucha.es/index.php?title=Unity_Gravedad._RigidBody RigidBody] para hacer que los personajes tropiecen con la puerta.
 +
 +
 +
 +
* A la hora de realizar animaciones nos puede ser útil la pestaña '''Curves''' de la ventana Animation:
 +
[[Imagen:Unity3d_animac_propia_18.jpg|500px|center]]
 +
: Dicha ventana nos muestra gráficamente como cambia cada uno de los componentes a lo largo de la animación, pudiendo modificar dicha curva.
 +
 +
: La modificación la podemos realizar en la pestaña '''Curves''' de una forma mucho más precisa, pero también podemos cambiar la curva 'por defecto' que crea para ciertas animaciones desde la pestaña '''Dopesheet'.
 +
<gallery caption="Modificando la curva de animación" widths="350" heights="300px" perrow="2">
 +
Image:Unity3d_animac_propia_19.jpg| Supongamos que realizamos una animación que consiste en rotar en algún eje 360 grados dicho eje y que lo haga de forma continua (por ejemplo, rotar una élice de un avión). Si realizamos la animación como he explicado anteriormente tendríamos la curva de la imagen. Como vemos la animación no es una línea recta que vaya de 0 a 360 grados, sino que es curva, y al inicio la velocidad del giro es menor. Esto lleva consigo que al ejecutar la animación paracería como si la élice se parase al llegar a 0 grados y comenzar la rotación nuevamente.
 +
Image:Unity3d_animac_propia_20.jpg| Para evitarlo, seleccionamos los dos KeyFrames de la animación y pulsando el botón derecho del ratón seleccionamos la opción '''Both Tangents''' => '''Linear'''.
 +
Image:Unity3d_animac_propia_21.jpg| Al hacerlo podemos comprobar como la curva pasa a ser ahora una línea recta y ya no se produce el efecto anterior.
 +
</gallery>
 +
 +
  
  
  
 
<br />
 
<br />
 +
 
=Modificando el orden de los componentes=
 
=Modificando el orden de los componentes=
  
* Si una animación modifica la posición-rotación, estas modificaciones van a sobre-escribir las modificaciones que tengamos en nuestros scripts.
+
* Supongamos que tenemos un script que realiza una rotación sobre un GameObject.
 +
: Además, sobre el mismo GameObject tenemos una animación la cual también modifica la rotación del mismo GameObject.
 +
 
 +
* En este caso tenemos un problema, ya que la animación siempre va a partir de la rotación inicial del GameObject antes de ejecutar la animación.
 +
: Recordar que todos los GameObjects se crean en el punto (0,0,0) y sin rotación y que después, en nuestro script, los trasladamos - rotamos a la posición que queramos.
 +
: Si nuestro GameObject está rotado y situado en otro punto inicialmente, la animación se aplicará sobre estos valores.
 +
 
 +
* Por lo tanto, cuando aplicamos la animación, esta, parte de la posición inicial del GameObject y de su rotación.
 +
: Si nuestro script aplica una rotación 'después' de la animación, cuando vuelva a ejecutar la siguiente parte de la animación, 'borrará' la rotación realizada en el script.
 +
 
  
 
* Para evitarlo podemos:
 
* Para evitarlo podemos:
Línea 634: Línea 858:
  
  
* Veamos un ejemplo:
+
* Veamos un ejemplo con un avión en el hemos creado una rotación que simula el movimiento de 'balanceo' del mismo en el aire (rotando el avión en el eje Z a izquierda-derecha).
 +
 
 +
 
 +
 
 +
 
  
 +
<br />
 +
=Investigación=
  
 +
* [https://docs.unity3d.com/Manual/class-BlendTree.html Blend Tree]
  
 +
* [https://docs.unity3d.com/Manual/NestedStateMachines.html SubStates Machines]
  
 +
* [https://docs.unity3d.com/Manual/AnimationLayers.html Animation Layers]
  
  
  
  
 +
<br />
 +
'''[https://wiki.cifprodolfoucha.es/index.php?title=Programacion_de_videojuegos_con_UNITY3D#UNIDAD_3:_Creando_videojuego_3D Enlace a la página principal del curso]'''
 +
<br />
 
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] -- (2018).
 
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] -- (2018).

Revisión actual del 09:04 25 feb 2022

Introducción

  • Más información en:


  • En Unity podemos gestionar animaciones que vengan ya incorporadas a los modelos importados o bien podemos realizar nuestras 'propias animaciones'.
Cada una de las animaciones conforma lo que se denomina Clip en Unity.


  • Las animaciones propias son animaciones consistentes en modificar alguno de los parámetros que conformar el transform de cada GameObject.
Es decir podemos modificar:
  • Posición
  • Escala
  • Rotación
Estas modificaciones se producirán a lo largo de un período de tiempo y que conformará la animación.


Por ejemplo:
  • Podemos tener un soldado que siempre se esté moviendo de izquierda a derecha sin parar y que se desplace 10 metros.
  • O un pájaro que vuele por la pantalla siguiendo una ruta 'predefinida' y que se repita continuamente.


  • En cuanto a las animaciones ya incorporadas a los modelos, estas animaciones están asociadas a un nombre (por ejemplo, andar, correr,...)
Unity permite crear una máquina de estados en las que cada animación tiene lugar en función del estado en que se encuentre el GameObject. Este pasará de un estado a otro en función de los valores que puedan tomar variables definidas por nosotros.
Unity3d animacion 0.jpg





Importando modelos con animaciones

  • Muchas de las animaciones que vamos a poder utilizar son aquellas en las que el modelo tiene asociado un esqueleto o bones.
Unity3d animacion 0A.jpg
Como vemos en la imagen, la figura 3D tiene asociado un RIG.
Básicamente consiste en asociar a un modelo 3D un conjunto de huesos los cuales están asociados a la figura, de tal forma que al mover dichos huesos, movemos las partes de la figura 3D asociada.
Este tipo de animaciones se denominan Humanoid en Unity y están asociados a un Avatar.
Los modelos que vienen con este tipo de animaciones tienen una pose en T.
Suelen ser todos bípedos y con unas características comunes para que:
  • Los esqueletos y movimientos asociados puedan ser aplicados a cualquier figura 3D con esta forma (retargeting)
  • Permite realizar Inverse Kinematic, es decir, indicar un punto determinado del espacio y hacer que los huesos del esqueleto se muevan correctamente para llegar a ese punto.
Unity3d animacion 0B.jpg



  • Podemos disponer de modelos 3D que no se adaptan a este tipo y que Unity denomina Generic:
Unity3d animacion 0C.jpg
En la imagen tenemos una figura 3D que no responde al tipo de 'Humanoide'.



  • Uno de los sitios que actualmente es gratuito y permite 'asociar' animaciones a modelos 3D es https://www.mixamo.com.
Una vez registrados accederemos a su página que actualmente tiene este aspecto:


  • Indicar que tanto en animaciones como en modelos, podemos filtrar los resultados buscando por palabras claves.
  • Si nos vale alguno de los modelos, veremos a continuación como hacer uso de ellos en Unity.
En el ejemplo siguiente partimos que no nos vale ninguno de los modelos y que utilizamos otro descargado o diseñado por nosotros mismos.
Recordar que esto solo vale para animaciones de tipo 'humanoide' como ya explicamos anteriormente.
  • Actualmente se puede descargar la aplicación Fuse de Adobe que permite diseñar nuestros avatares de forma totalmente personalizable.
Unity3d animacion 14.jpg





  • Ahora los importamos a Unity:



  • En este punto ya disponemos de 4 modelos con 4 animaciones (Clips).
Unity3d animacion 19A3.jpg


Fijarse que solo en una de ellas se previsualiza el modelo.
Si desplegáis el contenido de cada modelo (presionando la flecha hacia la derecha en cada uno) podéis ver como en todos ellos disponemos de un 'Clip' que viene a ser la animación asociada al mismo.
Unity3d animacion 19A4B.jpg

y un avatar.

  • Podemos comprobar como en Zombie Idle se encuentra toda la información del modelo (texturas, materiales, animación, avatar y mesh).
En el resto de animaciones solo tenemos el avatar y la animación ya que las hemos descargado sin el Skin.




  • El siguiente paso es configurar los modelos importados con las animaciones para indicar que son animaciones de tipo 'Humanoide'.
Para ello es necesario tener al menos en Avatar.
En el avatar es donde se guarda la información del esqueleto y las partes en las que se divide el modelo para poder realizar la animación. Estas partes son la cabeza, columna, brazos, piernas, manos,...



  • Indicar que los nombres de los Clips cuando lo descargamos de la web de Mixamo son siembre mixamo.com.
Aunque en el ejemplo que viene a continuación no están cambiados, para realizar operaciones posteriores va a ser necesario que tengan un nombre significativo.
Para cambiar el nombre del Clip, debemos de seleccionar el Modelo en la ventana Project y sobre la ventana Inspector => Animation, cambiar el nombre:
Unity3d animacion 62.jpg



Gestionando las animaciones. Animator Controller


Creando un Animator Controller. Animación inicial

  • En este punto ya tenemos las diferentes animaciones (clips) añadidas al proyecto de Unity.
Ahora es necesario gestionar dichas animaciones. Debemos de tener algún mecanismo que nos permite controlar cuando la figura 3D está en una animación o en otra.
Cuando pulsa una tecla quiero que pase de Idle a Walk.
Cuando presiono el botón de disparo quiero un Attack.
  • Todo esto se controla con una máquina de estado a a través de un Animator Controller de Unity.



  • Al pulsar dos veces sobre el AnimationController (darle un nombre adecuado), accedemos a la máquina de estados:



Reutilizando las animaciones sobre otros modelos

  • Imaginemos que estas mismas animaciones las queremos utilizar con otros 'modelos', en este caso, de tipo 'Humanoide'.



Caso especial: Modelo con un RIG diferente

  • Imaginemos que tenemos descargadas todas las animaciones anteriores y queremos aplicarlas a otro modelo diferente.
Veremos a continuación como hacerlo, pero para que funcione necesitamos que el modelo nuevo tenga 'las mismas partes' que el modelo original.
Mirar el siguiente modelo de Mixamo:
Unity3d animacion 63.jpg
¿ Notáis que falta algo ? :)
  • Realizar los siguientes pasos:
  • Descargar dicho modelo (sin animaciones asocidas), en posición T.
  • Importarlo a Unity.
  • Ir a la sección Model y desmarcar las opciones indicadas anteriormente (recordar presionar el botón Apply).
  • Si no veis correctamente el modelo recordar extraer los materiales y texturas en la sección 'Materials'.
  • Ir a la sección RIG e indicar que el modelo es de tipo Humanoid y el Avatar dejar la opción Create from this model. Os aparecerá un error al presionar el botón Apply.
Ese error indica que falta el brazo derecho y que no tiene transform asociado.
Unity3d animacion 64.jpg
  • Lo que hacen los avatares es estar divididos en partes (manos, pies, tronco cabeza,...) y asociar a cada parte un 'transform' el cual puede ser modificado para 'producir las animaciones'.
En este caso, el avatar no tiene el brazo derecho (lógico, para qué lo necesita el zombie :) )
  • Para solucionar vamos a modificar el avatar y decirle que haga uso de transforms en esas partes, pero que no van a 'dibujar' nada.





Analizando la máquina de estados

  • Más información:


  • Como ya comentamos anteriormente el Animator Controller permite tener una máquina de estados. En cada estado vamos a poder 'asociar' una animación y vamos a poder pasar de un estado a otro en base a diferentes tipos de condiciones.
Esas condiciones las vamos a poder modificar por medio de scripts.


  • Imaginemos un caso típico en el que en base a la vida que le queda al zombie, este se muere.
Para ello vamos a necesitar un parámetro vida o bien una variable de tipo boolean que indique cuando el zombie ha muerto.
Vamos a optar por la variable de tipo boolean.
Podríamos optar por el parámetro de vida si por ejemplo, al tener diferentes valores, quisiéramos diferentes animaciones. Imaginar que en caso de que le quede 5 de vida, pasaríamos a que el zombie se arrastrara. Y en caso de llegar a 0 moriría.


  • Estando en el Animator Controller del Zombie podemos ver los tipos de parámetros que podemos crear:
Unity3d animacion 38.jpg
  • Float: Una variable de tipo Flotante.
  • Int: Una variable de tipo Entero.
  • Bool: Una variable de tipo Booleana.
  • Trigger: Viene a ser una variable de tipo Booleana (admite valores true/false) con la diferencia que una vez la transición se ha producido su valor vuelve a valer false automáticamente.
Un ejemplo de uso de este tipo de variables puede ser cuando estamos disparando un arma y se acaba la munición. En ese momento debemos de pasar a la animación de cargar el arma (sería poniendo a true una variable de este tipo) e inmediatamente después continuaríamos con la animación de disparar. Al pasar a la animación de disparar, la variable Trigger ya valdría false otra vez.


  • Veamos un ejemplo práctico.
La pregunta que te estás haciendo es, ¿ por qué hago uso de una variable de tipo Trigger y no de una de tipo Boolean ?
Haz la prueba y comprueba lo que pasa.
El problema estriba en que AnyState es cualquier estado aún sin acabar de ejecutarse la animación.
Si haces uso de una variable booleana, el valor de dicha variable seguirá valiendo 'true' hasta que cambies nuevamente a 'false', por lo tanto, si hacemos la prueba gráficamente veremos que se ejecuta continuamente la animación de Zombie Death sin que acabe nunca.
Esto se puede solucionar a nivel de programación, cambiando el valor de dicha variable a false inmediatamente después de ponerla a true (veremos como hacerlo). Pero esto es lo que hace la variable de tipo Trigger. Se pone a false inmediatamente después de true.


  • Vamos a realizar ahora otro caso práctico que va a consistir en añadir un parámetro velocidad, para que en función del valor del parámetro cambie de estado de Zombie_Parado a Zombie_Walk y viceversa.



Modificando los parámetros por medio de scripts

  • Antes de entrar en los scripts vamos a realizar unas pequeñas modificaciones al ejemplo.
Vamos a dejar a los dos zombies sin rotar y cambiaremos la cámara de posición para que se vean más lejanos. Al hacerlo los zombies aparecerán de espaldas. Rotamos la cámara de sitio para que se vean por delante.
Aumentaremos la escala del plano para que tengan más superficie por donde 'andar'.
Unity3d animacion 53.jpg



  • Como queremos que desde la animación de 'parado' pase a la animación de 'andar' sin que haya una transición, no chequeamos el parámetro Has Exit Time de la transición:




  • Por medio de scripts vamos a poder acceder a los valores de los parámetros definidos en el AnimatorController.
Unity3d animacion 51.jpg
En la imagen hemos creado un script de nombre ControlEstadoZombie.
Recordar colocarlo dentro de una carpeta de nombre Scripts y dentro de esta organizarlo como mejor creáis.
Unity3d animacion 52.jpg


  • Ahora editamos el código del script.
 1 using UnityEngine;
 2 
 3 public class ControlEstadoZombie : MonoBehaviour {
 4 
 5     [SerializeField]
 6     private float velocidadMaxima;
 7     [SerializeField]
 8     private int vida;
 9 
10     private float velocidadActual = 0f;
11     private Animator animator;
12 
13     // Se ejecuta cuando hacemos un reset del script desde la ventana Inspector de Unity
14     void Reset()
15     {
16         velocidadMaxima = 2;
17         vida = 5;
18         velocidadActual = 0;
19     }
20 
21 
22     // Use this for initialization
23     void Start () {
24         animator = GetComponent<Animator>();
25     }
26 
27     // Update is called once per frame
28     void Update () {
29 
30     }
31 }
  • Líneas 11 y 24: Estas líneas definen un objeto de la clase Animator (de nombre animator) y en la línea 24 obtenemos el objeto Animator que tiene asociado el GameObject en el Inspector y se lo asignamos a la variable.
  • Resto de líneas las explicaremos más adelante.


Como a nosotros nos interesa acceder a los parámetros creados, podemos acceder con:


Disponemos de los correspondientes métodos GetFloat, GetBool, GetInteger para obtener los valores actuales de los parámetros.


  • Lo que queremos hacer es que cuando se produzca un evento (una colisión, una pulsación del ratón, que el zombie detecte nuestra presencia,....) cambiemos el estado de parado a moviéndose.
Vamos a hacerlo con la tecla W (podéis hacerlo de cualquier otra forma).
1     void Update() {
2         if (Input.GetKeyDown(KeyCode.W)){
3 
4             animator.SetFloat("velocidad", 1);
5         }else if (Input.GetKeyDown(KeyCode.S))
6         {
7             animator.SetFloat("velocidad", 0);
8         }
9     }
  • Liñas 4 y 7: Cambiamos el valor del parámetro 'velocidad' en función de si pulsamos la tecla W o la tecla S
  • Si ejecutamos este código vemos que funciona, pero que el zombie no se mueve debido a que no estamos modificando su posición como ya vimos en esta Wiki.
Modificamos el código para hacerlo:
 1     void Update() {
 2         transform.Translate(transform.forward * velocidadActual * Time.deltaTime);
 3         if (Input.GetKeyDown(KeyCode.W)){
 4             animator.SetFloat("velocidad", 1);
 5             velocidadActual = 1;
 6         }else if (Input.GetKeyDown(KeyCode.S))
 7         {
 8             animator.SetFloat("velocidad", 0);
 9             velocidadActual = 0;
10         }
11     }
Unity3d animacion 53.jpg
El zombie se mueve...


  • Ahora vamos a complicarlo un poco y vamos a hacer que el zombie tenga diferentes velocidades, es decir, que se pueda mover más rápido.
Podemos hacerlo cambiando el código:
 1     void Update() {
 2         transform.Translate(transform.forward * velocidadActual * Time.deltaTime);
 3         if (Input.GetKeyDown(KeyCode.W) && velocidad<5){
 4             animator.SetFloat("velocidad", 1);
 5             velocidadActual++;
 6         }else if (Input.GetKeyDown(KeyCode.S) && velocidad > 0)
 7         {
 8             animator.SetFloat("velocidad", 0);
 9             velocidadActual--;
10         }
11     }
  • Nota: Otra opción más fácil sería pasarle directamente al parámetro velocidad del animator, la velocidad del GameObject: animator.SetFloat("velocidad", velocidad)


Pero para hacerlo 'mas bonito' vamos a emplear un control gráfico HorizontalSlider.
Para ello creamos un nuevo método OnGUI:
 1 public class ControlEstadoZombie : MonoBehaviour {
 2 
 3     [SerializeField]
 4     private float velocidadMaxima;
 5     [SerializeField]
 6     private int vida;
 7 
 8     private float velocidadActual = 0f;
 9     private Animator animator;
10     private float m_MySliderValue;
11 
12     void Reset()
13     {
14         velocidadMaxima = 2;
15         vida = 5;
16         velocidadActual = 0;
17     }
18 
19     // Use this for initialization
20     void Start () {
21         animator = GetComponent<Animator>();
22     }
23 
24     void OnGUI()
25     {
26         GUI.Label(new Rect(0, 25, 40, 60), "Speed");
27 
28         // Devuelve un float en un rango entre 0.0f y velocidadMaxima
29         m_MySliderValue = GUI.HorizontalSlider(new Rect(45, 25, 200, 60), m_MySliderValue, 0.0f, velocidadMaxima);
30 
31         velocidadActual = m_MySliderValue;
32         animator.SetFloat("velocidad", velocidadActual);
33     }
34 
35     // Update is called once per frame
36     void Update() {
37 
38         transform.Translate(transform.forward * velocidadActual * Time.deltaTime);
39 
40     }
41 
42 }
  • Línea 4: Esta variable va a indicar la velocidad máxima a la que vamos a poder mover el zombie.
  • Líneas 24-33: Dibuja el Slider y en función de su valor ajusta la velocidad del zombie
  • Línea 16: Dibuja un label con texto speed.
  • Línea 19: Dibuja un Horizontal Slider con un rango entre 0.0f y velocidadMaxima. Devuelve el valor seleccionado gráficamente.
  • Línea 31: Asignamos la velocidad para la función translate del transform.
  • Línea 32: Cambiamos la velocidad del parámetro. No importa su valor, solo si es mayor que 0.1f


Unity3d animacion 58.jpg
  • Ahora el problema lo tenemos en que la velocidad del zombie no se corresponde con la 'velocidad del movimiento'. Gráficamente se mueve muy lento cuando aceleramos.
Para solucionarlo podemos hacer uso de la propiedad Speed
Unity3d animacion 59.jpg
Lo que queremos es que el valor de esa propiedad cambie con la velocidad.
Como a nivel de programación disponemos del objeto 'animator' que representa toda la máquina de estados, podemos modificar su valor con el siguiente código:
 1     void OnGUI()
 2     {
 3         GUI.Label(new Rect(0, 25, 40, 60), "Speed");
 4 
 5         // Devuelve un float en un rango entre 0.0f y velocidadMaxima
 6         m_MySliderValue = GUI.HorizontalSlider(new Rect(45, 25, 200, 60), m_MySliderValue, 0.0f, velocidadMaxima);
 7 
 8         velocidadActual = m_MySliderValue;
 9         animator.SetFloat("velocidad", velocidadActual);
10         animator.speed = (m_MySliderValue == 0) ? 1 : m_MySliderValue;  // Al iniciar la ejecución el Slider vale 0. La velocidad de la animación no puede valer 0 ya que no se ejecutaría y por lo tanto no cambiaría de estado.
11     }
Si ejecutáis el juego podéis comprobar como al aumentar la velocidad aumenta la velocidad de la animación.


  • El siguiente paso a programar podría ser cuando el zombie muere.
Normalmente tendréis una determinada vida y al acabar con ella el zombie (o el protagonista) deben morir.
Morir en el caso de los enemigos suele llevar el que desaparezca.
Vamos a programar que el zombie tenga una vida. Al pulsar la tecla 'M' vamos a quitar una vida al zombie. Al llegar a cero vamos a hacer que muera.
 1    void OnGUI()
 2     {
 3         if (vida > 0)
 4         {
 5             GUI.Label(new Rect(0, 25, 40, 60), "Speed");
 6 
 7             // Devuelve un float en un rango entre 0.0f y velocidadMaxima
 8             m_MySliderValue = GUI.HorizontalSlider(new Rect(45, 25, 200, 60), m_MySliderValue, 0.0f, velocidadMaxima);
 9 
10             velocidadActual = m_MySliderValue;
11             animator.SetFloat("velocidad", velocidadActual);
12             animator.speed = (m_MySliderValue == 0) ? 1 : m_MySliderValue;  // Al iniciar la ejecución el Slider vale 0. La velocidad de la animación no puede valer 0 ya que no se ejecutaría y por lo tanto no cambiaría de estado.
13         }
14     }
15 
16     // Update is called once per frame
17     void Update() {
18         if (vida > 0)
19         {
20             transform.Translate(transform.forward * velocidadActual * Time.deltaTime);
21         }
22 
23         /*
24         if (Input.GetKeyDown(KeyCode.W)){
25             animator.SetFloat("velocidad", 1);
26             velocidadActual = 1;
27         }else if (Input.GetKeyDown(KeyCode.S))
28         {
29             animator.SetFloat("velocidad", 0);
30             velocidadActual = 0;
31         }
32         */
33 
34         if (Input.GetKeyDown(KeyCode.M))   
35         {
36             vida--;
37             Debug.Log("Vida" + vida);
38             if (vida == 0)   // Podemos usar == ya que es de tipo int
39             {
40                 animator.speed = 1;     
41                 animator.SetTrigger("muerto");
42             }
43         }
44     }
45 }
Si ejecutáis el código y presionáis la tecla M cinco veces, el zombie morirá.
  • Línea 43: Fijarse que tenemos que volver a poner la velocidad a su valor por defecto, ya que sino, cuando inicie la animación del Zombie_Death lo haría a la velocidad establecida cuando el zombie andaba.


Unity3d animacion 60.jpg


  • Ahora solo queda hacer que el zombie desaparezca.
Aquí tenemos dos posibilidades:


Para destruir el zombie tengo que preguntar si estoy en la animación 'Zombie_Death' y se ha acabado de ejecutar. Eso traducido a programación es:
 1     // Update is called once per frame
 2     void Update() {
 3         if (vida > 0)
 4         {
 5             transform.Translate(transform.forward * velocidadActual * Time.deltaTime);
 6         }
 7 
 8         /*
 9         if (Input.GetKeyDown(KeyCode.W)){
10             animator.SetFloat("velocidad", 1);
11             velocidadActual = 1;
12         }else if (Input.GetKeyDown(KeyCode.S))
13         {
14             animator.SetFloat("velocidad", 0);
15             velocidadActual = 0;
16         }
17         */
18 
19         if (Input.GetKeyDown(KeyCode.M))   
20         {
21             vida--;
22             Debug.Log("Vida" + vida);
23             if (vida == 0)   // Podemos usar == ya que es de tipo int
24             {
25                 animator.speed = 1;     
26                 animator.SetTrigger("muerto");
27             }
28         }
29 
30         if (animator.GetCurrentAnimatorStateInfo(0).IsName("Zombie_Death") && animator.GetCurrentAnimatorStateInfo(0).normalizedTime>=1f)
31         {
32             Destroy(gameObject);
33         }
34     }


Unity3d animacion 61.jpg



  • Métodos útiles:
  • animator.GetCurrentAnimatorStateInfo(0).IsName("Zombie_Walk"): Devuelve true si el zombie se encuentra en la animación Zombie_Walk.
El (0) hace referencia a las capas, las cuales se usan para animar de forma diferente a diferentes partes del cuerpo.



Parámetro Root Motion

  • Más información:


Unity3d animacion 61b.jpg


  • Este parámetro va a determinar si la animación 'mueve' el GameObject en nuestro mundo 3D.
Esto quiere decir que la animación produce un movimiento que hará que los ejes X,Y,Z del gameobject se modifiquen...
Si queremos que dicha animación no afecte a los ejes, no marcaremos esta opción.
Un ejemplo lo tenemos en la animación de la muerte. Al morir el Zombie 'cae' al suelo por la propia animación. Deberíamos marcar esta opción si queremos que se modifique la posición del zombie al morir. Sino, tendríamos que programarlo nosotros.


  • Esto es necesario si hacemos uso de un NavMesh Agent, el cual sirve para calcular rutas para llegar a un objetivo esquivando obstáculos que pueda haber por el camino.
Un ejemplo de uso podría ser un laberinto en el que queremos que los enemigos se muevan por el mismo...
Veremos el uso del componente NavMesh en otra sección de esta Wiki.




Controlando el tiempo de la animación


  • Como vimos anteriormente, podemos saber cual es la animación que se está ejecutando en cada momento:
animator.GetCurrentAnimatorStateInfo(0).IsName("Zombie_Death") , siendo 'Zombie_Death' el nombre de la animación.
  • También podemos controlar en que momento de la animación nos encontramos con el siguiente código:
animator.GetCurrentAnimatorStateInfo(0).normalizedTime , esta propiedad devuelve un valor entre 0-1 expresando el porcentaje de la animación que ya ha sido mostrada (1 => 100%).


  • A nivel gráfico también podemos asociar una función a las animaciones de un modelo:



Asociando scrips a cada estado de la máquina de estados


Al alcanzar la máquina de estados el estado en el que se encuentra asociado el script, empezará a ejecutar una serie de métodos asociados.
Podemos emplearlo para gestionar acciones que queremos que se ejecuten cuando la máquina de estados alcanza dicho estado.
Por ejemplo, cuando un enemigo detecta al protagonista y cambia la variable de la máquina de estados para que siga al protagonista, podemos hacer que el script que deriva de StateMachineBehaviour lo siga y cuando salga de la animación deje de seguirlo.
En el ejemplo anterior, esta lógica la tendríamos codificada en el script que gestiona al enemigo, verificando la máquina de estado y haciendo que el enemigo siga al protagonista.
Para añadir este tipo de script simplemente tenemos que seleccionar un estado de la máquina de estado y presionar el botón Add Behavior:
Unity3d animacion 61g.jpg



Gestionando las animaciones. Animator Override Controller


  • Este control permite tener un Animator Controller basado en otro ya hecho, pero permitiendo cambiar las animaciones asociadas a cada uno de los estados.
Tendrá las mismas condiciones y variables que el Animator Controller del que se basa.
  • Veamos un ejemplo:




Creando animaciones propias

  • En Unity es posible crear nuestras propias animaciones, es decir, crear nuestros propios clips.
  • El proceso sería el siguiente:
  • Añadir a lo que va a ser el Clip aquellos GameObjects que queramos animar.
  • Dentro de cada GameObject añadimos aquellas propiedades que van sufrir modificaciones durante la animación.
Podemos modificar el componente Transform, el componente Mesh Renderer, Mesh Collider, RigidBody e incluso activar o desactivar scripts todo de forma dinámica.
  • Establecemos el tiempo que va a dura el Clip.
  • Iniciamos la grabación del Clip y establecemos 'puntos de referencia'. En cada uno de esos puntos modificamos alguno de los valores de alguno de los componentes indicados anteriormente. Unity ya se encarga de, por medio de la interpolación' modificar los valores de los componentes (por ejemplo la velocidad, posición, rotación,...) para llegar al punto destino con los valores indicados por el usuario.



  • Ahora llega el momento de grabar la animación.


  • Si ejecutamos el juego podemos ver como la animación se pone en marcha.
Esto es así ya que se ha creado un AnimatorController y que ha sido asociado al GameObject puerta al crear la animación.
Podemos tratarla como cualquier otra animación como hicimos en el punto anterior.
Unity3d animac propia 14.jpg


  • Vamos a realizar una modificación para hacer que la escala de la puerta decrezca y que así no 'salga' por la parte de arriba.
Normalmente esto no sería necesario ya que si el juego es en primera persona, la puerta no se abriría hasta estar cerca de la misma y por tanto no veríamos la parte de arriba.


  • Ahora vamos a hacer que la puerta tenga un RigidBody para hacer que los personajes tropiecen con la puerta.


  • A la hora de realizar animaciones nos puede ser útil la pestaña Curves de la ventana Animation:
Unity3d animac propia 18.jpg
Dicha ventana nos muestra gráficamente como cambia cada uno de los componentes a lo largo de la animación, pudiendo modificar dicha curva.
La modificación la podemos realizar en la pestaña Curves de una forma mucho más precisa, pero también podemos cambiar la curva 'por defecto' que crea para ciertas animaciones desde la pestaña Dopesheet'.




Modificando el orden de los componentes

  • Supongamos que tenemos un script que realiza una rotación sobre un GameObject.
Además, sobre el mismo GameObject tenemos una animación la cual también modifica la rotación del mismo GameObject.
  • En este caso tenemos un problema, ya que la animación siempre va a partir de la rotación inicial del GameObject antes de ejecutar la animación.
Recordar que todos los GameObjects se crean en el punto (0,0,0) y sin rotación y que después, en nuestro script, los trasladamos - rotamos a la posición que queramos.
Si nuestro GameObject está rotado y situado en otro punto inicialmente, la animación se aplicará sobre estos valores.
  • Por lo tanto, cuando aplicamos la animación, esta, parte de la posición inicial del GameObject y de su rotación.
Si nuestro script aplica una rotación 'después' de la animación, cuando vuelva a ejecutar la siguiente parte de la animación, 'borrará' la rotación realizada en el script.


  • 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.


  • Veamos un ejemplo con un avión en el hemos creado una rotación que simula el movimiento de 'balanceo' del mismo en el aire (rotando el avión en el eje Z a izquierda-derecha).




Investigación




Enlace a la página principal del curso

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