Menu

domingo, 8 de noviembre de 2015

Unity 2D pendientes sin matemáticas

See this post in english


Recientemente me he topado con el problema de controlar pendientes (cuestas, laderas, rampas) en un plataformas 2D. Hace tiempo me encargué de la programación de Fat Ninja Adventure. Eso fue antes de que saliese el 2D toolkit de Unity, por lo que tuve que programar todo el motor 2D :_(  En ese caso, no utilicé físicas para controlar al personaje 2D. Pero por fin he tenido o mejor dicho, he hecho un hueco para hacer un controlador de personaje 2D con físicas. Lo cual tiene sus propios problemas intrínsecos. Uno de ellos es el que nos ocupa.

Concretamente, el problema es que el personaje resbala por la pendiente, debido al cálculo de la gravedad y las fuerzas. Esto es algo que no aparece resuelto en el video tutorial oficial de Unity -al menos hasta donde yo sé. He encontrado soluciones matemáticas al problema, pero no me funcionan perfectamente en mi implementación. Además, debido a que en el curso del que soy profesor no tenemos mucho tiempo para extendernos con explicaciones matemáticas, siempre intento resolver este tipo de problemas sin fórmulas :)

Así que buscando una alternativa, se me ha ocurrido una solución que en mis pruebas está funcionando bastante bien, pero que dejo aquí para debate abierto. Si alguien quiere comentarla, que explique en los comentarios los pros y los contras que pueda econtrar, o cómo lo ha resuelto él. 


public void ComprobarPendiente()
    {
        // Si el personaje está en el suelo, y no se ha pulsado el input de movimiento ni el de salto: 
        if (enTierra && axisRaw == 0 && !salto)
        {
            RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down, 10, capaSuelo); 
 
            // Comprobamos si estamos en la pendiente 
            if (hit && Mathf.Abs(hit.normal.x) > 0.1f)
            {
 
                // Congelamos las constraints del Rigidbody2D, y ponemos su velocidad a 0
                rigidbody.constraints = RigidbodyConstraints2D.FreezeAll;
                rigidbody.velocity = Vector2.zero;
            }
        }
        else
        {
 
            // Si estamos moviéndonos o saltando, descongelamos pero dejamos la rotación congelada
            rigidbody.constraints = RigidbodyConstraints2D.None;
            rigidbody.constraints = RigidbodyConstraints2D.FreezeRotation;
        }
    }

   
Para la explicación, doy por supuesto que ya sabes cómo detectar que el personaje está en el suelo. 

1.- Puedes llamar a la función ComprobarPendiente al final de tu Update o FixedUpdate -dependerá de cómo tengas el código estructurado. La función necesita saber si el personaje está en el suelo, si el eje horizontal ha sido pulsado este frame y si se ha pulsado el salto. Son las 3 variables que aparecen en el primer IF

2.- Si el personaje está en el suelo, y no estamos pulsando ningún input, resbalará por la pendiente. Para prevenir esto, emitimos un Raycast hacia abajo, y comprobamos la normal del punto de impacto. De esta forma comprobamos si estamos en una pendiente. En caso afirmativo, congelamos todas las constraints del Rigidbody2D. Éste sería el "truco".

Esto evitará que el personaje se resbale pendiente abajo por culpa de la gravedad. Lo haremos sólo cuando el personaje está "Idle". También pondremos la velocidad a 0. Es posible que funcione modificando el atributo GravityModifier y ponerlo a 0.


3.- Si estás moviendo al personaje, o éste está en el aire, entonces descongelamos las constraints, aunque dejamos congelada la rotación.

No puedo predecir si esta técnica responderá bien para todos los casos, por eso la dejo aquí a debate. En mi caso al menos está funcionando como se desea. Podéis ver el vídeo de ejemplo debajo.





No hay comentarios:

Publicar un comentario