Noticias

¡RECUERDA QUE SI ERES UN NUEVO USUARIO, DEBES PRESENTARTE PARA PODER PUBLICAR MENSAJES! | TENEMOS CANAL OFICIAL DE TELEGRAM: t.me/unity3dspain

Mover y rotar personaje con teclas WASD

Iniciado por al_aranda, Noviembre 15, 2015, 07:13:21 PM

Tema anterior - Siguiente tema
Noviembre 15, 2015, 07:13:21 PM Ultima modificación: Noviembre 15, 2015, 07:18:38 PM por al_aranda
Hola a todo el mundo! Éste es mi primer post en este foro, y después de llevar un rato mirando posts similares (los hay) no he encontrado nada con lo que poder solucionar mi problema 100%. Hace unos meses decidí escoger como tema del Proyecto Final de Grado la creación de un Videojuego (en que momento jeje). He diseñado, modelado, rigueado y animado los personajes, y ahora es momento de pelearme con Unity (es la primera vez que hago un proyecto de desarrollo de videojuegos, lo mío es más la parte de diseño y animación 3D). El videojuego será un shooter en 3a persona muy sencillo. He conseguido poder mover y rotar al personaje con las teclas WASD y el mouse, tal y como he visto en uno de los tutoriales oficiales de Unity
).">
 
Pues bien, el problema es que en el tutorial se utilitza un muñeco sin piernas, con lo cuál éste puede ir en cualquier dirección sin importar que la animación de moverse se vea afectada. En mi caso, el personaje tiene piernas, con lo cuál al moverse hacia adelante no hay ningún problema, pero al moverse hacia atrás o hacia los lados, pues claro, se sigue reproduciendo la animación de correr (hacia adelante) y queda un poco mal. Llegados a este punto, y después de investigar, he decidido que no usaré el mouse para rotar el personaje, y que solo usaré las teclas WASD. De este modo lo que persigo es poder mover al personaje de manera que este siempre se "desplace" hacia adelante, es decir, que si estás apretando W y de repente quieres ir hacia atrás apretando S, pues lo que quiero es que el personaje rote 180º y vaya hacia atrás (no sé si me explico). De momento he sido capaz de poder mover el personaje (W y S) y de rotarlo (A y D), pero claro, el problema es que si aprieto estas últimas teclas, el personaje rota sobre su propio pívote de manera infinita hasta que no se suelta la tecla, y lo que yo quiero es que el personaje rote en dirección a la tecla que se ha presionado. Por ejemplo, que se dirija hacia arriba si se presiona W, hacia la izquierda si se presiona A, etc., y que si se sigue presionando esa tecla, se desplace en esa dirección... Como ejemplo sería un movimiento de desplazamiento estilo Mario 64, pero en vez de usar Joystick pues solo las teclas WASD. Sé que es un poco rollo todo lo que acabo de explicar, y no sé si se ha entendido. Muchas gracias de antemano y perdón por si hay realmente algún post donde se explique ésto (no he sido capaz de encontrarlo). 

Podrias implementar animation controller para lo primero que funciona a partir de eventos, por lo que usarias animaciones para las diferentes direcciones, y para lo segundo es tema vectores y posicion con respecto a la camara que es un poco mas complicado, pero todo depende de lo que quieras lograr.

Muchas gracias por responder. Pero se podrían activar distintas animaciones según si el personaje se mueve hacia delante, hacia atrás o hacia los lados? Es lo que más me gustaría conseguir, pero la verdad, por el tiempo que tengo y por mis conocimientos, lo veo algo super chungo... Habría alguna función para eso?De no ser posible eso (yo de hecho ya lo había descartado) me gustaría poder mover a mi personaje con las teclas WASD pero siempre con la animación de correr hacia adelante, y que según la tecla que pulses, pues el personaje rote hacia esa dirección y siga desplazándose. Es que no sé si me explico con esta última parte.Muchas gracias!

Lo primero es manejar una amigable interfaz que con un par de tutoriales se aprende y te ahorra un monton de trabajo para las transacciones, y para lo segundo si tienes una camara fija al personaje cambiale la rotacion del personaje y ya esta, ademas de utilizar boleanos para que tu personaje no gire.

Noviembre 15, 2015, 09:17:00 PM #4 Ultima modificación: Noviembre 15, 2015, 09:21:20 PM por al_aranda
Imagínate que la cámara no está por detrás del personaje, sino encima (no exactamente de forma perpendicular a éste, pero arriba) de manera que se verá buena parte del escenario. La cámara solo seguirá al personaje en traslación, no en rotación, de manera que sólo rotaría el personaje. Sería un estilo como ésto https://www.youtube.com/watch?v=WDUORDhHCm8 Gracias!

Y bueno solo es cuestion de inputs (que ya los tienes, wasd) y algunas condiciones con respecto a su posicion inicial.
Por ejemplo, estamos en la posicion inicial forward, si apretamos la W esta no girara ya que detectamos que ya esta en tal, por lo cual ignora esto y avanzamos.
Si presionamos la A vemos que estando en w(forward) para llegar a A(left) se debe restar -90 grados y asi respectivamente.
No se si logras comprender.

vale, pensé en eso de los grados, pero no sabía si sería una forma óptima. Entonces lo suyo seria obtener los inputs "Horizontal" y "Vertical" y a partir de ahi crear una serie de condicionales del estilo: si apreto la tecla A entonces obtener posicion del personaje y rotarlo -90º. Pero entonces habría que hacer tantos condicionales como opciones posibles no? porque tu puedes rotar desde forward a left, pero tambien desde forward a back, o desde back a right, etc...



he hecho lo siguiente. El resultado se parece más a lo que buscaba, pero no me acaba de convencer...
 public float speed = 6f;
    Animator animPlayer;    void Awake()
    {
        animPlayer = GetComponent<Animator>();
    }
    
    // Update is called once per frame
    void Update () {        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");
        Move();
        Animating(h, v);
    }    void Move()
    {
        
        if (Input.GetKey(KeyCode.W))
        {
            transform.Translate(0f, 0f, speed * Time.deltaTime, Space.World);
            transform.eulerAngles = new Vector3(0, 0, 0);
        }        if (Input.GetKey(KeyCode.S))
        {
            transform.Translate(0f, 0f, -speed * Time.deltaTime, Space.World);
            transform.eulerAngles = new Vector3(0, 180f, 0);
        }        if (Input.GetKey(KeyCode.A))
        {
            transform.Translate(-speed * Time.deltaTime, 0f, 0f, Space.World);
            transform.eulerAngles = new Vector3(0, -90f, 0);
        }        if (Input.GetKey(KeyCode.D))
        {
            transform.Translate(speed * Time.deltaTime, 0f, 0f, Space.World);
            transform.eulerAngles = new Vector3(0, 90f, 0);
        }
    }    void Animating(float h, float v)
    {
        bool idle = ((h == 0) && (v == 0));
        animPlayer.SetBool("IsMoving", !idle);
    }

Noviembre 16, 2015, 02:16:58 PM #10 Ultima modificación: Noviembre 16, 2015, 02:20:31 PM por Antonio
Hola!

Bueno, el grupo de condiciones comprobando tecla por tecla, pues es una manera, pero no creo que sea la más usada, de hecho, en ese código que has puesto, ya tienes otros inputs que podrían reducir las línes un poquito.

En la función Update estás llamando estas variables:
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
Los ejes (axis) "Horizontal" y "Vertical" puedes verlos en el menú Input ( Edit -> Project Settings -> Input), ahí podrás ver que esos ejes se corresponden con las teclas WSAD y las flechas arriba, abajo, izquierda y derecha. Es decir, cuando pulsas la tecla D, o la flecha derecha, el GetAxisRaw ("Horizontal") se pone a 1. Si pulsas la A o la flecha izquierda, el GetAxisRaw ("Horizontal") se pone a -1 y así... Y luego estos datos los guardas en las variables h y v, pero luego no los usas.

Si miras el código del character controller de ese shooter, verás que lo que hace es usar esos inputs para crear un vector de dirección, que es el que se utiliza para decirle al personaje en qué dirección moverse,  (tu en tus condicciones, sabes que Translate está usando otro vector de dirección, por eso pones cada una en función del (x,y,z). El código del tutorial también tiene en cuenta eso.
   void Move (float h, float v)
{
   movement.Set (h,0,v);
   movement = movement.normalized * speed * Time.deltaTime;

   playerRigidbody.MovePosition (transform.position + movement);
}
Más corto... movement = movement.normalized * speed * Time.deltaTime;
Esto lo que hace es 1º normalizar el vector, es decir, hacer que su longitud siempre sea 1, (cuando se pulsa 1 sóla tecla, la longitud se pone a 1, pero cuando se pulsa 2 teclas (1,0,1) la longitud es un poco mayor, por lo que si no la normalizamos, el jugador se movería más rápido al moverse en diagonal). Una vez normalizado a 1, multiplica ese vector por la velocidad (a lo mejor 1 es muy lento... pues le aumenta la velocidad aquí, también si hiciera falta reducirla, porque esté andando, atrapado o lo que sea, pues se atacaría por aqui). Y finalmente lo multiplica por el misterioso Time.deltaTime que es lo que hace que la distancia recorrida sea por segundo, y no por ciclo. (Si no estuviera, el personaje se movería más distancia por segundo en ordenadores que ejecuten el juego a 60 frames que en los ordenadores que lo ejecuten a 30 frames).

/---------------------/Si tomas ese vector de dirección, puedes reutilizarlo para más cosas, por ejemplo, para gestionar las animaciones. En el esquema del animator, puedes crear un "Blend Tree" que se gestione por 1 o 2 variables floats (por ejemplo, puedes poner que una sea la velocidad de movimiento (aunque tú estás usando sólo un bool para andar de modo que esta no es imprescindible) y la otra que sea le diga qué animaciones lanzar (adelante, atrás, lateral izquierda, lateral derecha, diagonales...).

Así que lo único que faltaría sería averiguar esa variable que decide las animaciones, teniendo el vector dirección, sabemos hacia qué lugar se está moviendo el personaje, también sabemos que la animación depende del punto a donde está mirando (que en ese ejemplo de Unity, es la dirección del ratón), luego entiendo que la animación varia dependiendo del ángulo entre el vector dirección y la dirección a la que está mirando (que no es más que otro vector3, punto de destino - punto de origen) (o lo que es lo mismo, punto donde está apuntando el ratón - transform.position del personaje).Pensando en ángulos, podríamos ver lo siguiente, Si el ángulo entre la dir movimiento y la dir de mira es:
<= 45º, entonces hay poca diferencia, el personaje ejecutaría la animación de correr adelante.
>45º && < 135º, entonces el personaje está moviéndose lateralmente.
>135º, entonces el personaje se está moviendo hacia atrás. Este sistema funciona pero hay un problema como ves, y es en los laterales. ¿Cómo diferenciar el lateral izq del lateral derecho? ya que muchas funciones del Ángulo, sólo dan la diferencia en positivo, no distingue entre un lado u otro. Por ello hay que complicar la cosa un poco más, en mi caso uso la función Atan ( http://docs.unity3d.com/ScriptReference/Mathf.Atan2.html">http://docs.unity3d.com/ScriptReference/Mathf.Atan2.html ).

La Atan2 permite calcular el ángulo de manera más precisa por lo que sí nos dirá si es +90 o -90, pero si lo usas sin más sobre el vector dirección, verás que los resultados están fijados al espacio global (si el personaje está mirando hacia +z, entones te dirá los ángulos correctamente, pero si el personaje esta mirando hacia +X es decir, hacia la derecha de la pantalla, entonces no te dirá los resultados exactos) por eso antes de usar la función Atan, necesitamos crear un nuevo Vector3 que transforme ese vector de movimiento adaptado al espacio global, en un vector de movimiento adaptado al game object del personaje. 

Ejemplos:
- El personaje está mirando hacia la derecha y se está moviendo hacia la derecha, en el espacio global, el personaje se está moviendo en dirección (1,0,0) pero en el espacio local, el personaje se está moviendo en dirección (0,0,1), es decir, el personaje se está moviendo hacia su flecha azul que en ese momento, está apuntando hacia la derecha de la pantalla.
- El personaje está mirando hacia la pantalla, pero se está moviendo hacia derecha de la pantalla, (el personaje se estaría moviendo lateralmente), en este caso, su dirección de movimiento global es (1,0,0) pero su dirección de movimiento local sería (-1,0,0) (el personaje se está moviendo hacia su izquierda).

Por esto, necesitamos transformar la dirección global del movimiento a la dirección local del personaje, eso se puede hacer con la función InverseTransformDirection. Algo así:
Vector3 localMove = transform.InverseTransformDirection(movement);
Mira como la aplica en transform (minúscula) que es el del propio personaje. Así, ya con el localMove podemos calcular el ángulo que buscamos con Atan2:
float movangle = Mathf.Atan2(localMove.x, localMove.z) * Mathf.Rad2Deg;
Y finalmente, llevas ese movangle al Animator para gestionar con él la animación que se debe ejecutar. 

Muchísimas gracias Adngel por tan detallada respuesta! Entonces sería posible crear los mismos controles que en el tutorial (WASD y mouse para apuntar) pero lanzando diferentes animaciones según si el personaje va hacia adelante, hacia atrás o hacia los lados no? La verdad que cuando vi ese tutorial, no me plantee el que mi jugador fuera a tener piernas y que tal hecho conllevaría todo este problema... Entonces vamos por pasos. Primero voy a intentar hacer lo del Blend Tree. Con esto 
playerRigidbody.MovePosition (transform.position + movement);
ya tendríamos el vector de dirección, y ahora haría falta crear otro Vector que será el que guarde la posición hacia la cuál esta mirando el personaje (que será la posición del mouse), no?

Noviembre 16, 2015, 08:21:19 PM #12 Ultima modificación: Enero 01, 1970, 01:00:00 AM por Antonio
Sí, es posible.
eature=oembed" frameborder="0" allowfullscreen="true"> Sobre la configuración del Blend Tree, yo la tengo así: En mi caso, hago algunos tejemanejes más, por eso mi ángulo guardado en Turn, va entre -1 y 1, en vez de -180 y 180, por otro lado, también tengo la velocidad speed para gestionar en el mismo Blend tree, la animación de andar y idle. Sobre las velocidades es algo más particular de las animaciones que estoy usando (algunas tenían una velocidad mayor que la deseada, por eso la redución).


La entrada a este Blendtree se hace desde fuera, algo parecido a lo que tendrás tu, es el estado principal y si el personaje muere, pues cambia de estado / animación. (A parte tengo otro Blendtree similar para cuando no lleve armas equipadas pero en fin, eso va para otro día XD ).

 Sí, la posición hacia la que mira, debería ser el ratón, a veces, se acorta código diciendo que la posición a la que mira, es el vector forward del personaje, y en algunos casos puede funcionar. A mi no me funcionó, porque al hacerlo así, el rayo de disparo quedaba desviado. (El vector Z del personaje no se corresponde exactamente con la Z del punto de disparo, y el rayo eléctrico salía más torcido también). Así que nada, al final yo opté por buscar el punto donde se da al ratón.

En el tutorial del muñeco sin piernas, también tienes el código de cómo tomó dicha posición del ratón. 

Noviembre 21, 2015, 03:41:10 AM #13 Ultima modificación: Enero 01, 1970, 01:00:00 AM por Andrewu-12
[quote author=Adngel" data-ipsquote-contapp="forums" data-ipsquote-contenttype="forums" data-ipsquote-contentclass="forums_Topic" data-ipsquote-contentid="34433" data-ipsquote-contentcommentid="123588">Sí, es posible.
eature=oembed" frameborder="0" allowfullscreen="true" __idm_id__="179201"> Sobre la configuración del Blend Tree, yo la tengo así:Mecanim.thumb.jpg.d96cd8b2a9026b80272ca8

Etiquetas: