Capítulo 4. Matemáticas
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
Las matemáticas son una parte inevitable del uso de una herramienta de desarrollo espacial como Unity. Te guste o la detestes, tienes que utilizarla, reconocerla e incluso abrazarla para poder hacer algo. Afortunadamente, puedes pasar por alto o fingir que muchas de ellas no están ahí, pero siempre están ahí, apoyándote y acechándote.
Este capítulo explora algunos de los problemas más comunes relacionados con las matemáticas que encontrarás al desarrollar utilizando Unity.
Nota
Estas no son todas las matemáticas que necesitarás para desarrollar un juego o Unity, ¡ni mucho menos! Pero son suficientes para ayudarte y ponerte en marcha. Hemos hecho todo lo posible para estructurarlo en torno a los problemas que podrías querer resolver utilizando las matemáticas, más que en torno a las matemáticas en sí.
¿Te has preguntado alguna vez qué es un vector? ¿O cómo se utiliza una matriz? ¿Y un cuaternión? Aquí los trataremos, al tiempo que abordaremos problemas matemáticos comunes en el desarrollo de Unity, como calcular los ángulos y las distancias a otra cosa en tu proyecto basado en Unity.
Consejo
Nada en este capítulo es único o específico del desarrollo con Unity. Cubrimos cosas desde una perspectiva centrada en Unity cuando hablamos de código, pero todo lo que hay aquí son simples matemáticas para el desarrollo espacial o de juegos.
Sigue leyendo para saber lo justo de matemáticas para el desarrollo de Unity.
Consejo
No podemos dejar de recomendar la serie de vídeos Just Enough Math de Paco Nathan, también de O'Reilly. Échale un vistazo si quieres aprender aún más matemáticas.
4.1 Almacenamiento de coordenadas dedimensiones variables mediante vectores
Problema
El desarrollo de Unity implica muchas coordenadas de distintas dimensiones:
-
Coordenadas unidimensionales, como 1 o -9
-
Coordenadas bidimensionales, como 8, 3 o -1, 4
-
Coordenadas tridimensionales, como 3, 1, -3 o -7, 6, 1
Siendo realistas, cuando construyes algo en Unity, mientras lleves la cuenta de lo que tienes que llevar la cuenta, a menudo no importa cómo lo hagas. Sin embargo, una forma común y útil de llevar la cuenta de las coordenadas son los vectores.
Los vectores son un conjunto de coordenadas de dimensiones variables. Así pues, nuestro problema aquí es: ¿cómo utilizo los vectores y Unity, y para qué puedo utilizarlos?
Solución
Para ofrecer aquí una solución, vamos a tener que desentrañar un poco el problema. Lo admitimos, escribimos un enunciado del problema deliberadamente amplio, pero eso es para poder mostrarte todo lo que necesitas saber sobre los vectores sin tener que tener una receta diminuta para cada manipulación o acción que quieras hacer con un vector.
Consejo
A menudo verás significados bastante específicos aplicados a los vectores: en los videojuegos suelen ser coordenadas en geometría, pero no hay absolutamente ninguna razón para que no puedas almacenar en ellos lo que quieras. Son sólo una estructura de datos.
En primer lugar, en Unity puedes definir un Vector2
: uno con dos dimensiones, normalmente una x y una y. Un Vector2
se utiliza normalmente para representar un punto en un espacio 2D en Unity.
Puedes definir un Vector2
con dos dimensiones:
Vector2
direction
=
new
Vector2
(
0.0f
,
2.0f
);
O utiliza uno de los vectores incorporados de Unity:
var
up
=
Vector2
.
up
;
// ( 0, 1)
var
down
=
Vector2
.
down
;
// ( 0, -1)
var
left
=
Vector2
.
left
;
// (-1, 0)
var
right
=
Vector2
.
right
;
// ( 1, 0)
var
one
=
Vector2
.
one
;
// ( 1, 1)
var
zero
=
Vector2
.
zero
;
// ( 0, 0)
Unity también tiene una clase llamada Vector3
, que es un vector con tres dimensiones. Hay varios vectores predefinidos disponibles en la clase Vector3
:
Vector3
point
=
new
Vector3
(
1.0f
,
2f
,
3.5f
);
var
up
=
Vector3
.
up
;
// ( 0, 1, 0)
var
down
=
Vector3
.
down
;
// ( 0, -1, 0)
var
left
=
Vector3
.
left
;
// (-1, 0, 0)
var
right
=
Vector3
.
right
;
// ( 1, 0, 0)
var
forward
=
Vector3
.
forward
;
// ( 0, 0, 1)
var
back
=
Vector3
.
back
;
// ( 0, 0, -1)
var
one
=
Vector3
.
one
;
// ( 1, 1, 1)
var
zero
=
Vector3
.
zero
;
// ( 0, 0, 0)
Consejo
Puedes obtener más información sobre Vector2
y
Vector3
en la documentación de la API de Unity.
Cada componente de Transform
en Unity tiene definidos vectores de dirección locales, que son relativos a su rotación actual. Por ejemplo, se puede acceder a la dirección local hacia delante de un objeto como:
var
myForward
=
transform
.
forward
;
Naturalmente, puedes realizar operaciones aritméticas básicas con vectores. Los vectores se pueden sumar:
var
v1
=
new
Vector3
(
1f
,
2f
,
3f
);
var
v2
=
new
Vector3
(
0f
,
1f
,
6f
);
var
v3
=
v1
+
v2
;
// (1, 3, 9)
var
v4
=
v2
-
v1
;
// (-1, -1, 3)
También puedes obtener la magnitud de un vector. También conocida como longitud del vector, la magnitud del vector es la distancia en línea recta desde el origen (0, 0, 0) hasta el vector. La magnitud de un vector es la raíz cuadrada de las sumas de los cuadrados de las componentes. Por ejemplo, la magnitud del vector (0, 2, 0) es 2; la magnitud del vector (0, 1, 1) es aproximadamente 1,41 (es decir, la raíz cuadrada de 2):
var
forwardMagnitude
=
Vector3
.
forward
.
magnitude
;
// = 1
var
vectorMagnitude
=
new
Vector3
(
2f
,
5f
,
3f
).
magnitude
;
// ~= 6.16
La magnitud puede utilizarse luego para hacer otros cálculos. Por ejemplo, para calcular la distancia entre dos vectores, puedes restar un vector de otro y calcular la magnitud del resultado:
var
point1
=
new
Vector3
(
5f
,
1f
,
0f
);
var
point2
=
new
Vector3
(
7f
,
0f
,
2f
);
var
distance
=
(
point2
-
point1
).
magnitude
;
// = 3
El método incorporado Distance
realiza el mismo cálculo por ti:
Vector3
.
Distance
(
point1
,
point2
);
Para calcular la magnitud de un vector se necesita la raíz cuadrada. Sin embargo, hay casos en los que no necesitas el valor real de la magnitud de un vector, y sólo quieres comparar dos longitudes. En estos casos, puedes omitir la raíz cuadrada y trabajar con el cuadrado de la magnitud. Hacer esto es un poco más rápido, y nos preocupamos bastante por los cálculos rápidos... especialmente en el desarrollo de juegos.
Para obtener este valor, utiliza la propiedad sqrMagnitude
:
var
distanceSquared
=
(
point2
-
point1
).
sqrMagnitude
;
// = 9
Muchas operaciones funcionan mejor con vectores que tienen una magnitud de 1. Un vector con una magnitud de 1 también se llama vector unitario, porque su magnitud es una sola unidad (es decir, uno). Puedes tomar un vector y producir uno nuevo que tenga la misma dirección pero con una magnitud de 1 dividiéndolo por su propia magnitud. Esto se llama normalizar un vector:
var
bigVector
=
new
Vector3
(
4
,
7
,
9
);
// magnitude = 12.08
var
unitVector
=
bigVector
/
bigVector
.
magnitude
;
// magnitude = 1
Se trata de una operación habitual en, por lo que puedes acceder directamente a una versión normalizada de un vector utilizando la propiedad normalizado:
var
unitVector2
=
bigVector
.
normalized
;
Los vectores también se pueden escalar. Cuando multiplicas un vector por un único número (un escalar), el resultado es un vector en el que cada componente del origen se multiplica por ese número:
var
v1
=
Vector3
.
one
*
4
;
// = (4, 4, 4)
También puedes realizar un escalado por componentes utilizando el método Scale
. Este método toma dos vectores y produce un tercer vector en el que cada componente del primero se multiplica por el componente correspondiente del segundo, es decir, dados dos vectores A
y B
, el resultado de A.Scale(B)
es (A.x * B.x, A.y * B.y, A.z * B.z)
:
v1
.
Scale
(
new
Vector3
(
3f
,
1f
,
0f
));
// = (12f, 4f, 0f)
También puedes obtener el producto punto de dos vectores, que te indica la diferencia entre las direcciones a las que apuntan.
El producto punto se define como la suma de los productos de los dos vectores. Es decir, dados dos vectores tridimensionales A
y B
, A•B
= sum(A.x * B.x, A.y * B.y, A.z * B.z)
.
Puedes utilizar el producto punto para determinar la semejanza de dos vectores. El producto punto entre dos vectores que apuntan en la misma dirección es 1:
var
parallel
=
Vector3
.
Dot
(
Vector3
.
left
,
Vector3
.
left
);
// 1
El producto punto entre dos vectores que apuntan en direcciones opuestas es -1:
var
opposite
=
Vector3
.
Dot
(
Vector3
.
left
,
Vector3
.
right
);
// -1
Y el producto punto entre dos vectores perpendiculares entre sí es 0:
var
orthogonal
=
Vector3
.
Dot
(
Vector3
.
up
,
Vector3
.
forward
);
// 0
Como feliz efecto secundario, el producto punto entre dos vectores es también el coseno del ángulo entre los dos vectores. Esto significa que, dado el producto punto entre dos vectores, puedes calcular el ángulo entre los vectores tomando su arco coseno:
var
orthoAngle
=
Mathf
.
Acos
(
orthogonal
);
var
orthoAngleDegrees
=
orthoAngle
*
Mathf
.
Rad2Deg
;
// = 90
Nota
El método Mathf.Acos
devuelve un valor medido en radianes. Para convertirlo a grados, puedes multiplicarlo por la constante Mathf.Rad2Deg
.
El producto punto es una buena forma de saber si un objeto está delante o detrás de otro.
Para saber si un objeto está delante de otro, primero tenemos que decidir qué significa "delante de". En Unity, el eje z local representa la dirección hacia delante, y puedes acceder a él a través de la propiedad forward
de un objeto Transform
.
Podemos producir un vector que represente la dirección desde el primer objeto hasta el segundo restando la posición del segundo de la posición del primero. Luego podemos tomar el producto punto de ese vector contra la dirección de avance del primer objeto.
Ahora podemos utilizar lo que sabemos sobre el producto punto para averiguar si el segundo objeto está delante del primero. Recuerda que el producto punto de dos vectores que apuntan en la misma dirección es 1. Si el segundo objeto está justo delante del primero, entonces la dirección hacia ese objeto será idéntica, lo que significa que el producto punto de los dos vectores será 1. Si es 0, entonces el objeto está en ángulo recto con la dirección hacia delante. Si es 0, entonces el objeto está en ángulo recto respecto a la dirección de avance. Si es -1, entonces está directamente detrás del objeto, porque está exactamente en la dirección opuesta a la de avance:
var
directionToOtherObject
=
someOtherObjectPosition
-
transform
.
position
;
var
differenceFromMyForwardDirection
=
Vector3
.
Dot
(
transform
.
forward
,
directionToOtherObject
);
if
(
differenceFromMyForwardDirection
>
0
)
{
// The object is in front of us
}
else
if
(
differenceFromMyForwardDirection
<
0
)
{
// The object is behind us
}
else
{
// The object neither before or behind us; it's at a perfect right
// angle to our forward direction.
}
También está disponible el producto cruzado, un tercer vector ortogonal (en ángulo recto) a dos vectores de entrada:
var
up
=
Vector3
.
Cross
(
Vector3
.
forward
,
Vector3
.
right
);
Consejo
El producto cruzado sólo está definido para vectores tridimensionales.
También puedes obtener un nuevo vector a partir de dos vectores, pasando de uno a otro a una determinada magnitud. Esto es especialmente útil para evitar el rebasamiento. Aquí nos movemos de (0, 0, 0) a (1, 1, 1), sin desplazarnos más allá de 0,5 unidades:
var
moved
=
Vector3
.
MoveTowards
(
Vector3
.
zero
,
Vector3
.
one
,
0.5f
);
// = (0.3, 0.3, 0.3) (a vector that has a magnitude of 0.5)
O reflejarse en un plano, definido por una normal:
var
v
=
Vector3
.
Reflect
(
new
Vector3
(
0.5f
,
-
1f
,
0f
),
Vector3
.
up
);
// = (0.5, 1, 0)
También puedes interpolar linealmente, o lerp, entre dos vectores de entrada, dado un número entre 0 y 1. Si proporcionas 0, obtendrás el primer vector; si proporcionas 1, obtendrás el segundo; y si proporcionas 0,5, obtendrás algún punto justo en medio de los dos:
var
lerped
=
Vector3
.
Lerp
(
Vector3
.
zero
,
Vector3
.
one
,
0.65f
);
// = (0.65, 0.65, 0.65)
Si especificas un número fuera del intervalo de 0 a 1, lerp
lo fijará entre 0 y 1. Puedes evitarlo utilizando LerpUnclamped
:
var
unclamped
=
Vector3
.
LerpUnclamped
(
Vector3
.
zero
,
Vector3
.
right
,
2.0f
);
// = (2, 0, 0)
Debate
Esto es sólo una muestra del uso de vectores en Unity. Las operaciones matemáticas que Unity te proporciona para realizar sobre un vector pueden simplificar muchas cosas. Puedes utilizar el producto punto, por ejemplo, para saber si un punto está delante o detrás de un personaje jugador, o crear un radar para averiguar dónde están los enemigos.
Los vectores también hacen que las operaciones complejas, como escalar o girar algo, sean muy sencillas. En lugar de tener que calcular manualmente cada objeto y su relación con los demás, puedes utilizar simplemente las matemáticas vectoriales.
Básicamente, los vectores te permiten abordar cuestiones relacionadas con la geometría con un código mucho más limpio del que necesitarías de otro modo. ¡Son herramientas matemáticas maravillosas en tu kit de herramientas de desarrollo de juegos!
4.2 Girar en el espacio 3D
Solución
Para rotar en el espacio 3D, necesitarás trabajar con cuaterniones, que son estructuras matemáticas muy útiles para representar rotaciones en el espacio 3D. Un cuaternión puede representar una rotación alrededor de cualquier eje por cualquier ángulo.
Consejo
Los cuaterniones pueden ser una bestia complicada, ya que -en términos puramente matemáticos- son números de cuatro dimensiones. Sin embargo, a efectos de desarrollo de juegos, lo único que son es una rotación, y no importa si no entiendes exactamente por qué funciona un cuaternión.
Por ejemplo, puedes utilizar un cuaternión para definir una rotación que gire alrededor de 90 grados en el eje x:
var
rotation
=
Quaternion
.
Euler
(
90
,
0
,
0
);
Y luego úsalo para girar un punto alrededor del origen:
var
input
=
new
Vector3
(
0
,
0
,
1
);
var
result
=
rotation
*
input
;
// = (0, -1, 0)
Existe un cuaternión identidad, que no representa ninguna rotación:
var
identity
=
Quaternion
.
identity
;
Puedes interpolar -esdecir, mezclar- entre dos rotaciones utilizando el método Slerp
, que se mueve suavemente entre rotaciones de forma que el cambio de ángulo sea constante en cada paso. Esto es mejor que una interpolación lineal de ángulos, en la que los ángulos cambian a un ritmo no constante:
var
rotationX
=
Quaternion
.
Euler
(
90
,
0
,
0
);
var
halfwayRotated
=
Quaternion
.
Slerp
(
identity
,
rotationX
,
0.5f
);
Consejo
Slerp
es la abreviatura de interpolación lineal esférica.
También puedes combinar cuaterniones. Por ejemplo, para girar algo alrededor del eje y, y luego alrededor del eje x, los multiplicas (se aplican en orden inverso):
var
combinedRotation
=
Quaternion
.
Euler
(
90
,
0
,
0
)
*
// rotate around x
Quaternion
.
Euler
(
0
,
90
,
0
);
// rotate around y
Nota
Esta combinación no es conmutativa: ¡el orden de la multiplicación importa! Girar por x y luego por y no es lo mismo que girar por y y luego por x.
Debate
Otro método de representar rotaciones en el espacio 3D es con ángulos de Euler,es decir, rotaciones alrededor de los ejes x, y y z, almacenados por separado. Los ángulos de Euler son fáciles de entender, y es habitual utilizarlos al expresar rotaciones en código.
Sin embargo, este enfoque es propenso a un problema llamado bloqueo de cardán, que se produce cuando un objeto se gira de forma que dos de sus ejes de rotación son paralelos. Cuando esto ocurre, el objeto pierde un grado de libertad. Este problema no existe en los cuaterniones, que siempre se pueden girar en cualquier dirección desde cualquier otra orientación.
Un método alternativo para evitar el bloqueo del cardán es utilizar una matriz que represente una rotación (ver Receta 4.3). Sin embargo, una matriz de rotación de 4×4 son 16 números, mientras que un cuaternión son sólo 4, lo que significa que los cuaterniones ocupan menos espacio que las matrices para el mismo resultado.
4.3 Realizar transformacionesen el espacio 3D con matrices
Solución
Puedes utilizar una matriz para representar una transformación completa. Una matriz no es más que una cuadrícula de números(Ecuación 4-1):
var
matrix
=
new
Matrix4x4
();
Ecuación 4-1. Una matriz de 4×4
Puedes establecer y obtener valores en cada lugar de la cuadrícula:
var
m00
=
matrix
[
0
,
0
];
matrix
[
0
,
1
]
=
2f
;
Puedes multiplicar una matriz por un vector para producir un nuevo vector. Dependiendo de los valores dentro de la matriz, esto tiene como resultado mover, escalar y rotar el vector original. También puedes realizar operaciones más complejas, como el cizallamiento o la aplicación de proyecciones en perspectiva.
Puedes multiplicar dos matrices entre sí para obtener una tercera matriz. Cuando multiplicas esta nueva matriz con un vector, el resultado es el mismo que si hubieras multiplicado por separado cada una de las matrices originales con el vector en secuencia.
Consejo
Los gráficos por ordenador, y por lo tanto el desarrollo de juegos, suelen utilizar matrices de 4×4 porque pueden utilizarse para realizar una amplia gama de transformaciones comunes.
Ahora crearemos una matriz que desplace (traslade) un vector 5 unidades, en el eje x. En primer lugar, definiremos una nueva matriz, utilizando cuatro Vector4
s (vectores de cuatro dimensiones):
var
translationMatrix
=
new
Matrix4x4
(
new
Vector4
(
1
,
0
,
0
,
5
),
new
Vector4
(
0
,
1
,
0
,
0
),
new
Vector4
(
0
,
0
,
1
,
0
),
new
Vector4
(
1
,
0
,
0
,
1
)
);
Nota
Cada una de las Vector4
s que utilizamos para crear una matriz representa una columna, no una fila.
La matriz que crea este código se muestra en la Ecuación 4-1.
Nota
Cuando multiplicamos un vector tridimensional por una matriz, añadimos 1 al final del vector, formando un vector tetradimensional. Lacomponente adicional se denomina comúnmentecomponentew
.
Multiplicando esta matriz por un vector de cuatro dimensiones, V
, se obtiene el resultado siguiente:
1*Vx + 0*Vy + 0*Vz + 5*Vw = resultX 0*Vx + 1*Vy + 0*Vz + 0*Vw = resultY 0*Vx + 0*Vy + 1*Vz + 0*Vw = resultZ 0*Vx + 0*Vy + 0*Vz + 1*Vw = resultW
Por ejemplo, para multiplicar el punto (0, 1, 2) (a Vector3
) con esta matriz:
-
Primero añadimos nuestro componente
w
:Vx = 0, Vy = 1, Vz = 2, Vw = 1 1*0 + 0*1 + 0*2 + 5*1 = 5 0*0 + 1*1 + 0*2 + 0*1 = 1 0*0 + 0*1 + 1*2 + 0*1 = 2 0*0 + 0*1 + 0*2 + 1*1 = 1
-
Luego descartamos la cuarta componente, quedando nuestro resultado. Nuestro resultado final es, por tanto, el vector (5, 1, 2).
Sin embargo, en lugar de hacernos hacer todo este trabajo nosotros mismos, Unity proporciona un método MultiplyPoint
como parte del tipo Matrix4x4
:
var
input
=
new
Vector3
(
0
,
1
,
2
);
var
result
=
translationMatrix
.
MultiplyPoint
(
input
);
// = (5, 1, 2)
Nota
Quizá te preguntes por qué la matriz tiene la cuarta fila, ya que sólo significa que tenemos que añadir y eliminar un cuarto componente inútil a nuestros vectores. Está ahí para facilitar operaciones como las proyecciones en perspectiva. Si sólo realizas transformaciones como traslaciones, rotaciones y escalas, puedes prescindir de utilizar sólo una parte de la matriz, y utilizar en su lugar la función MultiplyPoint4x3
de Matrix4x4
. Es un poco más rápida, pero sólo se puede utilizar para traslaciones, rotaciones y escalados, y no para ninguna de las otras tareas.
Unity también proporciona métodos de ayuda para trasladar puntos utilizando una matriz:
var
input
=
new
Vector3
(
0
,
1
,
2
);
var
translationMatrix
=
Matrix4x4
.
Translate
(
new
Vector3
(
5
,
1
,
-
2
));
var
result
=
translationMatrix
.
MultiplyPoint
(
input
);
// = (5, 2, 0)
También puedes girar un punto alrededor del origen utilizando matrices y cuaterniones:
var
rotate90DegreesAroundX
=
Quaternion
.
Euler
(
90
,
0
,
0
);
var
rotationMatrix
=
Matrix4x4
.
Rotate
(
rotate90DegreesAroundX
);
var
input
=
new
Vector3
(
0
,
0
,
1
);
var
result
=
rotationMatrix
.
MultiplyPoint
(
input
);
En este caso, el punto se ha desplazado desde delante del origen hasta debajo de él, dando como resultado el punto (0, -1, 0).
Si tu vector representa una dirección, y quieres utilizar una matriz para rotar el vector, puedes utilizar MultiplyVector
. Este método sólo utiliza las partes de las matrices que son necesarias para hacer una rotación, por lo que es un poco más rápido:
result
=
rotationMatrix
.
MultiplyVector
(
input
);
// = (0, -1, 0) - the same result.
También puedes utilizar una matriz escala un punto alejado del origen:
var
scale2x2x2
=
Matrix4x4
.
Scale
(
new
Vector3
(
2f
,
2f
,
2f
));
var
input
=
new
Vector3
(
1f
,
2f
,
3f
);
var
result
=
scale2x2x2
.
MultiplyPoint3x4
(
input
);
// = (2, 4, 6)
Multiplicar matrices entre sí da lugar a una nueva matriz que, cuando se multiplica por un vector, produce el mismo resultado que si hubieras multiplicado el vector por cada una de las matrices originales en orden. En otras palabras, si consideras una matriz como una instrucción para modificar un punto, puedes combinar varias matrices en un solo paso.
Consejo
Cuando combinamos matrices de este modo, lo llamamos concatenar las matrices.
En este ejemplo, concatenamos matrices:
var
translation
=
Matrix4x4
.
Translate
(
new
Vector3
(
5
,
0
,
0
));
var
rotation
=
Matrix4x4
.
Rotate
(
Quaternion
.
Euler
(
90
,
0
,
0
));
var
scale
=
Matrix4x4
.
Scale
(
new
Vector3
(
1
,
5
,
1
));
var
combined
=
translation
*
rotation
*
scale
;
var
input
=
new
Vector3
(
1
,
1
,
1
);
var
result
=
combined
.
MultiplyPoint
(
input
);
Debug
.
Log
(
result
);
// = (6, 1, 5)
Al igual que con los cuaterniones, ¡el orden de multiplicación importa! La multiplicación de matrices no es conmutativa, mientras que la multiplicación de números regulares sí lo es. Por ejemplo, 2 * 5
es el mismo cálculo que 5 * 2
: ambos cálculos dan como resultado el número 10.
Sin embargo, mover un objeto y luego rotarlo no produce el mismo resultado que rotar un objeto y luego moverlo. Del mismo modo, combinar una matriz que traslada un punto con otra que lo gira no tendrá el mismo resultado si las combinas en el orden inverso.
La combinación de matrices con la multiplicación las aplicará en orden inverso a la multiplicación. Dado un punto P
, y las matrices A
, B
, y C
:
P * (A * B * C) == (A * (B * (C * P)))
puedes crear una matriz combinada de traslación-rotación-escala utilizando el método Matrix4x4.TRS
:
var
transformMatrix
=
Matrix4x4
.
TRS
(
new
Vector3
(
5
,
0
,
0
),
Quaternion
.
Euler
(
90
,
0
,
0
),
new
Vector3
(
1
,
5
,
1
)
);
Esta nueva matriz escalará, rotará y luego trasladará cualquier punto al que la apliques.
También puedes obtener una matriz que convierta la posición de un punto del componente en el espacio local al espacio mundial, lo que significa tomando la posición local y aplicando la traslación, rotación y escala locales de este objeto, así como las de todos sus padres:
var
localToWorld
=
this
.
transform
.
localToWorldMatrix
;
También puedes obtener la matriz que hace lo contrario, es decir, que convierte del espacio mundial al espacio local:
var
worldToLocal
=
this
.
transform
.
worldToLocalMatrix
;
Uf. ¡Cuántas cosas se pueden hacer con las matrices!
4.4 Trabajar con ángulos
Solución
En Unity, la mayoría de las rotaciones que se representan como ángulos de Euler se dan como grados.
Podemos rotar las cosas utilizando grados, mediante el método Rotate
de la clase Transform
:
// Rotate 90 degrees - one quarter circle - around the X axis
transform
.
Rotate
(
90
,
0
,
0
);
Consejo
Hay 360 grados en un círculo; hay 2π radianes en un círculo. Sólo son unidades de medida diferentes para los ángulos.
Los grados son mucho más familiares para la mayoría de la gente, pero los radianes suelen ser más fáciles de calcular. Por eso, algunas partes de la Unidad, sobre todo las relacionadas con las matemáticas, esperan radianes. Hay 2π radianes en un círculo:
// The sine of pi radians (one half-circle) is zero
Mathf
.
Sin
(
Mathf
.
PI
);
// = 0
Puedes convertir de radianes a grados, y a la inversa , así:
// Converting 90 degrees to radians
var
radians
=
90
*
Mathf
.
Deg2Rad
;
// ~= 1.57 (π / 2)
// Converting 2π radians to degrees
var
degrees
=
2
*
Mathf
.
PI
*
Mathf
.
Rad2Deg
;
// = 360
Debate
El producto punto de dos vectores unitarios es igual al coseno del ángulo entre ellos. Si tienes el coseno de un grado, puedes obtener el grado original tomando el arco coseno del mismo. Esto significa que puedes hallar el ángulo entre dos vectores de la siguiente manera:
var
angle
=
Mathf
.
Acos
(
Vector3
.
Dot
(
Vector3
.
up
,
Vector3
.
left
));
El resultado de esto es π radianes; si quieres mostrárselo al usuario, primero debes convertirlo a grados. Hay 2π radianes en un círculo, mientras que hay 360 grados en un círculo; como resultado, para convertir un número de radianes a grados, lo multiplicas por 180/π
. Por ejemplo, π/2 radianes en grados = (π/2) * (180/π) = 90
. La conversión de grados a radianes funciona a la inversa: lo multiplicas por π/180
. Por ejemplo, 45 grados en radianes es 45 * (π/180) = π/4
.
Puedes simplificar esto en tu código utilizando las constantes Mathf.Deg2Rad
y Mathf.Rad2Deg
. Si multiplicas un ángulo expresado en radianes por Mathf.Rad2Deg
, obtendrás el resultado en grados; si multiplicas un ángulo expresado en grados por Mathf.Deg2Rad
, obtendrás el resultado en radianes.
4.5 Encontrar la distancia a un objetivo
Solución
Tendrás que crear y añadir un script al objeto que necesita saber cuándo el otro objeto está a su alcance:
-
Crea un nuevo script en C# llamado RangeChecker.cs, y añádele el siguiente código :
public
class
RangeChecker
:
MonoBehaviour
{
// The object we want to check the distance to
[SerializeField]
Transform
target
;
// If the target is within this many units of us, it's in range
[SerializeField]
float
range
=
5
;
// Remembers if the target was in range on the previous frame.
private
bool
targetWasInRange
=
false
;
void
Update
()
{
// Calculate the distance between the objects
var
distance
=
(
target
.
position
-
transform
.
position
).
magnitude
;
if
(
distance
<=
range
&&
targetWasInRange
==
false
)
{
// If the object is now in range, and wasn't before, log it
Debug
.
LogFormat
(
"Target {0} entered range!"
,
target
.
name
);
// Remember that it's in range for next frame
targetWasInRange
=
true
;
}
else
if
(
distance
>
range
&&
targetWasInRange
==
true
)
{
// If the object is not in range, but was before, log it
Debug
.
LogFormat
(
"Target {0} exited range!"
,
target
.
name
);
// Remember that it's no longer in range for next frame
targetWasInRange
=
false
;
}
}
}
-
Adjunta este script a cualquier objeto, y adjunta cualquier otro objeto al campo Objetivo del script, y el script detectará cuándo el objetivo entra y sale del rango especificado.
Debate
Si combinas esta receta con la Receta 4.6, puedes montar con bastante facilidad un comportamiento en el que un objeto sólo pueda "ver" los objetos cercanos que estén delante de él. Puedes ver una versión más sofisticada de este script en la Receta 10.1.
4.6 Encontrar el ángulo respecto a un objetivo
Solución
Tendrás que crear y añadir un script al objeto que necesita conocer el ángulo entre él y otro objeto:
-
Crea un nuevo script en C# llamado AngleChecker.cs, y añádele el siguiente código:
public
class
AngleChecker
:
MonoBehaviour
{
// The object we want to find the angle to
[SerializeField]
Transform
target
;
void
Update
()
{
// Get the normalized direction to the target
var
directionToTarget
=
(
target
.
position
-
transform
.
position
).
normalized
;
// Take the dot product between that direction and our forward
// direction
var
dotProduct
=
Vector3
.
Dot
(
transform
.
forward
,
directionToTarget
);
// Get the angle
var
angle
=
Mathf
.
Acos
(
dotProduct
);
// Log the angle, limiting it to 1 decimal place
Debug
.
LogFormat
(
"The angle between my forward direction and {0} is {1:F1}°"
,
target
.
name
,
angle
*
Mathf
.
Rad2Deg
);
}
}
-
Adjunta este script a cualquier objeto, y adjunta cualquier otro objeto al campo Objetivo del script, y el script registrará el ángulo, en grados, entre la dirección de avance del objeto y el objeto objetivo.
Debate
El concepto de "ángulo entre dos objetos" depende de que elijas al menos una dirección. No puedes obtener el ángulo entre dos puntos del espacio, porque hay un número infinito de ángulos posibles entre ellos. En lugar de eso, tienes que elegir una dirección relativa al primer objeto, y compararla con la dirección hacia el segundo.
Get Libro de recetas de desarrollo de Unity, 2ª edición now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.