Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 04/23/2012 in Blog Entries

  1. 6 points
    En la anterior entrada hablé sobre trigonometría, repasando las razones trigonométricas y viendo el teorema de pitágoras. Si no lo recuerdas o has llegado nuevo aquí, míralo antes de continuar (enlace debajo). Trigonometría Vectores Matrices Funciones Introducción Si estás familiarizado con motores de videojuegos como Unity, conocerás el concepto de vector ya que se usa habitualmente. Pero ¿qué es realmente un Vector? Un vector básicamente es una flecha que apunta en una dirección y que tiene una longitud concreta: Esto sería un ejemplo de un vector y cualquier flecha apuntando en cualquier dirección sería otro ejemplo. Pero ¿cómo representamos esto? Para poder hacerlo tenemos que llevarnos nuestro vector a un sistema de coordenadas (x e y) y establecer el origen del vector en el centro del sistema. Por si no recuerdas que era un sistema de coordenadas, es una forma de visualizar un punto en el espacio. En este caso el espacio es 2D por lo que se representa con dos líneas, una horizontal (representado por el valor X) y otra vertical (representado por el valor Y) que se cruzan en un punto, llamado centro: Así quedaría nuestro vector anterior dentro de un espacio de coordenadas. Esto, por ejemplo, podría representar el movimiento de una nave en su proceso de despegue. Comienza en la posición (0,0) y sigue en la dirección del vector hasta llegar a su objetivo. Teniendo en cuenta que X es el valor horizontal e Y el valor vertical, el vector sería (2,1) Pero ¿qué podemos hacer con esto? Como has visto puedes representar movimientos con ellos, por ejemplo. Pero para ver de forma práctica su uso vamos a usar Unity3D. Cualquier versión te servirá para este propósito puesto que solo vamos a tocar código básico. En mi caso usaré la versión 2020.1. Si usas otro motor la teoría sigue siendo válida pero tendrás que buscar la forma de adaptarlo por tu cuenta. Unity internamente usa vectores para colocar un objeto dentro de la escena (realmente esto es incorrecto ya que usa una matriz, pero no nos adelantemos). Si vas al componente Transform (crea un gameobject nuevo si no tienes ninguno) te encontrarás con este Vector3 (un estructura propia de Unity) que contiene 3 valores: Estos son la posición en X,Y,Z. Pero ¿pero qué son estos valores? Pues al igual que la gráfica anterior nos indican que desde el centro del mundo hasta la posición de este objeto el vector es (0,0,0). Vamos a ver qué podemos hacer con esto. Para ello creamos un objeto de nombre Player y otro de nombre Target. Los colocamos en diferente posición y le asignamos diferentes iconos para poder diferenciarlos: Recuerda que puedes cambiar el icono en la siguiente pestaña: Una vez hecho esto vamos a crear un nuevo script de nombre Player y lo vamos a añadir al objecto Player. En este script vamos a añadir una variable Transform pública de nombre target y vamos arrastrar nuestro Target a través del inspector. Con esto tendremos acceso al vector de posición del Target desde nuestro Player. Ya tenemos acceso a los dos vectores de posición, el del personaje y el del objetivo. Sabiendo esto ¿cómo hacemos que el personaje se mueva hacia el objetivo usando vectores? Pues aquí llega la parte interesante ya que si restas el vector de posición del objetivo con el vector de posición del personaje, el resultado es el vector necesario para llevar el player al objetivo. Veámoslo gráficamente creando la siguiente función: Si pulsamos play al añadir este código, veremos cómo el personaje se mueve hasta la posición del objetivo. Vale parece que se mueve, pero quiero más. ¿Que tal si medimos la distancia que hay entre uno y otro antes de moverlo? Para esto vamos a recordar la trigonometría que aprendimos en la parte anterior. Magnitud La magnitud de un vector es la distancia que hay entre dos puntos. En nuestro caso sería la posición del personaje y nuestro objetivo. Para calcularla solo tenemos que aplicar la fórmula de la distancia. Esta fórmula es simplemente el teorema de pitágoras disfrazado, y lo vamos a ver en la siguiente imagen: Como puedes ver, nosotros lo que queremos calcular es la distancia (h) por lo que podemos convertirlo en un triángulo añadiendo X e Y. Ahora solo tenemos que usar pitágoras para despejar h. ¿Lo recuerdas? Vamos a resolverlo en código. Para ello vamos a crear una clase estática nueva llamada BasicMath donde vamos a ir añadiendo nuestras funciones matemáticas a partir de ahora. Seguiremos usando Mathf (Unity) o Math (System) para realizar las operaciones básicas que se salen del objetivo de este tutorial. Mencionar que todos los cálculos e igualdades que haremos aquí serán basados en 2 dimensiones. Una vez se han entendido correctamente los cálculos, solo hay añadir la componente z. Esta sería nuestra implementación del teorema de pitágoras. Para ver la distancia simplemente podemos añadir un log en nuestra función MoveToTarget: Si estás familiarizado con Unity ya sabrás que por defecto nos soluciona todos los problemas de vectores con su estructura Vector. Podemos acceder directamente a la magnitud de un vector desde dentro del mismo de la siguiente forma: Como puedes ver, ambos resultados son iguales, aunque nuestra implementación es menos óptima que la que usa internamente Unity. Por lo que esta clase BasicMath y todo lo que contenga es únicamente para uso educativo. La magnitud tiene un pequeño problema y es que hacer una raiz cuadrada es algo costoso a nivel computacional. Por eso habitualmente se usa el valor antes de pasar por la raiz cuadrada. Seguirás teniendo un valor que representa la distancia pero será más eficiente. Podemos crear esta implementación de la siguiente forma y equivale al SqrMagnitud de la clase Vector: Esto es genial, tenemos al personaje moviéndose al target y conocemos su distancia hasta él. Pero no queremos que el player avance directamente hacia el objetivo, sino que lo haga poco a poco a la velocidad que nosotros queramos. Para esto vamos a necesitar normalizar nuestro vector. Normalización Normalizar un vector es cambiar su longitud a 1, manteniendo su dirección. En nuestro caso de ejemplo, el vector “dir” que tenemos. Al cambiar su longitud a 1 podemos controlar la velocidad de movimiento mediante un valor multiplicador. ¿Cómo calculamos el vector normalizado (no confundir con el Vector normal)? Dividiendo el vector por su magnitud: Podemos implementar rápidamente esto en nuestra clase BasicMath: Podemos ver que equivale al valor normalized de la clase Vector: Una vez tenemos el valor normalizado, podemos multiplicarlo por un valor y hacer que el personaje se mueva en función del tiempo: Vamos a tener que pasar nuestra normalización a Vector3 ya que estamos trabajando en dos dimensiones y la posición añade la Z (aunque como este caso sea 0). Si modificamos el valor speed podremos hacer que el personaje cambie su velocidad al moverse a través del vector. Antes de continuar me gustaría darte otra forma de mover un vector de un punto a otro. Pero en lugar de basado en la velocidad, basado en un valor comprendido entre 0 y 1. Cuando el personaje está en su posición inicial, el valor es 0 y cuando llega a su objetivo es 1. Esto es llamado interpolación (Lerp). Interpolación Para interpolar un vector simplemente vamos a sumarle al vector inicial, el vector de dirección al objetivo por un valor t (comprendido en 0 y 1). Veámoslo en código: La primera línea limita la t entre 0 y 1. La segunda simplemente suma al vector de origen, el vector dirección multiplicado por t. Si la t es cero devolverá el valor “a” y si es 1 devolverá el valor “b”. Con el siguiente código podremos probar esta funcionalidad nueva: Tenemos que crear un valor t (el atributo Range nos ayudará a limitarlo entre 0 y 1) y un Vector3 que guarde la posición inicial. Esto es necesario porque si le pasamos la posición actual al Lerp los resultados se irán actualizando al ir moviéndose y el resultado no será el que buscamos. Comentamos la anterior MoveToTarget y añadimos la nueva en la función Update. De esta forma si pulsamos Play tendremos un valor t en el inspector que si vamos moviendo entre 0 y 1 hará que nuestro player se desplace más o menos cerca del target. Hay otros tipos de interpolación que veremos en el apartado de Funciones en próximas partes de este tutorial. Una vez tenemos esto, vamos a pasar con una de las operaciones con vectores más importante. Producto Escalar El producto escalar (dot) es una operación entre dos vectores que nos devuelve un valor. Este valor nos da información muy útil sobre estos dos vectores: Si este valor es cero: el ángulo entre los dos vectores es 90 grados por lo que son totalmente perpendiculares. Si este valor es negativo: el ángulo es mayor a 90 grados. Cuanto menor sea este valor, mayor será el ángulo. Si este valor es positivo: el ángulo es menor que 90 grados. Cuanto mayor sea este valor menor será el ángulo. Aquí puedes experimentar visualmente con estos conceptos: https://www.fisicalab.com/apartado/producto-escalar La fórmula del producto escalar, siendo “a” y “b” dos vectores, es la siguiente: dot = ax * bx + ay * by Ya conocido el concepto vamos a practicar con él. Primero lo añadimos la función a nuestro BasicMath. Ahora añadimos la función Dot de testeo con lo siguiente: Esto calculará el producto escalar entre el vector del personaje y el vector de dirección. Como ves hemos utilizado el transform.up en lugar de la posición. Este valor indica la dirección del vector hacia arriba teniendo como referencia el tranform. Ya conocerás estos vectores si estás habituado a trabajar con Unity. Podemos verlos mejor aquí: Si usamos estos vectores a la hora de calcular el producto escalar, los resultados serán en función a la dirección y no en función de la posición. El vector de posición no nos indica hacia donde está apuntando, sino su posición con respecto al centro del mundo. Una posición por ejemplo de (3,4,0) no nos es útil para calcular el producto escalar, sin embargo el vector.up (0,1,0) sí, ya que es una dirección en este caso hacia arriba. También hemos normalizado los dos vectores antes del dot para que sea más fácil trabajar con el resultado. Activamos esta función en Update, comentando lo demás (puedes dejar el MoveToTarget si quieres). Si damos Play podemos ir moviendo el target y ver como va cambiando el producto escalar. Pero ¿de qué sirve conocer este valor?. Pues anteriormente vimos que este valor nos da información muy relacionada con los ángulos. De hecho podemos calcular el ángulo entre dos vectores conociendo los productos escalares. Ángulo Para calcular el ángulo tenemos la siguiente fórmula,siendo “a” y “b” vectores: θ = arcocoseno (dot(a, b)) Vamos a añadir esto a nuestro BasicMath. Este resultado estará en radianes por lo que crearemos dos funciones, una en radianes y otra que convierta el resultado a grados. Hemos normalizado ambos vectores antes de hacer el Dot. De esta forma lo hará desde dentro y podremos pasarle el vector sin normalizar. El valor Rad2Deg es el factor de conversión de radianes a grados que es 180 . Esto es un valor constante: No es necesario que añadas tanta precisión, puedes coger los decimales que quieras para este fin. Creamos ahora nuestra función Angle en el Player (puedes sustituir la función Dot ya que no la volveremos a usar). Volvemos a usar el Vector.up y añadimos la función al Update para verla. Para visualizar mejor el ángulo, voy a añadir en el OnDrawGizmos el siguiente código. Esto crea dos líneas que simular el comportamiento de los vectores. Si damos al play podremos ver dos líneas que forman un triangulo, el angulo entre las dos es el que nos está mostrando por consola: Con este valor de ángulos podrías, por ejemplo, rotar al personaje para que siempre mire al target con la función transform.Rotate. Con esto llegamos al final de esta entrada sobre vectores. Ha sido algo básico pero que sirve como punto de partida si quieres profundizar por ti mismo. Como he comentado estos cálculos son teniendo en cuenta solo 2 dimensiones. Te animo a que realices los cálculos añadiendo a las fórmulas una dimensión más. Podéis encontrar el código del proyecto aquí Recuerdo que no soy matemático y que los cálculos que aquí he realizado pueden contener errores o no ser del todo precisos. Cualquier duda o anotación siéntete libre de contactar conmigo. Nos vemos en la siguiente entrada.
  2. 4 points
    Cuando decidí dedicarme a crear videojuegos una de mis preocupaciones principales era mi escaso conocimiento matemático. Internet estaba lleno de mensajes asegurando que no es necesario saber matemáticas para crear videojuegos y aunque, pueda ser cierto en determinados casos, creo que aprender las nociones básicas te van a ayudar a saber lo que estás haciendo y sobre todo a construir mejores juegos. Como veréis no es necesario un conocimiento matemático avanzado para entender gran parte de los cálculos habituales. En esta serie de publicaciones voy a hablaros de algunos conceptos matemáticos básicos que se usan constantemente en videojuegos: Trigonometría Vectores Matrices Funciones Trigonometría La trigonometría, como su nombre indica (tri-tres; gono-ángulo; metría: medida), es la ciencia destinada al estudio de las medidas de los ángulos. Si por ejemplo quieres girar un personaje en tu juego necesitarás un ángulo de giro. Para poder medir un ángulo (o cualquier cosa en general) necesitamos una unidad de medida, vamos a ver las dos principales en el caso de los ángulos: Grado Cuando vas a comerte una pizza lo primero que haces (después de cocinarla claro) es partirla en diferentes trozos. Imagina que la cortamos en 8 trozos, siempre en 8 trozos porque nos encanta el tamaño de cada trozo. Si quisieras medir el ángulo de cada trozo podrías crear tu propia unidad de medida basada en tu especial devoción por los 8 trozos. Llamémosle pizzo a esta medida. Entonces si tu cortas tu pizza en 8 trozos, cada trozo tendría un ángulo de 1 pizzo. Podríamos pedir que se estableciera como medida oficial el pizzo pero me temo que ya tenemos una medida general que puedes usar en su lugar: el grado. Si en lugar de 8 trozos partimos la pizza en 4 trozos iguales y establecemos que cada trozo vale 90 grados, tendríamos la definición de grado. La definición es la siguiente: cada una de las partes que resultan en dividir en noventa partes un ángulo recto. Aquí podemos recordar los tipos de ángulos rápidamente. Radián Una vez hemos recordado la unidad familiar que es el grado, vamos a ver el radián que es el que usaremos de forma habitual. Pero antes de entrar en el radián, debemos conocer el número Pi. ¿Qué es Pi? El número Pi es uno de los más importante en las matemáticas. Su definición es la siguiente: Pi es la relación entre la longitud de una circunferencia y su diámetro. Pero ¿qué significa esto? Pues básicamente quiere decir que sí dibujamos una circunferencia de diámetro 1 y medimos su longitud, obtenemos Pi. Con esta imagen lo entenderás mucho mejor. Si pintamos el borde de una rueda de diámetro 1 y giramos mientras pintamos el suelo midiendo la longitud de la mancha de pintura, tenemos Pi, que es un valor constante (e infinito): 3.14159… De forma habitual se trabaja con radio = 1 en lugar de diámetro = 1. Por lo que la longitud de la circunferencia es este caso sería el doble de Pi (2 π). Como apunte este número es conocido como Tau y su valor es 6.28318… Pero, ¿qué relación tiene esto con un radián? Si seguimos con el ejemplo de la pizza anterior, imagina que te ha venido la inspiración y has cortado la pizza de tal forma que la longitud del arco de cada trozo que has partido mide exactamente 1. Vamos a verlo gráficamente: La pizza tiene radio = 1 y cortamos (no tendrías que cortarla realmente porque es minúscula pero bueno) un trozo cuya longitud de arco es exactamente 1 (arco azul en la imagen). Pues bien, el ángulo que se forma, nombrado “a” en la imagen, mediría exactamente 1 radián. ¿Que pasa si seguimos cortando trozos con longitud de arco 1? Pues tenemos que el ángulo que se forma con media circunferencia (“a” en la imagen) es π radianes. Y el de la la circunferencia completa sería 2π (Tau) radianes. Nota: los dibujos son solo explicativos, las distancias dibujadas no usan medidas reales. Seno, Coseno y Tangente Una vez conocemos el concepto de radián vamos a experimentar un poco con las circunferencias. ¿Qué pasaría si formamos un cuadrado cortando el eje x e y de la siguiente forma? ¿Como calculamos el valor de “x” y de “y”? ¿Que son x e y? Pues aunque resulte sorprendente esos valores son el seno y el coseno del ángulo “a”: La definición nos dice que el coseno de "a" es la abscisa de su punto asociado y el seno de a es la ordenada de dicho punto. Creo que es mucho más entendible con la imagen anterior. A medida que el ángulo cambia, sus valores en x e y modifican su valor. Aquí podemos verlo mejor: La tangente como ves es la recta que toca en un solo punto la circunferencia en función del ángulo. Teorema de Pitágoras En este punto vamos a hablar de otro término muy importante en matemáticas y que usaremos en los siguientes publicaciones, el Teorema de Pitágoras. Este teorema dice que dado un triángulo rectángulo, podemos relacionar su hipotenusa(h) con sus lados (x e y) de la siguiente forma: Despejando tendríamos: ¿Pero qué uso tiene esto? Pues en el siguiente episodio hablaremos de su uso con Vectores. Imagina que tienes un personaje en un escenario y quieres saber a cuánta distancia está un determinado enemigo. Deberás plantearlo con el teorema de pitágoras, donde la “h” es la distancia entre tu personaje y el enemigo. Tendrías que calcular la “x” e “y” para posteriormente poder despejar “h”. Pero vamos a verlo con un ejercicio simplificado: Imagina que tenemos el siguiente triángulo. Para calcular h haríamos lo siguiente: ¿Y si quiero conocer el ángulo? Para conocer el ángulo necesitamos profundizar nuestro conocimiento en las razones trigonométricas. Vamos a verlo siguiendo el mismo ejemplo: Para calcular θ vamos a necesitar calcular primero el seno del ángulo. Pero ¿cómo hacemos esto sin conocer el propio ángulo? Anteriormente vimos que: Esto es así siempre y cuando el radio (la hipotenusa) fuera 1. Pero ¿qué pasa si como en este caso la hipotenusa es 7.81? Solo tendremos que dividir la variable entre la hipotenusa de la siguiente forma: Vale ya sabemos calcular el seno sin tener el ángulo: Una vez tenemos el seno necesitamos conocer el concepto de función trigonométrica inversa. No os asustéis porque el concepto es muy simple. Necesitamos algo que multiplicado por el seno (que ya lo conocemos) nos de el ángulo. Veremos más a fondo este término cuando veamos las matrices. En este caso las función trigonométricas inversas las conocemos y son las siguientes: Nota: en español también se les llama cosecante (arco seno), secante (arco coseno) y cotangente (arcotangente) Ya conocemos cómo obtener el ángulo: Ya tenemos el valor en radianes del ángulo. Si necesitamos pasar de radianes a grados, podemos usar las siguientes fórmulas: Aquí podemos ver gráficamente las razones trigonométricas que hemos visto aquí: Espero que os sea de ayuda y perdonad si he cometido algún error o he explicado algo de forma muy torpe. No soy ni mucho menos un experto matemático, mi intención es que todos podáis acercaros y verlas un poco más fácil. Cualquier duda estaré encantado de ayudar. En la próxima parte veremos Vectores y pondremos en práctica todo lo aprendido aquí pero dentro de Unity. Hasta la próxima.
UnitySpain © Todos los derechos reservados 2020
×
×
  • Create New...