Advertisement
  1. Code
  2. ActionScript

Vectores euclidianos en Flash

Scroll to top
Read Time: 22 min
This post is part of a series called You Do The Math.
Playing Around with Elastic Collisions
Gravity in Action

() translation by (you can also view the original English article)

Dos veces al mes, revisamos algunas de las publicaciones favoritas de nuestros lectores de la historia de Activetuts +. El tutorial retroactivo de esta semana, publicado por primera vez en abril, es una guía de vectores euclidianos: qué son, por qué los usarían y cómo implementarlos en Flash con AS3.

Los vectores euclidianos son objetos en geometría con ciertas propiedades que son muy útiles para desarrollar juegos. Se pueden ver como puntos, pero también tienen una magnitud y una dirección. Se representan como flechas que van desde el punto inicial hasta el punto final, y así es como las dibujaremos en este artículo.

Los vectores euclidianos se utilizan comúnmente en matemáticas y física para muchas cosas: pueden representar la velocidad, la aceleración y las fuerzas en física, o ayudar a demostrar muchos teoremas importantes en matemáticas. En este tutorial, aprenderá sobre vectores euclidianos y construirá una clase que puede usar en sus propios proyectos Flash.

Tenga en cuenta que los vectores euclidianos son diferentes a la clase Vector de ActionScript y también diferentes al dibujo vectorial.

Los vectores se pueden usar en el entorno Flash para ayudarlo a lograr tareas complejas que de otra manera requerirían mucho esfuerzo si se realizaran sin ellas. En este artículo aprenderás a usarlos en Flash, y aprenderás muchos trucos geniales con vectores.


Paso 1: Coordenadas cartesianas y coordenadas del flash

Antes de saltar a los vectores, introduzcamos el sistema de coordenadas de Flash. Probablemente esté familiarizado con el sistema de coordenadas cartesianas (incluso si no lo conoce por su nombre):

Flash math vectors in AS3

El sistema de Flash es muy similar. La única diferencia es que el eje y está al revés:

Flash math vectors in AS3

Cuando comenzamos a trabajar con vectores en flash, debemos recordar eso. Sin embargo, son buenas noticias: este sistema diferente no hace mucha diferencia. Trabajar con vectores será básicamente como trabajar con vectores en el sistema cartesiano.


Paso 2: definir un vector

Para el propósito de este tutorial, definiremos y trabajaremos con los puntos iniciales de todos los vectores como el punto de registro de la etapa, tal como se usan comúnmente en las matemáticas. Un vector se definirá como un punto común, pero tendrá propiedades de magnitud y ángulo. Eche un vistazo a algunos vectores de ejemplo definidos en el escenario:

Flash math vectors in AS3Flash math vectors in AS3Flash math vectors in AS3

Como puede ver, un vector está representado por una flecha, y cada vector tiene una cierta longitud (o magnitud) y puntos a lo largo de cierto ángulo. La cola de cada vector está en el punto de registro (0, 0).

Crearemos una clase EuclideanVector simple para este tutorial, usando la clase Point para mantener las coordenadas del vector. Vamos a crear la clase básica de vectores ahora:

1
package
2
{
3
  import flash.geom.Point;
4
	
5
	public class EuclideanVector
6
	{
7
		public var position:Point;
8
		public var magnitude:Number;
9
		public var angle:Number;
10
		
11
		public function EuclideanVector(endPoint:Point)
12
		{
13
			position = endPoint;
14
		}
15
	}
16
}

Durante este tutorial, hablaremos sobre el sentido y la dirección de un vector. Tenga en cuenta que la dirección solo define una línea que "contiene" el vector. El sentido es lo que define de qué manera el vector señala a lo largo de esta línea.


Paso 3: inverso de un vector

En este tutorial usaremos la expresión "inverso de un vector". El inverso de un vector es otro vector con la misma magnitud y dirección, pero un sentido contrario. Eso se traduce en un vector con la señal opuesta a las coordenadas del primer vector. Entonces un vector con un punto final de (x, y) tendría un vector inverso con un punto final de (-x, -y).

Flash math vectors in AS3

Agreguemos una función a nuestra clase EuclideanVector para devolver el vector inverso:

1
public function inverse():EuclideanVector
2
{
3
	return new EuclideanVector(new Point(-position.x, -position.y));
4
}

Paso 4: operaciones básicas Adición

Ahora que hemos aprendido cómo definir un vector, aprendamos cómo agregar dos vectores: es tan simple como agregar sus coordenadas por separado. Mira esta imagen:

Flash math vectors in AS3Flash math vectors in AS3Flash math vectors in AS3

Si observa en la imagen, el resultado de la adición de dos vectores es otro vector, y puede ver que sus coordenadas son la suma de las coordenadas de los otros dos vectores. En el código, se vería así:

1
public function sum(otherVector:EuclideanVector):EuclideanVector
2
{
3
	position.x += otherVector.position.x;
4
	position.y += otherVector.position.y;
5
	
6
	return this;
7
}

Entonces podemos decir que:

1
vecR == vec1.sum(vec2);

Paso 5: operaciones básicas Resta

La resta funciona casi igual que la suma, pero en su lugar agregaremos el inverso del segundo vector al primer vector.

Flash math vectors in AS3Flash math vectors in AS3Flash math vectors in AS3

Ya se sabe cómo sumar dos vectores, así que aquí está el código para restar:

1
public function subtract(otherVector:EuclideanVector):EuclideanVector
2
{
3
	position.x -= otherVector.position.x;
4
	position.y -= otherVector.position.y;
5
	
6
	return this;
7
}

Este código es extremadamente útil para obtener un vector que va del punto de un vector al punto de otro. Mira nuevamente la imagen y verás que esto es verdad. Se usará mucho en los ejemplos posteriores.


Paso 6: operaciones básicas Multiplicación por un número

La multiplicación entre un vector y un número (los números regulares se conocen como "escalares" en matemáticas vectoriales) da como resultado un vector que ha tenido una magnitud multiplicada por este número, pero apuntando en la misma dirección; está "estirado" si el escalar es mayor que 1, y aplastado si el escalar está entre 0 y 1. El sentido del nuevo vector será el mismo que el vector original si el escalar es positivo, o el opuesto si es negativo. Básicamente, este número "escala" el vector. Mira la imagen:

Flash math vectors in AS3

En el código, solo multiplicamos las coordenadas de un vector por el número, que luego escalará el vector:

1
public function multiply(number:Number):EuclideanVector
2
{
3
	position.x *= number;
4
	position.y *= number;
5
	
6
	return this;
7
}

Paso 7: Obtener la Magnitud de un Vector

Para obtener la magnitud de un vector, utilizaremos el teorema de Pitágoras. Si olvidó lo que es, aquí hay un repaso rápido:

Flash math vectors in AS3

(Más información aquí)

El código es muy simple:

1
public function magnitude():Number
2
{
3
	return Math.sqrt((position.x * position.x) + (position.y * position.y));
4
}

También debe eliminar la línea public var magnitude:Number, ya que esto es lo que usaremos a partir de ahora.

La magnitud de un vector siempre será positiva, ya que es la raíz cuadrada de la suma de dos números positivos.


Paso 8: obtener el ángulo de un vector

El ángulo de un vector es el ángulo entre el eje xy la línea de dirección del vector. El ángulo se mide desde el eje x y girando en sentido antihorario hasta la línea de dirección en el sistema cartesiano:

Flash math vectors in AS3Flash math vectors in AS3Flash math vectors in AS3

Sin embargo, en el sistema de coordenadas de Flash, dado que el eje y está al revés, este ángulo se medirá girando en el sentido de las agujas del reloj:

Flash math vectors in AS3Flash math vectors in AS3Flash math vectors in AS3

Esto se puede calcular fácilmente usando el siguiente código. El ángulo se devolverá en radianes, en un rango de 0 a 2pi. Si no sabes qué son los radianes o cómo usarlos, este tutorial de Michael James Williams te ayudará mucho.

1
public function angle():Number
2
{
3
	var angle:Number = Math.atan2(position.y, position.x);
4
	
5
	if (angle < 0)
6
	{
7
		angle += Math.PI * 2;
8
	}
9
	
10
	return angle;
11
}

Paso 9: producto escalar

El producto escalar entre dos vectores es un número aparentemente sin significado, pero tiene dos usos útiles. Primero veamos cómo se puede calcular el producto escalar:

Flash math vectors in AS3Flash math vectors in AS3Flash math vectors in AS3

Pero también se puede obtener por las coordenadas de cada vector:

Flash math vectors in AS3Flash math vectors in AS3Flash math vectors in AS3

El producto escalar puede decirnos mucho sobre el ángulo entre los vectores: si es positivo, entonces el ángulo oscila entre 0 y 90 grados. Si es negativo, el ángulo oscila entre 90 y 180 grados. Si es cero, el ángulo es de 90 grados. Eso sucede porque en la primera fórmula, solo el coseno es responsable de darle al producto escalar una "señal": las magnitudes son siempre positivas. Pero sabemos que un coseno positivo significa que el ángulo oscila entre 0 y 90 grados, y así sucesivamente para los cosenos negativos y cero.

El producto de puntos también se puede usar para representar la longitud de un vector en la dirección del otro vector. Piense en ello como una proyección. Esto resulta extremadamente útil en cosas como el Teorema de Separación de Ejes (SAT) y su implementación en AS3 para la detección de colisiones y la respuesta en juegos.

Aquí está el código práctico para obtener el producto escalar entre dos vectores:

1
public function dot(otherVector:EuclideanVector):Number
2
{
3
	return (position.x * otherVector.position.x) + (position.y * otherVector.position.y);
4
}

Paso 10: Ángulo más pequeño entre vectores

El ángulo entre vectores, como se ve en el Paso 9, puede ser dado por el producto de puntos. Aquí es cómo calcularlo:

1
public function angleBetween(otherVector:EuclideanVector):Number
2
{
3
	return Math.acos(dot(otherVector) / (magnitude() * otherVector.magnitude()));
4
}

Paso 11: Ángulo a distancia entre vectores

También hay otra forma de calcular el ángulo, que da resultados entre -pi y pi y siempre calcula el ángulo que va del primer vector al segundo vector; esto es útil cuando desea integrarse fácilmente con la rotación de un objeto de visualización (que va de -180 a 180).

El método funciona obteniendo el ángulo para ambos vectores, luego restando los ángulos y trabajando en el resultado.

Flash math vectors in AS3Flash math vectors in AS3Flash math vectors in AS3

El código:

1
public function rangedAngleBetween(otherVector:EuclideanVector):Number
2
{
3
	var firstAngle:Number;
4
	var secondAngle:Number;
5
	
6
	var angle:Number;
7
	
8
	firstAngle = Math.atan2(otherVector.position.y, otherVector.position.x);
9
	secondAngle = Math.atan2(position.y, position.x);
10
	
11
	angle = secondAngle - firstAngle;
12
	
13
	while (angle > Math.PI)
14
		angle -= Math.PI * 2;
15
	while (angle < -Math.PI)
16
		angle += Math.PI * 2;
17
	
18
	return angle;
19
}

Tenga en cuenta que este ángulo devuelve positivo si secondAngle es más alto que firstAngle, por lo que el orden en el que obtiene el ángulo de rango afectará el resultado.


Paso 12: normalizando un vector

La normalización de un vector significa hacer que su magnitud sea igual a 1, mientras se preserva la dirección y el sentido del vector. Para hacer eso, multiplicamos el vector por 1/magnitude. De esta forma, su magnitud se reducirá o aumentará a 1.

1
public function normalize():EuclideanVector
2
{
3
	var m:Number = magnitude();
4
	position.x /= m;
5
	position.y /= m;
6
	
7
	return this;
8
}

Paso 13: Obteniendo el normal de un vector

El normal de un vector es otro vector que forma un ángulo de 90 grados con respecto al primero. Se puede calcular mediante las siguientes fórmulas:

Flash math vectors in AS3

Las fórmulas se basan en el hecho de que, como la normalidad es siempre perpendicular a un vector, solo necesitamos cambiar el orden de las coordenadas xey e invertir una de ellas para obtener una normal. La siguiente imagen muestra el proceso:

Flash math vectors in AS3

En la imagen, Vec es el vector original, Vec2 es el vector con las coordenadas intercambiadas de Vec, y Vec3 es un vector con la coordenada y negativa de Vec2. Ang y Ang2 son variables, pero el ángulo entre Vec y Vec3 es siempre de 90 grados.

Y el código es simple

1
public function normalRight():EuclideanVector
2
{
3
	return new EuclideanVector(new Point(-position.y, position.x));
4
}
5
6
public function normalLeft():EuclideanVector
7
{
8
	return new EuclideanVector(new Point(position.y, -position.x));
9
}

Paso 14: girando un vector

Para rotar un vector, asumimos que la posición (0, 0) (su punto inicial) será el centro de rotación. El punto girado está dado por la fórmula:

Flash math vectors in AS3Flash math vectors in AS3Flash math vectors in AS3

Esta fórmula se obtiene aplicando una matriz de rotación a ese vector. Estaríamos yendo más allá del alcance de este tutorial si entramos en la matriz y cómo funciona, así que simplemente dejaré la fórmula aquí.

El código es más o menos el mismo:

1
public function rotate(angleInRadians:Number):EuclideanVector
2
{
3
	var newPosX:Number = (position.x * Math.cos(angleInRadians)) - (position.y * Math.sin(angleInRadians));
4
	var newPosY:Number = (position.x * Math.sin(angleInRadians)) + (position.y * Math.cos(angleInRadians));
5
	
6
	position.x = newPosX;
7
	position.y = newPosY;
8
	
9
	return this;
10
}

Este es el final de nuestras operaciones básicas de vectores. Lo que verá a continuación es maneras de usar esta clase para hacer cosas interesantes. Aquí está nuestra clase hasta ahora:

1
package
2
{
3
	import flash.geom.Point;
4
	
5
	public class EuclideanVector
6
	{
7
		public var position:Point;
8
		public var angle:Number;
9
		
10
		public function EuclideanVector(endPoint:Point)
11
		{
12
			position = endPoint;
13
		}
14
		
15
		public function inverse():EuclideanVector
16
		{
17
			return new EuclideanVector(new Point(-position.x, -position.y));
18
		}
19
		
20
		public function sum(otherVector:EuclideanVector):EuclideanVector
21
		{
22
			position.x += otherVector.position.x;
23
			position.y += otherVector.position.y;
24
			
25
			return this;
26
		}
27
		
28
		public function subtract(otherVector:EuclideanVector):EuclideanVector
29
		{
30
			position.x -= otherVector.position.x;
31
			position.y -= otherVector.position.y;
32
			
33
			return this;
34
		}
35
		
36
		public function multiply(number:Number):EuclideanVector
37
		{
38
			position.x *= number;
39
			position.y *= number;
40
			
41
			return this;
42
		}
43
		
44
		public function magnitude():Number
45
		{
46
			return Math.sqrt((position.x * position.x) + (position.y * position.y));
47
		}
48
		
49
		public function angle():Number
50
		{
51
			var angle:Number = Math.atan2(position.y, position.x);
52
			
53
			if (angle < 0)
54
			{
55
				angle += Math.PI * 2;
56
			}
57
			
58
			return angle;
59
		}
60
		
61
		public function dot(otherVector:EuclideanVector):Number
62
		{
63
			return (position.x * otherVector.position.x) + (position.y * otherVector.position.y);
64
		}
65
		
66
		public function angleBetween(otherVector:EuclideanVector):Number
67
		{
68
			return Math.acos(dot(otherVector) / (magnitude() * otherVector.magnitude()));
69
		}
70
		
71
		public function rangedAngleBetween(otherVector:EuclideanVector):Number
72
		{
73
			var firstAngle:Number;
74
			var secondAngle:Number;
75
			
76
			var angle:Number;
77
			
78
			firstAngle = Math.atan2(otherVector.position.y, otherVector.position.x);
79
			secondAngle = Math.atan2(position.y, position.x);
80
			
81
			angle = secondAngle - firstAngle;
82
			
83
			while (angle > Math.PI)
84
				angle -= Math.PI * 2;
85
			while (angle < -Math.PI)
86
				angle += Math.PI * 2;
87
			
88
			return angle;
89
		}
90
		
91
		public function normalize():EuclideanVector
92
		{
93
			position.x /= magnitude();
94
			position.y /= magnitude();
95
			
96
			return this;
97
		}
98
		
99
		public function normalRight():EuclideanVector
100
		{
101
			return new EuclideanVector(new Point(-position.y, position.x));
102
		}
103
		
104
		public function normalLeft():EuclideanVector
105
		{
106
			return new EuclideanVector(new Point(position.y, -position.x));
107
		}
108
		
109
		public function rotate(angleInRadians:Number):EuclideanVector
110
		{
111
			var newPosX:Number = (position.x * Math.cos(angleInRadians)) - (position.y * Math.sin(angleInRadians));
112
			var newPosY:Number = (position.x * Math.sin(angleInRadians)) + (position.y * Math.cos(angleInRadians));
113
			
114
			position.x = newPosX;
115
			position.y = newPosY;
116
			
117
			return this;
118
		}
119
	}
120
}

OK, hemos cubierto la construcción de la clase vectorial, ahora tomemos una decisión de utilizarla.


Paso 15: determinar si un punto está dentro de un polígono

La acción comienza aquí. Determinar si un punto se encuentra dentro de un polígono o no es un tema muy interesante, y hay muchos métodos para lograrlo. En este artículo, presentaré los tres métodos que generalmente se usan:

  • El Número de cruce o el algoritmo de regla par o impar, que determina si un punto está dentro de un polígono a partir del número de aristas que cruza un "rayo" desde el punto hasta el infinito.
  • El algoritmo del número de cuerda, que da la respuesta basada en la suma de todos los ángulos formados entre los vértices consecutivos de un polígono y el punto a verificar.
  • El algoritmo de polígono convexo, que, como su nombre lo indica, solo funciona para polígonos convexos y se basa en si un punto está o no en un cierto "lado" de cada borde del polígono.

Todos estos algoritmos dependerán del hecho de que conoces las coordenadas de los vértices (esquinas) que definen el polígono.


Paso 16: El número de cruce o el algoritmo de regla par impar

Este algoritmo se puede usar para cualquier forma. Esto es lo que lees: cualquier forma, tiene agujeros o no, ya sea convexo o no. Se basa en el hecho de que cualquier rayo que se proyecte desde el punto que deseas ver hasta el infinito cruzará un número par de aristas si el punto está fuera de la forma, o un número impar de aristas si el punto está dentro de la forma. Esto puede ser probado por el teorema de la curva de Jordan, lo que implica que tendrá que cruzar un límite entre una región y otra región si desea pasar de una a otra. En nuestro caso, nuestras regiones están "dentro de la forma" y "fuera de la forma".

Flash math vectors in AS3
Flash math vectors in AS3
Flash math vectors in AS3

El código para este algoritmo es el siguiente:

1
public function isPointInsideShape1(point:EuclideanVector, shapeVertices:Vector.<EuclideanVector>):Boolean
2
{
3
	var numberOfSides:int = shapeVertices.length;
4
	
5
	var i:int = 0;
6
	var j:int = numberOfSides - 1;
7
	
8
	var oddNodes:Boolean = false;
9
	
10
	while (i < numberOfSides)
11
	{
12
		if ((shapeVertices[i].position.y < point.position.y && shapeVertices[j].position.y >= point.position.y) ||
13
			(shapeVertices[j].position.y < point.position.y && shapeVertices[i].position.y >= point.position.y))
14
		{
15
			if (shapeVertices[i].position.x + (((point.position.y - shapeVertices[i].position.y) / (shapeVertices[j].position.y - shapeVertices[i].position.y)) *
16
				(shapeVertices[j].position.x - shapeVertices[i].position.x)) < point.position.x)
17
			{
18
				oddNodes = !oddNodes;
19
			}
20
		}
21
		
22
		j = i;
23
		
24
		i++;
25
	}
26
	
27
	return oddNodes;
28
}

Devolverá false si el punto no está dentro de la forma o true si el punto está dentro de la forma.


Paso 17: Algoritmo del número de cuerda

El algoritmo de número de cuerda usa la suma de todos los ángulos hechos entre el punto a verificar y cada par de puntos que definen el polígono. Si la suma está cerca de 2pi, entonces el punto que se verifica está dentro del vector. Si está cerca de 0, entonces el punto está afuera.

Flash math vectors in AS3
1
public function isPointInsideShape2(point:EuclideanVector, shapeVertices:Vector.<EuclideanVector>):Boolean
2
{
3
	var numberOfSides:int = shapeVertices.length;
4
	
5
	var i:int = 0;
6
	var angle:Number = 0;
7
	
8
	var rawAngle:Number = 0;
9
	
10
	var firstVector:EuclideanVector;
11
	var secondVector:EuclideanVector;
12
	
13
	while(i < numberOfSides)
14
	{
15
		firstVector = new EuclideanVector(new Point(shapeVertices[i].position.x - point.position.x, shapeVertices[i].position.y - point.position.y));
16
		secondVector = new EuclideanVector(new Point(shapeVertices[(i + 1) % numberOfSides].position.x - point.position.x, shapeVertices[(i + 1) % numberOfSides].position.y - point.position.y));
17
		
18
		angle += secondVector.rangedAngleBetween(firstVector);
19
		
20
		i++;
21
	}
22
	
23
	if(Math.abs(angle) < Math.PI)
24
		return false;
25
	else
26
		return true;
27
}

El código usa el ángulo entre vectores y da espacio para imprecisiones: observe cómo estamos verificando los resultados de la suma de todos los ángulos. No verificamos si el ángulo es exactamente cero o 2pi. En cambio, verificamos si es menor que pi y mayor que pi, un valor mediano considerable.


Paso 18: Algoritmo del polígono cóncavo

El algoritmo de polígono cóncavo se basa en el hecho de que, para un polígono cóncavo, un punto dentro de él siempre está a la izquierda de los bordes (si los estamos bucleando en sentido contrario a las agujas del reloj) oa la derecha de los bordes (si estamos pasando por ellos en el sentido de las agujas del reloj).

Flash math vectors in AS3

Imagínese de pie en una habitación con la forma de la imagen de arriba, y camine alrededor de los bordes de la misma con su mano izquierda arrastrándose a lo largo de la pared. En el punto a lo largo de la pared donde estás más cerca del punto que te interesa, si está a tu derecha, entonces debe estar dentro de la habitación; si está a la izquierda, entonces debe estar afuera.

El problema radica en determinar si un punto está a la izquierda oa la derecha de un borde (que básicamente es un vector). Esto se hace a través de la siguiente fórmula:

Flash math vectors in AS3Flash math vectors in AS3Flash math vectors in AS3

Esa fórmula devuelve un número menor que 0 para los puntos a la derecha del borde, y mayor que 0 para los puntos a la izquierda del mismo. Si el número es igual a 0, el punto se encuentra en el borde y se considera dentro de la forma. El código es el siguiente:

1
public function isPointInsideShape3(point:EuclideanVector, shapeVertices:Vector.<EuclideanVector>):Boolean
2
{
3
	var numberOfSides:int = shapeVertices.length;
4
	
5
	var i:int = 0;
6
	
7
	var firstEdgePoint:EuclideanVector;
8
	var secondEdgePoint:EuclideanVector;
9
	
10
	var leftOrRightSide:Boolean;
11
	
12
	while(i < numberOfSides)
13
	{
14
		firstEdgePoint = shapeVertices[i];
15
		secondEdgePoint = shapeVertices[(i + 1) % numberOfSides];
16
		
17
		if(i == 0)
18
		{
19
			// Determining if the point is to the left or to the right of first edge

20
			// true for left, false for right

21
			
22
			leftOrRightSide = ((point.position.y - firstEdgePoint.position.y) * (secondEdgePoint.position.x - firstEdgePoint.position.x)) - ((point.position.x - firstEdgePoint.position.x) * (secondEdgePoint.position.y - firstEdgePoint.position.y)) > 0;
23
		}
24
		else
25
		{
26
			// Now all edges must be on the same side

27
			
28
			if(leftOrRightSide && ((point.position.y - firstEdgePoint.position.y) * (secondEdgePoint.position.x - firstEdgePoint.position.x)) - ((point.position.x - firstEdgePoint.position.x) * (secondEdgePoint.position.y - firstEdgePoint.position.y)) < 0)
29
			{
30
				// Not all edges are on the same side!

31
				
32
				return false;
33
			}
34
			else if(!leftOrRightSide && ((point.position.y - firstEdgePoint.position.y) * (secondEdgePoint.position.x - firstEdgePoint.position.x)) - ((point.position.x - firstEdgePoint.position.x) * (secondEdgePoint.position.y - firstEdgePoint.position.y)) > 0)
35
			{
36
				// Not all edges are on the same side!

37
				
38
				return false;
39
			}
40
		}
41
		
42
		i++;
43
	}
44
	
45
	// We looped through all vertices and didn't detect different sides

46
	
47
	return true;
48
}

Este código funciona independientemente de si tienes los vértices de la forma definidos en sentido horario o antihorario.


Paso 19: Ray Casting

Ray casting es una técnica que se usa a menudo para detectar colisiones y renderizar. Consiste en un rayo que se lanza de un punto a otro (o hasta el infinito). Este rayo está hecho de puntos o vectores, y generalmente se detiene cuando golpea un objeto o el borde de la pantalla. De forma similar a los algoritmos de punto en forma, hay muchas maneras de lanzar rayos, y veremos dos de ellos en esta publicación:

  • El algoritmo de línea de Bresenham, que es una manera muy rápida de determinar puntos cercanos que darían una aproximación de una línea entre ellos.
  • El método DDA (Digital Differential Analyzer), que también se usa para crear una línea.

En los próximos dos pasos analizaremos ambos métodos. Después de eso, veremos cómo hacer que nuestro rayo se detenga cuando golpea un objeto. Esto es muy útil cuando necesita detectar colisión contra objetos que se mueven rápidamente.


Paso 20: El algoritmo de la línea de Bresenham

Este algoritmo se usa muy a menudo en gráficos de computadora, y depende de la convención de que la línea siempre se creará apuntando hacia la derecha y hacia abajo. (Si se debe crear una línea en las direcciones hacia arriba y hacia la izquierda, todo se invierte más adelante). Vamos a entrar en el código:

1
public function createLineBresenham(startVector:EuclideanVector, endVector:EuclideanVector):Vector.<EuclideanVector>
2
{
3
	var points:Vector.<EuclideanVector> = new Vector.<EuclideanVector>();
4
	
5
	var steep:Boolean = Math.abs(endVector.position.y - startVector.position.y) > Math.abs(endVector.position.x - startVector.position.x);
6
	
7
	var swapped:Boolean = false;
8
	
9
	if (steep)
10
	{
11
		startVector = new EuclideanVector(new Point(startVector.position.y, startVector.position.x));
12
		endVector = new EuclideanVector(new Point(endVector.position.y, endVector.position.x));
13
	}
14
	
15
	// Making the line go downward

16
	if (startVector.position.x > endVector.position.x)
17
	{
18
		var temporary:Number = startVector.position.x;
19
		
20
		startVector.position.x = endVector.position.x;
21
		
22
		endVector.position.x = temporary;
23
		
24
		temporary = startVector.position.y;
25
		
26
		startVector.position.y = endVector.position.y
27
		
28
		endVector.position.y = temporary;
29
		
30
		swapped = true;
31
	}
32
	
33
	var deltaX:Number = endVector.position.x - startVector.position.x;
34
	var deltaY:Number = Math.abs(endVector.position.y - startVector.position.y);
35
	
36
	var error:Number = deltaX / 2;
37
	
38
	var currentY:Number = startVector.position.y;
39
	
40
	var step:int;
41
	
42
	if (startVector.position.y < endVector.position.y)
43
	{
44
		step = 1;
45
	}
46
	else
47
	{
48
		step = -1;
49
	}
50
	
51
	var iterator:int = startVector.position.x;
52
	
53
	while (iterator < endVector.position.x)
54
	{
55
		if (steep)
56
		{
57
			points.push(new EuclideanVector(new Point(currentY, iterator)));
58
		}
59
		else
60
		{
61
			points.push(new EuclideanVector(new Point(iterator, currentY)));
62
		}
63
		
64
		error -= deltaY;
65
		
66
		if (error < 0)
67
		{
68
			currentY += step;
69
			error += deltaX;
70
		}
71
		
72
		iterator++;
73
	}
74
	
75
	if (swapped)
76
	{
77
		points.reverse();
78
	}
79
	
80
	return points;
81
}

El código producirá un vector AS3 de vectores euclidianos que formarán la línea. Con este Vector, más adelante podemos verificar colisiones.


Paso 21: El método DDA

Una implementación del Analizador Diferencial Digital se usa para interpolar variables entre dos puntos. A diferencia del algoritmo de línea de Bresenham, este método solo creará vectores en posiciones enteras para simplificar. Aquí está el código:

1
public function createLineDDA(startVector:EuclideanVector, endVector:EuclideanVector):Vector.<EuclideanVector>
2
{
3
	var points:Vector.<EuclideanVector> = new Vector.<EuclideanVector>();
4
5
	var dx:Number;
6
	var dy:Number;
7
8
	var _x:Number = startPoint.position.x;
9
	var _y:Number = startPoint.position.y;
10
11
	var m:Number;
12
13
	var i:int;
14
15
	dx = endPoint.position.x - startPoint.position.x;
16
	dy = endPoint.position.y - startPoint.position.y;
17
18
	if (Math.abs(dx) >= Math.abs(dy))
19
		m = Math.abs(dx);
20
	else
21
		m = Math.abs(dy);
22
23
	points.push(new EuclideanVector(new Point(int(_x), int(_y))));
24
25
	i = 1;
26
27
	while (i <= m)
28
	{
29
		_x += dx / m;
30
		_y += dy / m;
31
		
32
		points.push(new EuclideanVector(new Point(int(_x), int(_y))));
33
		
34
		i++;
35
	}
36
	
37
	return points;
38
}

Este código también devolverá un vector AS3 de vectores euclidianos.


Paso 22: Verificar colisiones usando rayos

Verificar la colisión por rayos es muy simple. Como un rayo consta de muchos vectores, comprobaremos las colisiones entre cada vector y una forma, hasta que se detecte uno o hasta que se alcance el final. En el siguiente código, shapeToCheck tendrá una forma como las que hemos estado usando en los pasos 13-16. Aquí está el código:

1
public function checkRayCollision(ray:Vector.<EuclideanVector>, shape:Vector.<EuclideanVector>):Boolean
2
{
3
	var rayLength:int = ray.length;
4
	
5
	var i:int = 0;
6
	
7
	while(i < rayLength)
8
	{
9
		if(isPointInsideShape1(ray[i], shape))
10
		{
11
			return true;
12
		}
13
		
14
		i++;
15
	}
16
	
17
	return false;
18
}

Puede usar cualquier función de punto dentro de la forma con la que se sienta cómodo, ¡pero preste atención a las limitaciones de la última!


Conclusión

¡Estás listo para comenzar a utilizar este conocimiento en todas partes ahora! Será útil muchas veces y le ahorrará muchos cálculos adicionales cuando intente hacer cosas más complejas en Flash.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.