En este tutorial, seguiré el enfoque sugerido por Richard Davey (¡Gracias, Richard!), Y utilizado por él y otros, para detectar colisiones entre mapas de bits con una modificación sutil. También compararé el rendimiento entre varios enfoques de detección de colisiones de mapa de bits utilizando el arnés PerformanceTest de Grant Skinner.
Nota: Además de formar parte de la sesión de lanzamiento de disparos, este artículo también forma parte de la Detección y reacción de colisiones..
Describo este enfoque alternativo en breve aquí.
En primer lugar, verificamos si los cuadros delimitadores de los dos mapas de bits se superponen utilizando Rectángulo. Los guiones son los siguientes. Primero, las variables..
private var enemy1: Bitmap, myShip: Bitmap; privado var myShipSp: Sprite; private var rec_e: Rectangle, rec_m: Rectangle; privado var intersec: Rectangle;
enemy1 = nuevo E1 como Bitmap; addChild (enemigo1); myShip = new My as Bitmap; myShipSp = nuevo Sprite; addChild (myShipSp); myShipSp.addChild (myShip); enemy1.x = stage.stageWidth >> 1; myShipSp.x = stage.stageWidth >> 1; enemy1.y = stage.stageHeight * 0.2; myShipSp.y = stage.stageHeight * 0.8; // dibujando cajas alrededor del dibujo del sprite (enemy1.getBounds (stage), this, 0); draw (myShipSp.getBounds (stage), this, 0);
Aquí comprobamos cualquier área de superposición entre las casillas. Revisa DetectVisible.as
en la fuente de descarga para el script completo
actualización de funciones privadas (e: Evento): void // determinar el cuadro delimitador del área de intersección rec_e = enemy1.getBounds (etapa); rec_m = myShipSp.getBounds (etapa); intersec = rec_e.intersection (rec_m); // vuelve a dibujar el cuadro delimitador de ambos sprites this.graphics.clear (); draw (enemy1.getBounds (stage), this, 0); draw (myShipSp.getBounds (stage), this, 0); // solo dibuje el cuadro delimitador del área de intersección si hay uno si (! intersec.isEmpty ()) lines.graphics.clear (); dibujar (intersec, lineas); t.text = "Área de intersección con un rectángulo rojo". else t.text = "Sin área de intersección".
Aquí hay una demo. Arrastra la nave espacial más pequeña alrededor.
(No se preocupe por el cuadro rojo que se "deja atrás" cuando el barco se arrastra fuera del cuadro delimitador del otro.)
Entonces, si hay un área de recuadro en intersección, procedemos a verificar si hay píxeles superpuestos en el área. Sin embargo, primero intentemos dibujar un mapa de bits en esta área de intersección. El guion completo esta en DetectVisible2.as
actualización de funciones privadas (e: Evento): void // determinar el cuadro delimitador del área de intersección rec_e = enemy1.getBounds (etapa); rec_m = myShipSp.getBounds (etapa); intersec = rec_e.intersection (rec_m); // vuelve a dibujar el cuadro delimitador de ambos sprites this.graphics.clear (); draw (enemy1.getBounds (stage), this, 0); draw (myShipSp.getBounds (stage), this, 0); // solo dibuje el cuadro delimitador del área de intersección si hay uno si (! intersec.isEmpty ()) lines.graphics.clear (); dibujar (intersec, lineas); // para dibujar el área de intersección y verificar la superposición del área coloreada var eM: Matrix = enemy1.transform.matrix; var myM: Matrix = myShipSp.transform.matrix; bdt_intersec = nuevo BitmapData (intersec.width, intersec.height, false, 0) eM.tx - = intersec.x; eM.ty - = intersec.y myM.tx - = intersec.x; myM.ty - = intersec.y bdt_intersec.draw (enemy1, eM); bdt_intersec.draw (myShip, myM); bm_intersec.bitmapData = bdt_intersec; bm_intersec.x = 10 bm_intersec.y = stage.stageHeight * 0.8 - bm_intersec.height; t.text = "Área de intersección con un rectángulo rojo. \ n" else t.text = "Sin área de intersección".
Tenga en cuenta que dado que estamos dibujando el área mediante el uso de una matriz, se tendrá en cuenta cualquier escalado, sesgo y otras transformaciones en ambos mapas de bits. Aquí hay una demo; Echa un vistazo a la casilla en la esquina inferior izquierda.
Entonces, ¿cómo comprobamos el píxel correcto? Bueno, en primer lugar, le damos al color de este cuadro de intersección un tono negro (Rojo = 0, Verde = 0, Azul = 0). Luego, la sombra de la nave espacial más pequeña se pintará en esta caja oscura como verde, con el modo de mezcla de AÑADIR. Del mismo modo, la sombra de la nave espacial extraterrestre estacionaria más grande se pintará de rojo..
Así que ahora, habrá áreas de rojo y verde para las naves espaciales, y negras si no hay un área superpuesta. Sin embargo, si hay píxeles de estos dos mapas de bits que se superponen, estos se dibujarán en amarillo (Rojo = 255, Verde = 255, Azul = 0). Usamos el metodo Bitmapdata.getColorBoundsRect
para comprobar la existencia de esta área.
Aquí está el fragmento de Main.as
// para dibujar el área de intersección y verificar la superposición del área coloreada var eM: Matrix = enemy1.transform.matrix; var myM: Matrix = myShipSp.transform.matrix; bdt_intersec = nuevo BitmapData (intersec.width, intersec.height, false, 0) eM.tx - = intersec.x; eM.ty - = intersec.y myM.tx - = intersec.x; myM.ty - = intersec.y // tweak color bdt_intersec.draw (enemy1, eM, nuevo ColorTransform (1,1,1,1,255, -255, -255), BlendMode.ADD); bdt_intersec.draw (myShip, myM, nuevo ColorTransform (1,1,1,1, -255,255, -255), BlendMode.ADD); bm_intersec.bitmapData = bdt_intersec; bm_intersec.x = 10 bm_intersec.y = stage.stageHeight * 0.8 - bm_intersec.height; t.text = "Área de intersección con un rectángulo rojo. \ n" // compruebe la existencia del color correcto intersec_color = bdt_intersec.getColorBoundsRect (0xffffff, 0xffff00); if (! intersec_color.isEmpty ()) t.appendText ("Y hay píxeles interesantes en el área.");
Tenga en cuenta que suprimimos los componentes Rojo y Azul en la línea 113 para maximizar el Verde para la pequeña nave espacial. En la línea 112 hacemos lo mismo con la nave espacial alienígena con los componentes Azul y Verde.
Entonces, después de recibir comentarios sobre problemas de rendimiento relacionados con la detección de colisiones, decidí hacer algunas pruebas rápidas y sucias en estos enfoques. Creé 20 naves espaciales enemigas y una nave espacial de un jugador y verifiqué la detección de colisiones entre esa nave de jugador contra las otras 20. Estos sprites están empaquetados en la misma zona para forzar la detección de colisiones para que todos los enfoques tengan una carrera completa.
El primer acercamiento es el más simple.. BitmapData
se captura en el inicio y para cada trama, la detección de colisiones se comprueba con BitmapData.hitTest ()
. Para el segundo enfoque., BitmapData
se actualiza cada cuadro y la detección de colisiones se realiza en base a esos BitmapData
capturado El tercero se refiere al enfoque sugerido por este tutorial..
Así que el resultado para una de las pruebas que he hecho es el siguiente.
- bitmapdata corregido (1000 iteraciones) Versión del jugador: WIN 11,1,102,55 (debug) - método ... ttl ms ... avg ms bitmapdata arreglado 168 0,17 - - bitmapdata updates (1000 iteraciones) Versión del jugador: WIN 11,1,102,55 (depuración) - método ... ttl ms ... avg ms bitmapdata actualizaciones 5003 5.00 - - método personalizado (1000 iteraciones) Versión del reproductor: WIN 11,1,102,55 (depuración) - método ... ttl ms ... avg ms método personalizado 4408 4.41 -
los Prueba de rendimiento
Da diferentes resultados cada vez que ejecuto la prueba. Así que lo corrí un par de veces y obtuve un tiempo promedio. Conclusión: el método más rápido es el primero, seguido del tercero y luego el segundo enfoque.
Así que guardando BitmapData
para mapas de bits cuando se introducen por primera vez en el escenario y se comprueba hitTest
cada fotograma después es realmente eficiente, siempre que estos sprites no realicen ninguna otra transformación que no sea la traducción (como rotación, sesgo y escala) a través del tiempo. De lo contrario, se verá obligado a adoptar el segundo o el tercer enfoque, y el tercero es más eficiente como lo indica la imagen de arriba..
Puedes echar un vistazo Colisiones.as
y Resultados.as
para el guión completo.
Luego me embarcé para buscar las líneas específicas de código que requerían más tiempo de cómputo. El segundo y tercer enfoque tomaron más tiempo, por lo que derivé varias funciones de ellos, cada una de ellas en puntos diferentes. Echa un vistazo a uno de los resultados a continuación.
- hitTest predeterminado (1000 iteraciones) Versión del jugador: WIN 11,1,102,55 (depuración) incluye límites - método ... ttl ms ... avg ms predeterminado hitTest 189 0.19 - - hitTest predeterminado (1000 iteraciones) Versión del jugador: WIN 11,1,102,55 ( debug) include transform - method ... ttl ms ... avg ms default hitTest 357 0.36 - - default hitTest (1000 iteraciones) Versión del jugador: WIN 11,1,102,55 (debug) include hittest - method ... ttl ms ... avg ms default hitTest 4427 4.43 - - método personalizado (1000 iteraciones) Versión del jugador: WIN 11,1,102,55 (depuración) incluir límites y transformar - método ... ttl ms ... avg ms método personalizado 411 0,41 - - método personalizado (1000 iteraciones) Versión del jugador: WIN 11, 1,102,55 (depuración) incluye sorteos y límites - método ... ttl ms ... método ms personalizado promedio 3320 3.32 -
La primera, segunda y tercera veces se refieren a la segunda aproximación en diferentes puntos de interrupción, y la cuarta y quinta se refieren a la tercera aproximación. Mirando los resultados por tercera y quinta vez., BitmapData.draw
Parece llevar mucho tiempo de computación. Y el tiempo que se tarda en dibujar con el segundo enfoque parece ser más costoso en tiempo de cálculo, lo que me lleva a pensar que los tamaños para BitmapData.draw
Operar en sí importa. Puedes echar un vistazo Colisiones2.as
y Resultados2.as
para los guiones completos.
Una cosa que me parece un poco perturbadora es la inconsistencia de estas pruebas: no siempre obtengo los mismos resultados de tiempo, aunque casi siempre siguen la misma clasificación en todo momento. Por lo tanto, es lo suficientemente bueno como para hacer una comparación simple entre las funciones.
Bueno, gracias por su tiempo mirando leyendo este pequeño consejo. Espero que haya sido útil. Deje comentarios si no está de acuerdo con nada en este tutorial. Me encantaría responder a los comentarios!