En algún punto de la creación de un videojuego es muy probable que se necesite emplear vectores. Se usan en físicas, en IA, trigonometría y muchas otras situaciones, pero ¿qué es un vector? En términos simples, un vector es una cantidad con dirección. Comencemos observando un vector unidimensional, dibujando una flecha que comienza en 0 y termina en 5. Este es el vector a de magnitud 5. Si dibujamos otra flecha comenzando en 5 y terminando en 8 tenemos el vector b de magnitud 3.
Precisemos que lo que importa no es dónde comienza un vector, sino cuál es su longitud (magnitud) y su dirección. Entonces, el vector b comienza en 5, tiene 3 unidades de longitud y apunta a la "derecha", lo que lo hace equivalente a un vector que comenzara en 0 y llegara hasta 3. Ahora, es posible sumar ambos vectores al juntar a y b (punta con cola) para obtener el vector c de magnitud 8. ¿Y para números negativos? Podemos deducir de la imagen anterior, que si un vector que apunta hacia la derecha corresponde a un número positivo, un vector apuntando hacia la izquierda corresponderá a un número negativo, lo que nos indica que un vector unidimensional no es más que un número con signo (+/-). Esto explica el concepto básico de un vector: la longitud y la dirección cuentan, no así la posición.
¿Y qué pasa con los vectores bidimensionales? Se les puede pensar no sólo consistiendo de una dirección "izquierda" y "derecha, sino también "arriba" y "abajo":
En la segunda figura, esas flechas todavía no se pueden considerar vectores ya que necesitamos simplificarlos usando sus coordenadas inicial y final. Para el vector a tenemos que su coordenada inicial es [2,2] y la final es [4,3], y para obtener el vector debemos restar las coordenadas iniciales de las coordenadas finales de la forma [(x2-x1), (y2-y1)] = [(4-2), (3-2)] = [2,1]. Ahora para el vector b : [(-1.2 -(-3.2)) ,(2.1 - 1.1)] = [2 ,1]. ¿Notamos algo? Los dos vectores son iguales. Esta es una demostración más de que un vector no tiene posición, sólo dirección y magnitud. Podemos incluso dibujar los dos vectores relativos el uno a otro trasladando su inicio a la posición local [0,0]:
Todo esto significa que un vector 2D está definido por dos valores: una posición x y una posición y relativas al cruce de ejes [0,0]. ¿Y para los vectores 3D? Los vectores 3D tienen una dimension adicional, la "profundidad" por lo que se describen en términos de tres posiciones x, y y z alrededor de un origen, así:
Regularmente los vectores pueden usarse en distintas situaciones, pero hay ocasiones en que se desea limitar su valor(por ejemplo, cuando se trabaja con ángulos) para lo cual se les normaliza. Esto consiste en convertir un vector de longitud n en un vector de longitud 1, lo que da por resultado que las componentes del vector se normalicen entre 0 y 1. A estos vectores también se les llama vectores unitarios:
para calcular un vector normalizado, primero debemos tener las componentes originales del vector, y luego usar éstas para obtener la longitud del vector. Luego dividimos cada una de las componentes por la longitud para así obtener las componentes normalizadas del vector con las cuales formamos el vector normalizado en el que LA SUMA DE LOS CUADRADOS DE TODAS LAS COORDENADAS ES IGUAL A 1. Aquí el procedimiento:
En primer lugar usamos las coordenadas del vector para obtener las componentes:
vx = (x2 - x1) = (7 - 1) = 6
vy = (y2 - y1) = (4 - 1) = 3
Segundo, usamos las componentes para obtener la longitud del vector:
len = sqr(vx2 + vy2) = sqr(36 + 9) = sqr(45) = 6.708203932499369
Esto nos da la magnitud exacta del vector a, la cual usamos para normalizar sus dos componentes vx y vy:
vx = (vx/len) = (6 / 6.708203932499369) = 0.8944271909999159
vy = (vy/len) = (3 / 6.708203932499369) = 0.4472135954999579
a = 1
¡Bien! Hemos normalizado las componentes del vector! Pero... qué uso práctico tiene esto en el contexto de GameMaker:Studio? Bueno, aquí un ejemplo...
Supongamos que tienes un juego en el que el jugador tiene que disparar a un enemigo y es necesario saber cuánta distancia tiene que recorrer el objeto bala en los ejes x e y para contactarlo:
Para esto, se usarían las coordenadas del jugador y el enemigo para obtener tanto las componentes como la longitud del vector, entonces se procedería a normalizar las componentes, lo que las dejaría expresadas en un valor entre 0 y 1 el cual finalmente se multiplicaría por la velocidad a la que se desea proyectar la bala en cada paso. Estos dos valores finales se almacenarían y se sumarían a las coordenadas x e y iniciales cada paso. Parece complicado, ¿no? No lo es, mira: (los valores han sido redondeados a un solo decimal para efectos de simplicidad):
px = 100;
py = 425;
ex = 356;
ey = 83;
bullet_speed = 5;
vx = (ex - px) = 256
vy = (ey - py) = -342
len = sqr(vx2 + vy2) = sqr(65536 + 116964) = 427.2
vx = vx / len = 0.6
vy = vy / len = 0.8
speed_x = vx * bullet_speed = 3
speed_y = vy * bullet_speed = 4
Así que al final obtenemos que es necesario sumar 3 a la coordenada x de la bala y 4 a su coordenada y en cada paso.
point_direction(x1, y1, x2, y2)
Argumento | Descripción |
---|---|
x1 | La coordenada x del punto inicial del vector |
y1 | La coordenada y del punto inicial del vector |
x2 | La coordenada x del punto final del vector |
y2 | La coordenada y del punto final del vector |
Returns: Real
Esta función devuelve la dirección de un vector formado por las componentes especificadas [x1,y1] y [x2,y2] relativas a las coordenadas fijas x/y de la habitación. Por ejemplo, en la imagen de abajo, si queremos obtener la dirección de la nave del jugador a la posición del enemigo para poder lanzarle un misil, usaríamos esta función (el código está en el ejemplo más abajo):
var ex, ey;
ex = instance_nearest(x, y, enemy).x;
ey = instance_nearest(x, y, enemy).y;
with (instance_create(x, y, obj_Missile))
{
direction = point_direction(x, y, ex, ey);
}
El código anterior lee las coordenadas x e y del enemigo más cercano las cuales se pasan a un objeto bala (obj_Missile) para usarse en la función point_direction para ajustar la dirección de su trayectoria.
point_distance(x1, y1, x2, y2);
Argumento | Descripción |
---|---|
x1 | La coordenada x del punto inicial del vector |
y1 | La coordenada y del punto inicial del vector |
x2 | La coordenada x del punto final del vector |
y2 | La coordenada y del punto final del vector |
Devuelve: Real
Esta función devuelve la longitud de un vector formado por las componentes [x1,y1] y [x2,y2]. Por ejemplo, en la imagen de abajo, usaríamos esta función si queremos obtener la distancia entre la nave del jugador y el enemigo, para determinar si el enemigo está lo suficientemente cerca como para dispararle (el código exacto está en el ejemplo):
var ex, ey;
ex = instance_nearest(x, y, enemy).x;
ey = instance_nearest(x, y, enemy).y;
if point_distance(x, y, ex, ey) < 200
{
instance_create(x, y, obj_Missile)
}
El código anterior lee las coordenadas x e y del enemigo más cercano y las usa para calcular la distancia (longitud) del vector formado por éste y las coordenadas del jugador. Si el valor es menor a 200, el objeto jugador creará entonces una instancia del objeto "obj_Missile".
point_distance_3d(x1, y1, z1, x2, y2, z2);
Argumento | Descripción |
---|---|
x1 | La coordenada x del punto inicial del vector |
y1 | La coordenada y del punto inicial del vector |
z1 | La coordenada z del punto inicial del vector |
x2 | La coordenada x del punto final del vector |
y2 | La coordenada y del punto final del vector |
z2 | La coordenada z del punto final del vector |
Devuelve: Real
Esta función toma las componentes dadas del vector y devuelve la longitud (distancia) del vector. Funcioa de la misma manera que point_distance() pero con la adición de una tercera coordenada z (profundidad) para su aplicación en espacios tridimensionales.
var inst, ex, ey, ez;
inst = instance_nearest(x, y, enemy);
if inst
{
ex = inst.x;
ey = inst.y;
ez = inst.z;
if point_distance_3d(x, y, z, ex, ey, ez) < 200
{
instance_create(x, y, obj_Missile)
}
}
Este código lee las coordenadas x, y, z del enemigo más cercano y las usa para calcular la distancia (longitud) del vector formado por éstas y las coordenadas del jugador. Si el valor resultante es menor a 200, el objeto jugador creará una instancia del objeto "obj_Missile".
dot_product(x1, y1, x2, y2)
Argumento | Descripción |
---|---|
x1 | La componente x del primer vector. |
y1 | La componente y del primer vector. |
x2 | La componente x del segundo vector. |
y2 | La componente y del segundo vector. |
Devuelve: Real
El producto punto es un valor que expresa la relación angular entre dos vectores y se calcula multiplicando sus componentes y sumando el resultado. El nombre "producto punto" proviene del símbolo · que se usa para designar esta operación ("producto escalar" es su nombre alternativo, aunque enfatiza el resultado escalar más que su naturaleza vectorial).
La fórmula se puede escribir:
En 2D, el producto punto de dos vectores a[i1,j1] y b[i2, j2] es i1i2 + j1j2. De acuerdo a lo anterior, el producto punto se calcula de la siguiente manera en GameMaker:Studio:
a · b = (x1*x2)+(y1*y2);
La peculariedad acerca del producto punto es la relación que tiene con el ángulo formado por los dos vectores de entrada, la cual se puede expresar como:
a · b = (longitud de a) * (lonitud de b) * cos(ángulo)
Es decir, el producto punto de dos vectores es igual al coseno del ángulo formado entre los dos, multiplicado porla longitud de cada uno. La siguiente imagen lo ilustra:
De la fórmula y figura anteriores se deduce lo siguiente:
¿Qué significa todo esto trasladado al diseño de videojuegos? Bien, esta relación matemática puede usarse en distintas circunstancias, pero veamos una aplicación. Se desea crear una "línea de visión" para un enemigo en un juego de plataformas, de tal manera que el enemigo "vea" al jugador si ambos se ubican dentro de 90° (a cada lado) respecto del vector normal del enemigo.
Básicamente, debemos calcular el vector normal del enemigo (compuesto de una dirección y una distancia de visión) y luego obtener el vector que va desde el jugador al enemigo. Calculamos entonces el producto punto de ambos vectores, y si el resultado es positivo, el jugador es visto, y si es negativo no es visto.
var x1, y1, x2, y2;
x1 = lengthdir_x(1, image_angle);
y1 = lengthdir_y(1, image_angle);
x2 = o_Player.x - x;
y2 = o_Player.y - y;
if dot_product(x1, y1, x2, y2) > 0 seen=true else seen=false;
El código anterior crea un vector basado en la variable image angle de la instancia (enemigo), luego calcula el vector desde el objeto jugador "o_Player" hasta sí mismo. Por último, calcula el producto punto de los dos vectores y si es mayor a 0, la variable "seen" se ajusta a true y en caso contrario (igual o menor a 0) la variable "seen" se ajusta a false.
dot_product_3d(x1, y1, z1, x2, y2, z2)
Argumento | Descripción |
---|---|
x1 | La componente x del primer vector. |
y1 | La componente y del primer vector. |
z1 | La componente z del primer vector. |
x2 | La componente x del segundo vector. |
y2 | La componente y del segundo vector. |
z2 | La componente z del segundo vector. |
Devuelve: Real
El producto punto es un valor que expresa la relación angular entre dos vectores y se calcula multiplicando sus componentes y sumando el resultado. El nombre "producto punto" proviene del símbolo · que se usa para designar esta operación ("producto escalar" es su nombre alternativo, aunque enfatiza el resultado escalar más que su naturaleza vectorial).
La fórmula se puede escribir:
En 2D, el producto punto de dos vectores a[i1,j1] y b[i2, j2] es i1i2 + jij2, por lo que en 3D, el producto punto de los vectores a[i1,j1,k1] y b[i2,j2,k2] es i1i2 + j1j2 + k1k2. De acuerdo a lo anterior, dot_product_3d se calcula de la siguiente manera en GameMaker:Studio:
a · b = (x1*x2) + (y1*y2) + (z1*z2);
La peculariedad acerca del producto punto es la relación que tiene con el ángulo formado por los dos vectores de entrada, la cual se puede expresar como:
a · b = (longitud de a) * (lonitud de b) * cos(ángulo)
Es decir, el producto punto de dos vectores es igual al coseno del ángulo formado entre los dos, multiplicado porla longitud de cada uno. La siguiente imagen lo ilustra:
De la fórmula y figura anteriores se deduce lo siguiente:
¿Qué significa todo esto trasladado al diseño de videojuegos? Bien, esta relación matemática puede usarse en distintas circunstancias, pero veamos una aplicación. Un ejemplo sencillo y práctico sería generar una comprobación de "altura" en un juego, de tal manera que el enemigo "vea" al jugador cuando éste se encuentre por encima del plano formado por el vector normal del enemigo y el suelo 3D.
Básicamente, obtenemos el vector normal del enemigo, el cual es perpendicular al suelo y luego calculamos el vector jugador - enemigo. Después, calculamos el producto punto de estos dos vectores. SI el resultado es positivo, el jugador está "encima" del plano del enemigo y si es negativo está por debajo de éste.
var x1, y1, x2, y2;
x1 = 0;
y1 = 1;
z1 = 0;
x2 = o_Player.x - x;
y2 = o_Player.y - y;
z2 = o_Player.z - z;
if dot_product_3d(x1, y1, z1, x2, y2, z2) > 0 above=true else above=false;
El código anterior crea un vector normal a lo largo del eje y de la instancia (enemigo) y luego calcula el vector del objeto "o_Player" a la instancia del enemigo. Por último calcula el producto punto de los dos vectores; si es mayor a 0, la variable "above" se ajusta a true, pero si es menor o igual a 0 se ajusta a false.
dot_product_normalised(x1, y1, x2, y2)
Argumento | Descripción |
---|---|
x1 | La componente x del primer vector. |
y1 | La componente y del primer vector. |
x2 | La componente x del segundo vector. |
y2 | La componente x del segundo vector. |
Devuelve: Real
El producto punto es un valor que expresa la relación angular entre dos vectores y se calcula multiplicando sus componentes y sumando el resultado. El nombre "producto punto" proviene del símbolo · que se usa para designar esta operación ("producto escalar" es su nombre alternativo, aunque enfatiza el resultado escalar más que su naturaleza vectorial).
La fórmula se puede escribir:
En 2D, el producto punto de dos vectores a[i1,j1] y b[i2, j2] es i1i2 + j1j2. De acuerdo a lo anterior, el producto punto se calcula de la siguiente manera en GameMaker:Studio:
a · b = (x1*x2)+(y1*y2);
El producto punto normalizado se corrige de tal manera que devuelve un valor entre -1 y 1 (consulta Vectores Normalizados para más detalles), lo cual es realmente útil en determinadas circunstancias, especialmente cuando se trabaja con iluminación y otras funciones 3D.
var x1, y1, x2, y2;
x1 = lengthdir_x(1, image_angle);
y1 = lengthdir_y(1, image_angle);
x2 = o_Player.x - x;
y2 = o_Player.y - y;
if dot_product_normalised(x1, y1, x2, y2) > 0 seen=true else seen=false;
El código anterior crea un vector basado en la variable image angle de la instancia y luego calcula el vector desde el objeto jugador "o_Player" hasta sí misma. Por último, calcula el producto punto de los dos vectores y si es mayor a 0, ajusta la variable "seen" a true, y si es menor o igual a 0 "seen" se ajusta a false.
dot_product_normalised_3d(x1, y1, z1, x2, y2, z2)
Argumento | Descripción |
---|---|
x1 | La componente x del primer vector. |
y1 | La componente y del primer vector. |
z1 | La componente z del primer vector. |
x2 | La componente x del segundo vector. |
y2 | La componente y del segundo vector. |
z2 | La componente z del segundo vector. |
Devuelve: Real
El producto punto es un valor que expresa la relación angular entre dos vectores y se calcula multiplicando sus componentes y sumando el resultado. El nombre "producto punto" proviene del símbolo · que se usa para designar esta operación ("producto escalar" es su nombre alternativo, aunque enfatiza el resultado escalar más que su naturaleza vectorial).
La fórmula se puede escribir:
En 2D, el producto punto de dos vectores a[i1,j1] y b[i2, j2] es i1i2 + j1j2. De acuerdo a lo anterior, el producto punto se calcula de la siguiente manera en GameMaker:Studio:
a · b = (x1*x2) + (y1*y2) + (z1*z2);
El producto punto normalizado se corrige de tal manera que devuelveun valor entre -1 y 1 (consulta Vectores Normalizados para más detalles), lo cual es realmente útil en determinadas circunstancias, especialmente cuando se trabaja con iluminación y otras funciones 3D.
var x1, y1, x2, y2;
x1 = 0;
y1 = 1;
z1 = 0;
x2 = o_Player.x - x;
y2 = o_Player.y - y;
z2 = o_Player.z - z;
if dot_product_normalised_3d(x1, y1, z1, x2, y2, z2) > 0 above=true else above=false;
El código anterior crea un vector normal a lo largo del eje y de la instancia, y luego calcula el vector que va del objeto "o_Player" hasta si misma. Por último calcula el producto punto de los dos vectores; si es mayor a 0, la variable "above" se ajusta a true, pero si es menor o igual a 0 se ajusta a false.
angle_difference(ang1, ang2)
Argumento | Descripción |
---|---|
ang1 | El primer ángulo. |
ang2 | El segundo ángulo. |
Devuelve: Real
Esta función devuelve la diferencia más pequeña entre dos ángulos como un valor comprendido -180 y 180 grados (en donde un ángulo positivo tiene sentido antihorario y uno negativo tiene sentido horario).
var pd = point_direction(x, y, mouse_x, mouse_y);
var dd = angle_difference(image_angle, pd);
image_angle += min(abs(dd), 10) * sign(dd);
Éste código obtiene el ángulo del puntero del mouse respecto a la instancia, y luego calcula la diferencia entre éste ángulo y el image_angle de la instancia, usando el resultado de la resta para girarla léntamente hacia el puntero.