Detección de colisiones a nivel de píxeles para gráficos transformados

En el tutorial anterior, analizamos los aspectos básicos de la detección de colisiones a nivel de píxeles. En este tutorial, exploraremos el uso de matrices para definir mejor el área de interés, muy útil para los gráficos que han sido rotados, traducidos o sesgados..


Detección de colisiones

Esta es la pieza final que trataremos de programar. Haz clic en la bola y engancha para comenzar la demostración interactiva..

Observe cómo, a pesar de que el gráfico del árbol de coco gira y se transforma de otra manera, todavía tenemos una detección de colisiones con un píxel perfecto.


Paso 1: El dibujar Función

La detección de colisiones a nivel de píxel requiere que el gráfico se convierta en un BitmapData objeto. Lo hemos visto en el tutorial anterior utilizando la analogía de una radiografía. En este tutorial, explicaré este proceso de "rayos X".

Supongamos que tenemos una pieza de un gráfico (diagrama 1) y queríamos convertirlo en un BitmapData objeto; Necesitamos definir las dimensiones de la BitmapData objeto primero (diagrama 2). En este caso, es bastante simple porque los gráficos anchura y altura Las propiedades proporcionan esto. Entonces llamamos al dibujar() método; Los píxeles que están al menos ocupados por el gráfico se rellenarán, en teoría (diagrama 3). Esta matriz de píxeles se comparará con otra matriz de píxeles de otro gráfico para verificar una colisión entre los dos (diagrama 4).


Paso 2: Diferentes espacios de coordenadas

Hay diferentes espacios de coordenadas utilizados en Flash IDE (diagramas 1 y 2 anteriores). Estoy seguro de que todos los lectores habrían experimentado esto: vea mi tutorial sobre espacios afines para obtener una visión más detallada.

En el IDE de Flash, dibujamos una imagen y la convertimos en un símbolo de tipo Clip de película. Cuando hacemos doble clic en el Clip de película, Llegamos a un espacio de coordenadas diferente (diagrama 3). Desde aquí, podemos hacer clic en la etiqueta del escenario para salir del espacio de coordenadas locales de este gráfico y llegar al espacio de coordenadas del escenario. Entonces podemos empezar a transformar la instancia de Clip de película en el escenario (diagrama 4). De hecho podemos crear múltiples instancias de la Clip de película, Cada uno de ellos tiene diferentes transformaciones..

En la Biblioteca, el gráfico original permanece sin cambios a pesar de todos los ajustes realizados en sus copias en el escenario. Esto es racional porque cada vez que hacemos una nueva copia de un Clip de película En el escenario, siempre son iguales a la copia original en la biblioteca. Ahora la pregunta es: "¿Cómo captura Flash toda la transformación que hemos hecho a las copias en el escenario?" Bueno, cada uno usa el MovieClip.transform.matrix propiedad para capturar todas sus transformaciones (traducción, rotación, sesgo, etc.).

Ahora volvamos a donde lo dejamos. Es crucial que entendamos esto porque la dibujar() método de BitmapData no se refiere a la instancia gráfica en el escenario cuando se realiza una "radiografía", sino a la fuente gráfica sin cambios en la biblioteca.

los BitmapData El primer píxel del objeto se alinea con el punto de registro (el punto rojo) del Clip de película en el espacio de coordenadas local (consulte el diagrama 3 en el Paso 1), luego capture el gráfico en forma de píxeles con las dimensiones que especificamos.

Cuando se trata de hitTest cheques, ActionScript alinea este primer píxel (el píxel superior izquierdo) del BitmapData Objeto con el punto de registro de la instancia gráfica en el escenario. Con esto, todos los píxeles en el BitmapData el objeto se mapeará en el espacio de coordenadas en el escenario y obtendrá sus coordenadas individuales. Las verificaciones se pueden realizar más adelante comparando estas coordenadas entre dos mapas de bits para ver si alguno de los píxeles se superpone.

Nota: Esta explicación asume la Clip de película o Duende La instancia se agrega a la lista de visualización del escenario. En ActionScript, podemos agregar objetos de visualización a la propia clase de documento porque se extiende Clip de película o Duende.


Paso 3: El problema

Entonces, si el gráfico de interés se gira en el escenario, ¿cómo lo hacemos? dibujar()?

Del diagrama anterior, podemos ver claramente estos problemas..

Diagrama Problema Descripción
1 Dimensión de BitmapData objeto Dado que la orientación del objeto ha cambiado, la dimensión requerida de BitmapData El elenco ya no puede ser tomado convenientemente de la anchura y altura Propiedades de la instancia gráfica..
2 Orientación de fuente gráfica. La instancia del gráfico en el escenario se rota, pero la de la biblioteca no. Necesitamos tomar una instantánea de la fuente gráfica transformada de la biblioteca.
3 Coordenadas del píxel superior izquierdo (píxel inicial) de BitmapData objeto No podemos alinear el BitmapData El primer píxel del objeto con el punto de registro de la instancia gráfica. Esto sera incorrecto.

Paso 4: La Solución

Para resolver estos problemas, primero definiremos un rectángulo que delimita estrechamente la instancia gráfica girada en el escenario. Disposiciones de ActionScript para esto a través de la getBounds () función. Esto solucionará nuestro primer problema. Observa la imagen de abajo. Observe que hay una diferencia entre el punto de registro de la instancia gráfica y el del rectángulo.

He incluido la siguiente presentación Flash para mostrar el cuadro delimitador rectangular (cuadro rojo) y el cuadro delimitador del espacio local (cuadro negro)


Paso 5: Transformación

A continuación, traeremos una instantánea de la fuente gráfica transforrada a este rectángulo en el escenario. Observa la imagen de abajo..

Comenzamos teniendo el punto de registro de la fuente gráfica alineado con el del área rectangular (diagrama 1). Luego, giramos (diagrama 2) y lo compensamos (diagrama 3) antes de tomar la captura de "rayos X" de la imagen en el BitmapData objeto (diagrama 4).

Podemos hacerlo manualmente, o elegir hacer una copia de las instancias gráficas transform.matrix propiedad. Al utilizar el segundo enfoque, debemos tener cuidado de no utilizar el transform.matrix traducción propiedad: de lo contrario, los puntos de registro no se alinearán como se ve en el diagrama 1. En ambos casos, tendremos que calcular las distancias x e y para compensar.


Paso 6: Implementación de ActionScript

Después de esta larga explicación, espero que sea más fácil entender el código. He resaltado las líneas importantes y he añadido comentarios:

 coco var privado: CTree, hk: gancho; private var bdat1: BitmapData, bdat2: BitmapData; privado var t1: TextField; ángulo de var privado: Número = 45 var de coco privadoBox: Rectángulo; función pública Matrix_Bitmap4 () coconut = new CTree (); addChild (coco); Coconut.rotation = ángulo; coconut.x = stage.stageWidth * 0.3; coconut.y = stage.stageHeight * 0.2; coconutBox = coconut.getBounds (esto); // obtener caja rectangular en el paso 2 var coconut_newX: Number = coconut.x - coconutBox.x // obtener offset x en el paso 3 var coconut_newY: Number = coconut.y - coconutBox.y // obtener offset y en el paso 3 var m : Matriz = Matriz nueva (); m.rotate (angle / 180 * Math.PI); // rotar el gráfico en el paso 3 // var m: Matriz = coconut.transform.matrix // recomendado si ocurrieron muchas transformaciones. //m.tx = 0; m.ty = 0; // Para este caso, está haciendo el mismo trabajo que la línea anterior. m.translate (coconut_newX, coconut_newY); // implementar el desplazamiento bdat1 = new BitmapData (coconutBox.width, coconutBox.width, true, 0x00000000); bdat1.draw (coco, m);

Además, no olvide cambiar la ubicación del primer píxel (arriba a la izquierda) en BitmapData Objeto al de la caja rectangular.

 verificación de función privada (e: Evento): void var closeEnough: Boolean = coconut.hitTestObject (hk) if (closeEnough) // var point1: Point = new Point (coconut.x, coconut.y); // ahora que tenemos un cuadro diferente con una ubicación diferente para el píxel inicial, // deberíamos referirnos a coconutBox como el punto inicial var point1: Point = new Point (coconutBox.x, coconutBox.y); var point2: Point = new Point (hk.x, hk.y); if (bdat1.hitTest (point1, 1, bdat2, point2, 1)) t1.text = "Al menos un píxel ha colisionado" else t1.text = "No collision"

Y he aquí un ejemplo del trabajo..


Paso 7: Actualización constante

Si la forma de interés, en este caso el árbol de coco, se está transformando constantemente (girando, escalando, encogiendo, sesgando, etc.) entonces la BitmapData El objeto debe actualizarse en cada marco y esto requerirá cierto procesamiento. Además, tenga en cuenta que he optado por el enfoque alternativo mencionado en el Paso 4. Aquí está la secuencia de comandos para actualizar la copia de rayos X del gráfico para cada fotograma:

 función privada updateBmp (): void coconutBox = coconut.getBounds (this); // obtener caja rectangular en el paso 2 var coconut_newX: Number = coconut.x - coconutBox.x // obtener offset x en el paso 3 var coconut_newY: Number = coconut.y - coconutBox.y // obtener offset y en el paso 3 // var m: Matriz = Matriz nueva (); //m.rotate(angle / 180 * Math.PI); // rotar el gráfico en el paso 3 var m: Matriz = coconut.transform.matrix // recomendado si ocurrieron muchas transformaciones, m.tx = 0; m.ty = 0; // para este caso, está haciendo el mismo trabajo que la línea anterior. m.translate (coconut_newX, coconut_newY); // implementar el desplazamiento bdat1 = new BitmapData (coconutBox.width, coconutBox.width, true, 0x00000000); b = nuevo Bitmap (bdat1); addChild (b); b.x = stage.stageWidth * 0.3; b.y = stage.stageHeight * 0.2; bdat1.draw (coco, m); 

La siguiente función se realiza en cada fotograma:

 verificación de función privada (e: Evento): void coconut.rotation + = angle; // cambios dinámicos en el tiempo de ejecución coconut.scaleX + = 0.01 var close Enough: Boolean = coconut.hitTestObject (hk) if (closeEnough) updateBmp (); var point1: Point = new Point (coconutBox.x, coconutBox.y); var point2: Point = new Point (hk.x, hk.y); if (bdat1.hitTest (point1, 1, bdat2, point2, 1)) t1.text = "Al menos un píxel ha colisionado" else t1.text = "No collision" bdat1.dispose (); 

Y esta es la salida. Empieza a arrastrar el gancho para ver la animación..


Conclusión

Entiendo que este tutorial puede no ser demasiado rápido para leer, pero es crucial para obtener una comprensión clara de lo que está sucediendo. Espero que esto haya sido útil y si está interesado en manipular más esta matriz 2x2, visite mi artículo sobre el tema. Gracias.