Unity Gravedad. RigidBody

De MediaWiki
Ir a la navegación Ir a la búsqueda

Introducción

  • Más información en:


  • Unity permite hacer uso del denominado motor de físicas (Physics Engine).
Este es un software que permite emular el comportamiento real de los objetos cuando cuando son afectados por la gravedad.


  • Podemos diferenciar dos aspectos:
  • Sobre un objeto (GameObject) actúa la gravedad y por tanto existe una fuerza que hace que caiga.
Relacionado con la gravedad existen varios parámetros como el rozamiento, la fuerza de la gravedad,...
  • Cuando dos cuerpos 'chocan' estos reaccionan desplazándose en base a las fuerzas y dirección.
Esto lo veremos en un punto posterior, son los denominados colliders.


Unity3d gravedad 1.jpg



  • MUY IMPORTANTE: En el momento en que asociamos un RigidBody a un GameObject, este debería desplazarse siguiendo las reglas del motor de físicas, en base a 'fuerzas' que son aplicadas sobre el GameObject.
Por lo tanto no debemos de moverlo haciendo uso de su componente transform, como hemos hecho hasta ahora, a no ser que esté marcada la opción isKinematic' en cuyo caso no se verá afectado por el motor de físicas, como veremos más adelante.



Añadiendo un RigidBody

  • El objeto no para de caer ya que no hemos visto los Colliders, que son los elementos dentro del motor de físicas, que hacen que 'choquen' unos GameObjects con otros.



Parámetros del RigidBody

Unity3d gravedad 1.jpg


  • Mass: La masa en kilogramos.
Recordar que aumentar la masa no supone caer más rápido :)
Supone que necesitaremos más fuerza para mover el objeto.
  • Drag: Es la resistencia del aire al moverse el objeto como consecuencia de una fuerza aplicada al mismo.
  • Un valor 0 indica que no hay resistencia.
  • Un valor infinito indica que se para inmediatamente.
En el ejemplo anterior, podemos escribir Infinity en la propiedad Drag y observaremos como el camión no cae, pero no porque la gravedad no 'tire' de él, sino por la modificación en la propiedad Drag.
Unity3d gravedad 6.jpg
  • Angular Drag: Es la resistencia del aire al rotar el objeto. Un valor 0 indica que no hay resistencia (no tiene el efecto anterior de poner Infinity).
  • Use Gravity: Indica si al GameObject le afecta la gravedad. En caso de desmarcarlo, el camión no caería pero si tuviera un Collider se vería afectado por las colisiones con otros GameObjects.
  • Is Kinematic: Esta opción la veremos cuando veamos los Colliders. Sirve para que podamos tener asociado un Collider a un GameObject que se va a desplazar en el juego, pero que no se vea 'afectado' por el motor de físicas. Por tanto, vamos a poder controlarlo como hemos hecho anteriormente, mediante su componente transform y mediante scripts.
  • Interpolate: No deberíamos necesitar tocas esta opción. Indica la forma en como el motor de físicas calcula las posiciones por las que debe pasar el GameObject para llegar a su destino en base a las fuerzas aplicadas sobre él. Si vemos que el movimiento de un GameObject va a saltos o se comporta de forma extraña podríamos escoger alguna de las opciones disponibles.
  • Collision Detection: El valor por defecto es 'Discrete'. Cuando tenemos un GameObject que se mueve a mucha velocidad, puede suceder que se produzca una colisión entre dos frames del movimiento y que dicha colisión no se detecte. Para evitarlo podemos cambiar el valor de este parámetro:
  • Continous: Utilizado para detectar cuando el GameObject pasa a través de un Static GameObject que no tenga RigidBody asociado o bien a través de un GameObject con un RigidBody que tenga el modo 'Colission Detection' a 'Discrete'.
  • Continous Dynamic: Utilizado para detectar el GameObject pasa a través de otro GameObject que tenga el modo 'Colission Detection' a 'Continous' o 'Continous Dynamic'
  • Continous Speculative: Requiere menos carga de CPU que en el caso anterior, y lo que hace es 'presuponer' la trayectoria que va a seguir el GameObject para determinar si va a ver una colosión.
Más información y ejemplos en este enlace: https://docs.unity3d.com/Manual/ContinuousCollisionDetection.html
  • Constraints:
Unity3d gravedad 7.jpg
Al marcar un eje hacemos que dicho eje no se vea afectado por las fuerzas al producirse una colisión.
Podemos evitar que sufra cambios tanto cuando el movimiento implique un cambio de posición (Freeze Position) como cuando implique un cambio en la rotación (Freeze Rotation).
Veremos ejemplos de esto cuando explique los Colliders.




Fuerzas constantes


  • Unity permite aplicar una fuerza constante a un GameObject haciendo uso la componente ConstantForce dentro de la sección Physics.
Recordar que dicha fuerza se aplicará sobre el RigidBody asociado al GameObject y es lo que hará que 'se mueva'.
Ejemplo de uso podría ser un proyectil.
Unity3d gravedad 8.jpg


  • Al añadir dicho componente podemos ver las siguientes propiedades:
Unity3d gravedad 9.jpg
El valor que se introduzca será la aceleración en metros por segundo al cuadrado, que queramos que tenga el cuerpo.
  • Force: Mueve el GameObject. Es la fuerza en metros por segundo aplicada al RigidBody en cada uno de los ejes con respecto al mundo real, es decir, los ejes sin rotar.
  • Relative Force: Mueve el GameObject. Es la fuerza en metros por segundo aplicada al RigidBody en cada uno de los ejes con respecto a sus ejes locales (pueden estar rotados).
  • Torque: Rota el GameObject. Es la fuerza en metros por segundo aplicada al RigidBody en cada uno de los ejes con respecto al mundo real, es decir, los ejes sin rotar.
  • Relative Torque: Rota el GameObject. Es la fuerza en metros por segundo aplicada al RigidBody en cada uno de los ejes con respecto a sus ejes locales (pueden estar rotados)


Veamos diferentes ejemplos con el camión del ejercicio anterior.




Movimientos en un RigidBody

  • Para mover un GameObject que está sujeto a las leyes físicas (asociado con un RigidBody y la propiedad isKinematic no está marcada) no lo podemos hacer modificando su propiedad Transform.
Es necesario utilizar los métodos asociados a su RigidBody.
  • Otro cambio importante es que las modificaciones que hagamos sobre su RigidBody las tenemos que realizar en el método FixedUpdate() del script.
A diferencia del método Update():
  • FixedUpdate siempre es llamado en el mismo intervalo de tiempo (update puede varias de un frame a otro).
  • Las modificaciones realizadas sobre el RigidBody que implicen un cálculo en la física, es realizado inmediatamente.


  • Por lo tanto, al usar la física:
  • Debemos actuar sobre el RigidBody y no sobre su componente Trasnform.
  • Debemos hacerlo sobre el método FixedUpdate.



Aplicando Fuerzas

  • Una de las formas más comunes de mover un RigidBody es aplicando una fuerza al mismo.
Para ello podemos llamar:
  • El primer parámetro es un Vector que indica la dirección y la fuerza que se va a aplicar.
  • El segundo parámetro indica el tipo de fuerza (mirar la ayuda para ver los diferentes tipos).
  • Puede aplicarse para modificar su velocidad (se movería a una velocidad fija). Dentro de esta tenemos dos variantes si queremos que tenga en cuenta la masa. Si se tiene en cuenta, necesitaremos una fuerza mayor para mover el gamobject a la velocidad que queramos.
  • Puede aplicarse para tener una aceleración (acelera hasta la velocidad indicada y después iría 'parando' en función del rozamiento. Igual que en el caso anterior, podemos hacer que dependa de la masa.
  • El primer parámetro es un Vector que indica la dirección y la fuerza que se va a aplicar a la rotación del GameObject. Con esta fuerza rotamos pero no desplazamos el GameObject
  • El segundo parámetro indica el tipo de fuerza (mirar la ayuda para ver los diferentes tipos), como en el caso anterior.
  • Podéis consultar en este enlace otros métodos diferentes para aplicar fuerzas, como un emular el efecto de una explosión.


  • Veamos un ejemplo.
Creamos un script con el siguiente código:
 1 using UnityEngine;
 2 
 3 public class Ej5_AplicandoFuerzas : MonoBehaviour {
 4 
 5     [SerializeField]
 6     private float fuerza;
 7     [SerializeField]
 8     private float aceleracion;
 9 
10     private Rigidbody rb;
11 
12     void Start()
13     {
14         rb = GetComponent<Rigidbody>();
15     }
16 	
17 	void FixedUpdate () {
18 
19         if (Input.GetKeyDown(KeyCode.Space))
20         {
21             if (gameObject.name.Equals("AutobusFuerza"))
22             {
23                 rb.AddForce(transform.forward * fuerza, ForceMode.VelocityChange);
24             }
25             if (gameObject.name.Equals("AutobusAceleracion"))
26             {
27                 rb.AddForce(transform.forward * aceleracion, ForceMode.Acceleration);
28             }
29 
30         }
31 
32     }
33 }


  • Asociamos dicho script a un GameObject (en el ejemplo está sobre el autobús) y creamos un Prefab basado en ese GameObject.
Nota: En el ejemplo se hace uso de un Empty GameObject para que el eje Z del morro del autobús coincida con el eje Z del mundo.
  • A continuación arrastramos dos veces el Prefab a la escena para crear dos GameObjects y cambiaremos sus nombres por: AutobusFuerza y AutobusAceleracion.




Métodos de movimiento del RigidBody



NOTA IMPORTANTE: Debemos de tener cuidado de emplear los métodos MovePosition, MoveRotation o position, ya que dichos métodos mueven el RigidBody sin hacer uso de la física, por lo que el resto de GameObjects puede que no 'reaccionen' de forma correcta. Por ejemplo, en el caso de que un objeto esté encima de otro y que este se mueva, no lo 'llevará' con él.
Nota: Recordar que interpolar es 'inventarse' los puntos intermedios que van desde un punto inicial a otro final.
En base a la rotación y posición en el punto inicial, el sistema determina cual debería ser la rotación y desplazamiento para poder llegar al punto final.
  • Veamos un ejemplo haciendo uso de Velocity y AngularVelocity.
Como aún no vimos los colliders, debéis de desmarcar la opción de 'Use Gravity' del RigidBody.
Creamos el siguiente script y lo asociamos al camión:
 1 public class Ej6_MovimientosRigidBody_Velocity : MonoBehaviour {
 2 
 3     [SerializeField]
 4     public float velocidadMovimiento;
 5 
 6     private Vector3 rotacion;
 7     private Rigidbody rb;
 8 
 9     private void Reset()
10     {
11         velocidadMovimiento = 5;
12     }
13 
14 	// Use this for initialization
15 	void Start () {
16         rb = GetComponent<Rigidbody>();
17         rotacion = new Vector3(0, 45, 0);       // El camión rotará 45 grados por segundo en el eje Y
18 	}
19 	
20 	// Update is called once per frame
21 	void FixedUpdate () {
22 		
23         if (Input.GetKeyDown(KeyCode.R))    // Reset posición y rotación
24         {
25             rb.rotation = Quaternion.identity;
26             rb.position = Vector3.zero;
27         }
28         if (Input.GetKey(KeyCode.UpArrow))
29         {
30             rb.velocity = transform.forward*velocidadMovimiento;       // Dependerá del Drag del RigidBody que quede se desplace más o pare en seco
31         }
32         else if (Input.GetKey(KeyCode.DownArrow))
33         {
34             rb.velocity = -transform.forward * velocidadMovimiento;    // Dependerá del Drag del RigidBody que quede se desplace más o pare en seco
35         }
36         if (Input.GetKey(KeyCode.LeftArrow))
37         {
38             rb.angularVelocity=-transform.up*velocidadMovimiento;  // Dependerá del Angular Drag del RigidBody que quede rotando más tiempo o pare en seco
39         }
40         else if (Input.GetKey(KeyCode.RightArrow))
41         {
42             rb.angularVelocity = transform.up * velocidadMovimiento;  // Dependerá del Angular Drag del RigidBody que quede rotando más tiempo o pare en seco
43         }
44     }
45 }



  • Veamos otro ejemplo haciendo uso de MovePosition y MoveRotation:
Recordar que en este caso, no se está haciendo uso del motor de físicas y por lo tanto puede que no funcione correctamente al interaccionar con otros objetos.
Creamos el siguiente script:
 1 using UnityEngine;
 2 
 3 public class Ej6_MovimientosRigidBody : MonoBehaviour {
 4 
 5     [SerializeField]
 6     private float velocidadMovimiento;
 7 
 8     private Vector3 rotacion;
 9     private Rigidbody rb;
10 
11     private void Reset()
12     {
13         velocidadMovimiento = 5;
14     }
15 
16 	// Use this for initialization
17 	void Start () {
18         rb = GetComponent<Rigidbody>();
19         rotacion = new Vector3(0, 45, 0);       // El camión rotará 45 grados por segundo en el eje Y
20 	}
21 	
22 	// Update is called once per frame
23 	void FixedUpdate () {
24 		
25         if (Input.GetKeyDown(KeyCode.R))    // Reset posición y rotación
26         {
27             rb.rotation = Quaternion.identity;
28             rb.position = Vector3.zero;
29         }
30         if (Input.GetKey(KeyCode.UpArrow))
31         {
32             rb.MovePosition(transform.position + transform.forward * velocidadMovimiento * Time.deltaTime);
33         }
34         else if (Input.GetKey(KeyCode.DownArrow))
35         {
36             rb.MovePosition(transform.position + -transform.forward * velocidadMovimiento * Time.deltaTime);
37         }
38         if (Input.GetKey(KeyCode.LeftArrow))
39         {
40             Quaternion qt = Quaternion.Euler(-rotacion * Time.deltaTime);
41             rb.MoveRotation(rb.rotation*qt);
42         }
43         else if (Input.GetKey(KeyCode.RightArrow))
44         {
45             Quaternion qt = Quaternion.Euler(rotacion * Time.deltaTime);
46             rb.MoveRotation(rb.rotation * qt);
47         }
48     }
49 }
Asociar el script al camión y acordaos de deshabilitar el anterior.
Básicamente lo que hace el script es mover el GameObject asociado haciendo uso de su RigidBody mediante las teclas de los cursores.
Si pulsamos la tecla R situará el GameObject en la posición (0,0,0) y sin rotación.


  • Al ejecutar el juego, teniendo un GameObject con el script asociado, podemos moverlo pero haciendo uso de su RigidBody, no de su componente transform.
Fijarse que el método que estamos utilizando es el FixedUpdate.


Unity3d mov rigidbody 4.jpg





Unión de GameObjects haciendo uso del Physics Engine

  • Unity permite 'unir' varios rigidbody siguiendo las leyes físicas.
Por ejemplo:
  • Puedo emular el comportamiento de una flecha con dos rigidbody, uno para la 'cabeza' de la flecha y otro para 'el cuerpo'.
  • Puedo tener un arma magnética que al arrojarla a un robot, permanezca 'pegada' al mismo.
  • Puedo tener una espada con dos rigidbody formando la hoja de la misma y que en función de la fuerza con la que golpee se rompa la espada.


Unity3d joints 1.jpg


  • Todos esos 'efectos' los voy a conseguir con los denominados Joints.


  • Existen varios tipos diferentes en función de lo que queramos conseguir:
  • FixedJoints: Dos rigidbody unidos que pueden 'romperse' al aplicar una determinada fuerza.
  • SpringJoint: Permite conectar dos rigidbody y que la distancia entre ellos, al aplicar una fuerza, varíe como si estuvieran unidos por un muelle que se puede estirar y o encoger. El movimiento de los dos cuerpos puede ser independiente (rotación, traslación).
  • CharacterJoint: Permite mover a la vez varias partes del cuerpo, como las manos, hombros,...Ejemplo de uso. También es utilizado para crear efectos RagDoll, 'muñeca de trapo', cuando un cuerpo de una persona cae por una escalera y emulamos sus movimientos 'insconscientes' de brazo, piernas y cabeza. Un ejemplo de uso.
  • HingeJoint: Efecto visagra, como abrir una puerta o un letrero movido por el viento. Ejemplo de uso.


  • Vídeos:






Enlace a la página principal del curso





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