Jump to content
UnitySpain
fracto

Control Animaciones con Script

Recommended Posts

Buenas a todos,

estoy desarrollando una aventura grafica. Para ello estoy usando el asset Adventure Creator (AC), un personaje también comprado del asset (temporal hasta colocar el definitivo) y animaciones descargadas desde Mixamo.

A parte de las típicas animaciones como andar, correr, hablar ... que puedo gestionar con AC me gustaría que el personaje pudiera realizar otras acciones como subir una escalera vertical, escalar una cuerda, empujar objetos...

He preguntado en el foro de AC y el creador me comenta que este tipo de animaciones se deben gestionar a parte mediante scripts. Así que me he puesto a investigar un poco y he encontrado tutoriales que hablan sobre controlar animaciones mediante scripts pero, debido a mi nula capacidad de programación, no acabo de conseguir lo que quiero. Por ello necesito de vuestra ayuda.

He creado un archivo donde gestionar todas estas acciones "extra" que quiero utilizar, y he querido empezar por gestionar la animación de subir escaleras verticales.

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

public class PlayerExtraAnimations : MonoBehaviour {

    Animator anim;
    Rigidbody rb;
    CapsuleCollider ccol;

    public bool IsClimbing;

    Vector3 tempPos;

    // Use this for initialization
    void Start()
    {
        anim = GetComponent<Animator>();
        rb = GetComponent<Rigidbody>();
        ccol = GetComponent<CapsuleCollider>();

        IsClimbing = false;
    }

	// Update is called once per frame
	void Update ()
    {
      if(IsClimbing)
      {
        IsClimbing = true;
        anim.SetBool("IsClimbing", true);
        tempPos = transform.position;
        anim.Play("ClimbLadder");
        tempPos.y += 0.01f;
        transform.position = tempPos;
      }
	}

En el Animator Controller he creado un parámetro IsClimbing que, cuando es TRUE, ejecuta la animación de subir la escalera. 

Pues bien, tal como lo tengo no funciona. Si utilizo las cuatro lineas siguientes dentro directamente del Update(), fuera de cualquier condicionante, si se ejecuta la animación pero es continua, no la puedo controlar a mi antojo.

 tempPos = transform.position;
 anim.Play("ClimbLadder");
 tempPos.y += 0.01f;
 transform.position = tempPos;

Por otro lado, me encuentro también con la duda de cómo indicar que, cuando el personaje llega a la parte superior de la escalera se ejecute la animación ClimbTop. Entiendo que debe ser utilizando algún Collider verdad?

Adjunto captura del Animator Controller por si puede servir de ayuda.

Muchas gracias de antemano!

 

Share this post


Link to post
Share on other sites

Te recomiendo mires en Youtube para este tipo de cosas por que engloba muchos temas y se nos hace complicado explicarte en un post.

 

Share this post


Link to post
Share on other sites

Hola, fijate algunas cuestiones del scripting, por ej:

if(IsClimbing)
      {
        IsClimbing = true;	//<---- Si yo soy lightbug entonces soy lightbug
		...

y arriba estás imponiendo en Start un IsClimbing = false, pero bueno...

sacando esto, te recomiendo que le dejes la parte de animación al animator, y solamente desde scripting des las señales que el animator interpretará, osea, en Animator vos seteas estados (clips) y transiciones (basicamente una maquina de estados o FSM), configuras el tiempos de blend, si tiene esto y aquello y muchas cosas más.

En algun script donde puedas verificar la condición de transicion entre dos estados simplemente seteas un Trigger, que en animator significa una bool que cuando la seteas se pone en true y en el cuadro siguiente se pone en false:

switch(state)
{
	case State.Idle:

		//Transición Idle -> Walk
		if(se dio la condición para caminar)
		{
			animator.SetTrigger("Walk");	//Importante ponerlo igual a la variable del Animator
			state = State.Walk;
		}

		break;

	case State.Walk:
		...

}

Y si sos perezoso como yo podés tener un Animator controller como este (imagen ejemplo):

GUI_ButtonAnimator.png

En cada flechita configurás las transiciones con los triggers que ya agregaste, hay mucha info al respecto.

Ya que los estados y transiciones del personaje las gestionas desde un script no pasa nada, aunque es probable que no estés haciendo esto, entonces deberías repensar tu diseño en general. Que te ande o no para el caso del climbing es irrelevante, y si mañana empezas a mandar varios estados que son mezclas de otros estados se te empieza a complicar, simplemente separalos (hoja y papel), luego defini que acción provoca el paso de uno a otro, implementá el "gran switch" como el del ej y verás que con el animator y los Trigger mencionados te queda todo bien facil (por lo menos para comenzar).

 

Share this post


Link to post
Share on other sites

@francoe1 gracias por el consejo. Lo que he codificado hasta ahora lo he ido sacando de diferentes videotutoriales pero al no tener conocimientos de programación cuando me quiero salir de lo que se explica ya me pierdo...

@lightbug gracias por la ayuda, pero no he entendido nada jajaja como comenté mi nivel de programación es nulo. 

Todo el sistema de movimiento del personaje ya lo tengo configurado ya que se encarga directamente el asset que comentaba, Adventure Creator. Como es un asset específico para aventuras gráficas no viene incluido el poder utilizar otro tipo de acciones tales como las que quiero realizar.

Las acciones no quiero que sean para ejecutar con controles durante el juego sino que sean a modo de cinemáticas programadas, por lo que entiendo que debe ser mas sencillo (el juego es tipo point&click).

Por ejemplo, para la acción de subir las escaleras tengo claro el esquema de lo que debe hacer el personaje:

- El personaje camina hasta la escalera y se pone de frente. Esto ya se ejecuta durante el juego.

- Se ejecuta la animación de subir las escaleras, así como el personaje va ascendiendo. Aquí tengo el problema, no se cómo indicar al personaje que ha llegado a la base de la escalera y puede empezar a ejecutar la animación. Por otro lado, el personaje no asciende correctamente.

- Cuando llego a la parte superior de la escalera, mediante un trigger le comunico al personaje que deje de ejecutar la acción de subir las escaleras y se ejecute la animación  de llegada desde la escalera. Pienso que es lo más simple.

¿Cómo lo ves?

 

Share this post


Link to post
Share on other sites
23 hours ago, fracto said:

Por ejemplo, para la acción de subir las escaleras tengo claro el esquema de lo que debe hacer el personaje:

- El personaje camina hasta la escalera y se pone de frente. Esto ya se ejecuta durante el juego.

- Se ejecuta la animación de subir las escaleras, así como el personaje va ascendiendo. Aquí tengo el problema, no se cómo indicar al personaje que ha llegado a la base de la escalera y puede empezar a ejecutar la animación. Por otro lado, el personaje no asciende correctamente.

- Cuando llego a la parte superior de la escalera, mediante un trigger le comunico al personaje que deje de ejecutar la acción de subir las escaleras y se ejecute la animación  de llegada desde la escalera. Pienso que es lo más simple.

¿Cómo lo ves?

Mirá, en lineas generales es dificil decirte que hacer porque el asset funcionará de tal manera y la verdad no lo conozco, pero tu forma de hacerlo parece bien, aunque tenés una forma más simple. Si separamos todas las fases te queda algo así:

Detección:

Si la escalera tuviera su collider con un simple raycast podés detectar si se cliqueó o no, incluso con menos que eso, seguramente esto ya está en el asset como mencionas en el punto 1 por eso este aspecto es. Una vez que tu personaje deba ir a la escalera14 (por ej) calculas las distancias entre ambos, cuando esta sea menor a X listo, a estado ->  "Climb" (quizás acá tengas que meter mano)

Subida/Bajada:

No hay mucho que decir aca, solamente que te moverás en la dirección del "up" de la escalera, en un sentido o en el otro, esto si es que tenés control sobre el movimiento, esto significa que la animación sera de peldaño a peldaño (también el movimiento). Si es "completa", osea sube o baja todos los peldaños simplemente debés elegir entre dos clips (el animator lo hace), por ej una con el personaje mirando hacia arriba y otra para abajo.

De Climb a Idle (parado):

Si tenés el origen de la escalera (transform.position) estrategicamente colocado respecto al modelo o sprite (generalmente en una escalera está debajo del primer peldaño al igual que en tu personaje) entonces ya tenés conocimiento espacial de la base y de la cima de la escalera (si conoces, que deberías, su altura exacta), esto comparado con el origen del personaje ya te da información de la subida o bajada, podés decir si el personaje está en el 10% del recorrido, en el 60%, etc. Por ejemplo en un personaje que hice hace tiempo cuando entrabas en el estado "Climb" (ponele) llevaba el personaje a una altura dada, creo que 21 cm (mientras sea mayor a 0 está bien), la joda era que el recorrido estaba limitado de 20 cm a (top - 40) cm, entonces si me iba por debajo o por encima me dejaba al personaje (lo trasladaba) en dos puntos distintos. Esto lo podés hacer via programación, o podes tener en la escana (hijos de la escalera por ej) dos objetos vacios que harán de referencia de abajo y de arriba, cada uno te impone una posicion y una rotación, super útil.

Un poco te comento todo esto para darte ideas, como podés ver si detectas la escalera todo se resume en verificar distancias entre dos puntos, no es más complicado que eso, no necesitas un trigger, pero si te funciona con un trigger y querés usarlo no pasa nada, funciona igual lo unico que en vez de comparar distancias tenés tu trigger de Top y tu trigger de bottom, pero es menos eficiente e innecesariamente más complicado. Como decís, si tu nivel de programación es nulo va a ser defícil que no se te complique con cualquier cosita.

 

 

 

Share this post


Link to post
Share on other sites

@lightbug ante todo agradecerte la ayuda.

El código que he realizado actualmente es el siguiente:

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

public class PlayerExtraAnimations : MonoBehaviour {

  Animator anim;
  Rigidbody rb;

  public float dist;
  public float climbspeed;
  public GameObject ladder;

  void Start()
  {
    anim = GetComponent<Animator>();
    rb = GetComponent<Rigidbody>();
  }

  void Update()
  {
    float distLadder = Vector3.Distance (transform.position, ladder.transform.position);
    if(distLadder < dist)
    {
      Climb();
    }

  }

  void Climb()
  {
    Vector3 move = new Vector3 (0,1,0) * climbspeed * Time.deltaTime;
    rb.MovePosition(transform.position + move);
    anim.SetBool("IsClimbing", true);
  }

}

Siguiendo los pasos que me has indicado voy a ir tratando cada uno de ellos mientras lo vaya solventando:

On 8/23/2018 at 1:24 AM, lightbug said:

Detección:

Si la escalera tuviera su collider con un simple raycast podés detectar si se cliqueó o no, incluso con menos que eso, seguramente esto ya está en el asset como mencionas en el punto 1 por eso este aspecto es. Una vez que tu personaje deba ir a la escalera14 (por ej) calculas las distancias entre ambos, cuando esta sea menor a X listo, a estado ->  "Climb" (quizás acá tengas que meter mano).

Listo. He creado una función que me regula cuando activar la animación "Climb" en función de la distancia entre el player y la escalera. De momento la distancia la tengo dentro de una variable publica para ir ajustando su valor. La idea es crear un script que me funcione para las diferentes escaleras que tenga en el juego, sean más o menos largas. Vendrá luego.

Se ejecuta la transición de Idle a Climb (ajustaré las transiciones desde el animator) pero el personaje no asciende. He visto algunos videos sobre cómo mover un objeto con rigidbody y debería funcionar.

Inicialmente he pensado que hubiera una interferencia de colisión entre el player y la escalera, así que he marcado una distancia lejana para que se activara la animación. Pues bien, una de las razones era esta.

Pero bueno, el personaje no asciende de manera continua sino que "bota" como una pelota jejeje así que supongo que en estas dos lineas debe haber un error

Vector3 move = new Vector3 (0,1,0) * climbspeed * Time.deltaTime;
rb.MovePosition(transform.position + move);

¿A que puede deberse?

Share this post


Link to post
Share on other sites
7 hours ago, fracto said:

¿A que puede deberse?

Se debe a dos cosas, una fundamental, tu personaje no debería ser un rb(cada vez que digo rb estoy suponiendo rb dinámico), para eso tenes el componente character controller que es en mi opinión es infinitamente mejor para el control de personajes, pero bueno sacando esto de lado y suponiendo que te funciona todo con Rb (o que el asset viene así), la segunda, es que tu Rb es aún afectado por la gravedad cuando le impones ese movimiento vertical, por eso te produce ese efecto, no resulta "correcto" mover al rb así, cambiandole la posición en un cuadro, eso funciona bien para un rb kinemático. Lo que tenés que hacer es, en el momento de cambiar de idle a climb ponelo en rb kinemático (creo que con isKinematic = true) y alrevés de climb a idle. Esto te permite modificarlo como si fuera un "objeto común" con su transform, si querés usar MovePosition o Translate creo que da igual. Lo mismo podés usar cada vez que querés imponer un movimiento "no real" al objeto, una escalera como en este caso es el ejemplo perfecto, meterse en un ducto de ventilación podría ser otro, trepar una pared tipo parkour otro, y así :)

Saludos

Edited by lightbug

Share this post


Link to post
Share on other sites

Pues he añadido las líneas correspondientes en el código

 

  void Update()
  {
    float distLadder = Vector3.Distance (transform.position, ladder.transform.position);
    if(distLadder < dist)
    {
      rb.isKinematic = true;
      rb.detectCollisions = false;
      Climb();
    }

  }

  void Climb()
  {
    Vector3 move = new Vector3 (0,1,0) * climbspeed * Time.deltaTime;
    rb.MovePosition(transform.position + move);
    anim.SetBool("IsClimbing", true);
  }

y sigue sin funcionar. El personaje ya no bota pero sigue sin subir.

Sobre el utilizar un rigidbody es porque, para que el personaje funcione con el sistema del Adventure Creator, se pide que el personaje lo lleve.

Share this post


Link to post
Share on other sites
8 hours ago, fracto said:

y sigue sin funcionar.

Estas seguro que se da la condición? imprimiste mensajes para tener seguridad? Segun veo, Climb() se está dando cuando la distancia con la escalera es menor a tanto, eso es incorrecto. Lo de la distancia funciona solamente para la detección (o condición de salida), y encima la detección es distinta para el top y para el bottom, cosas que tenés que evaluar. Por ej cliqueas en la ladder, resulta que la distancia player-bottom (osea al origen de la escalera) = 2 m , distancia player-top = 5 m, entonces te vas hacia el bottom para subir por la escalera. Por el contrario si estuvieras en el piso de arriba sería al revés (y bajarías en vez de subir). Esta condición del lugar más cercano es para pensarla bien, se podrían dar situaciones no tan disparatadas raras en las que esto no salga bien. Una vez que estés en "Climb" la condición de salida (climb a idle) cambia, podés hacer lo del recorrido que mencioné antes, para esto necesitas tener info de la escalera, punto de referencia de bottom (salida a bottom), punto de referencia de top (salida a top) e ir calculando el recorrido en todo momento, cosa de que si te pasas por encima te vas a top, si te pasas por debajo te vas a bottom. Y si es automática solamente con evaluar la distancia al punto de referencia contrario alcanza, es decir si entraste por bottom evaluar distancia a top, y al revés si entraste por top.

Se que suena a quilombo, meter estos estados dentro de un asset que ya funciona con su propia metodología puede no resultar muy lindo, si te resulta y solo con esto te funciona, perfecto, pero sino hacerlo de cero al movimiento no es una gran complicación.

 

Share this post


Link to post
Share on other sites

Nuevos avances en el código, a partir de tutoriales que he visualizado.

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

public class ClimbLadder : MonoBehaviour {

  Animator anim;

  public GameObject startPos, endPos;
  public float speed = 1.0f;
  public float distToLadder;

  float startTime, totalDistance;

  void Start()
  {
    anim = GetComponent<Animator>();

    startTime = Time.time;
    totalDistance = Vector3.Distance(startPos.transform.position, endPos.transform.position);
  }

  void Update()
  {
    float currentDuration = (Time.time - startTime) * speed;
    float journeyFraction = currentDuration / totalDistance;
    
    this.transform.position = Vector3.Lerp(startPos.transform.position, endPos.transform.position, journeyFraction);

    //float distToBase = Vector3.Distance(transform.position, startPos.transform.position);
    //if(distToBase <= distToLadder)
    //{
    //  this.transform.position = Vector3.Lerp(startPos.transform.position, endPos.transform.position, journeyFraction);
    //  anim.Play("ClimbLadder");
    //}


  }

}

Cuando ejecuto el juego el personaje se situa en la base de la escalera y asciende correctamente hasta el punto superior de la misma. De momento no he añadido la animación, a ver si logro que funcionen primero los desplazamientos. La transición es suave.

El siguiente paso que he realizado ha sido incluir las lineas del código que tenía anteriormente (indicados al lado de la doble //), para dar la condición de la distancia entre el personaje y el punto base de la escalera como me habías comentado, de manera que cuando estoy a una distancia X de la base se ejecute la acción de subir la escalera.

Pues bien, aquí es donde ya me ha dado el problema. Cuando llego a la distancia indicada el personaje se me situa de repente en la parte superior, no hay transición suave entre los dos puntos.

Adjunto un video para que puedas ver la diferencia.

Climb Ladder ejemplo

 

Share this post


Link to post
Share on other sites
On 8/25/2018 at 5:49 PM, fracto said:

Pues bien, aquí es donde ya me ha dado el problema. Cuando llego a la distancia indicada el personaje se me situa de repente en la parte superior, no hay transición suave entre los dos puntos.

Adjunto un video para que puedas ver la diferencia.

Nunca podés determinar el recorrido por el tiempo de juego, es como querer saber si llueve mirando al inodoro, sí, quizás si el reflejo del inodoro hace visible a la ventana, y la ventana está justo abierta y se ve la lluvia puedas decir "Sí, está lloviendo". Eso pasa más o menos aca, cuando das play se da perfecto porque impusiste las condiciones para que sea dé. Si te pasas tiempo jugando todo termina en que el tercer parámetro de lerp toma valores mayores a 1, es decir, terminas en la posicion del segundo parámetro, osea el top.

Algunas cosas:

  • Lerp NO es indicado para esto, el movimiento no es parejo a 10 cm , a 50cm o al metro, depende del recorrido, y en la realidad el subir/bajar una escalera no depende de esto
  • Separa las cosas por estados o condiciones, usa el switch de antes, solamente estás retrasando lo inevitable, te va a salvar la vida esto
  • El código funciona para una sola escalera ya que nunca actualizas los start y end para otras escaleras, si hacés bien el punto anterior esto lo solucionas.
  • Distancia <start , end> no nocesariamente es la altura, pero vamos a suponer que tenes esta convención y está bien.

 

Share this post


Link to post
Share on other sites

Yo te voy a dar un consejo cojonudo:

- Si quieres hacer una aventura gráfica con poca o nula programación, deja el Unity y usa el Visionaire Studio u otros motores que hay.
- Si quieres hacer una aventura gráfica con programación, deja el Unity y usa el Visionaire Studio u otros motores que hay, muchos mas, a mi me encanta wintermute, pero no se en que estado está ahora mismo.
- Si quieres hacer una aventura gráfica con Unity, olvídate del AC, engorroso y limitado, y ya puedes aprender a programar y a conocer Unity bien, porque una AG no es algo sencillo. lleva mucho trabajo 

Que Unity mola mucho, pero no es la panacea y no es lo mas adecuado para todo, aunque se pueda hacer, que se puede. Que mola mucho hacer cosas sin saber programar, a mi me mola hacer naves espaciales, pero no se nada de aeronáutica. Y desde luego seguro que a todos nos mola hacer un Triple A, pero hay que empezar por un Flappy Bird.

 

Share this post


Link to post
Share on other sites

Ponle un collider invisible en un objeto vacío frente al moñaco, y que cuando se choque con ese collider se active todo el código.A ver que tal.

Share this post


Link to post
Share on other sites

×
×
  • Create New...