Detección de colisiones a nivel de píxeles

Hasta ahora, nuestros métodos de detección de colisiones se han basado matemáticamente. Aunque esto es útil, hay casos en los que el enfoque matemático simplemente no vale la pena, como en el caso de una forma orgánica e irregular: los cálculos necesarios son demasiado complejos y caros para justificarlos. En su lugar, podemos comprobar cada píxel individual de las formas. Este también es un enfoque costoso, pero al menos puede optimizarse.


Detección de colisiones

Esta es la pieza final que trataremos de crear. Arrastre el gancho sobre el árbol de coco y observe lo que dice el texto en la parte inferior.


Paso 1: Uno por uno

Supongamos que tenemos dos mapas de bits y nos gustaría comprobar si chocan, píxel por píxel: ¿qué significa? Bueno, supongamos que ambos mapas de bits son 3x3px, y todos los píxeles están rellenos.

Estaremos haciendo literalmente esto:

  1. Compruebe si a1 y b1 comparten la misma ubicación.
  2. Repita el paso (1) pero ahora para a1 y b2.
  3. Repita lo mismo entre a1 y b3, b4 ... b9.
  4. Repita los pasos (1) a (3) para a2, a3 ... a9.

Hay algunas observaciones que me gustaría señalar.

Observación Descripción
Pixeles superiores izquierda Los píxeles de la parte superior izquierda para ambos mapas de bits se utilizan como el píxel de inicio para las comprobaciones. Por ejemplo, a1 es el píxel inicial comparado con todos los píxeles en b, que comienza con b1. Ambos píxeles superiores izquierda.
Progresión de la línea de exploración Como se mencionó en el punto anterior, la verificación se realiza en el orden de a1, a2, a3 ... a9. Tenga en cuenta la forma en que estos píxeles están dispuestos.
Espacio de coordenadas comunes Supongamos que ambos gráficos se agregan a la lista de visualización del escenario. La ubicación de cada píxel en ambos mapas de bits., en el espacio de coordenadas del escenario., se comparará para ver si se produce alguna superposición.
Cálculo caro Para dos mapas de bits de 3x3, se requiere un máximo de 9x9 repeticiones. Si mi tamaño de mapa de bits llega a 100x100, puede ver qué tan rápido crece el cálculo total. Sin embargo, si una comprobación devuelve un resultado positivo, el resto de las comprobaciones puede abortarse, ya que cuando un píxel se superpone en ambos mapas de bits, podemos decir que se produce una colisión entre los mapas de bits.

Paso 2: Consideraciones adicionales

Ahora, el Paso 1 se puede tomar literalmente si todos los píxeles están llenos. Con gráficos de mapa de bits, definimos un área de dimensión rectangular. Pero no todos los píxeles están rellenos para formar el gráfico..

El siguiente ejemplo muestra el mapa de bits correcto que ocupa solo b2, b4, b5, b6 y b8. En este caso, deberíamos verificar cada píxel en el mapa de bits izquierdo (a1, a2, a3… a9) contra solo los píxeles b2, b4, b5, b6, b8 en el mapa de bits derecho.

Ahora ActionScript nos proporciona otro parámetro., alfa, que define la transparencia del píxel, siendo 0 completamente transparente y 1 completamente opaco. Para b2, b4, b5, b6, b8, podemos definir un valor de umbral para alfa, decir 0.5.

Entonces, asumamos que b2 y b8 son ambos píxeles con alfa 0.1; Debido a que son menores que el valor de umbral de 0.5, no los consideraremos como píxeles llenos, y por lo tanto no los verificamos. Así que al final, cada píxel en el mapa de bits izquierdo (a1, a2, a3 ... a9) se verifica contra b4, b5, b6 en el mapa de bits derecho solamente.


Paso 3: Implementación de ActionScript

En ActionScript, podemos superponer gráficos vectoriales en BitmapData instancias. Puedes imaginarte ActionScript tomando una radiografía de un gráfico vectorial y transfiriéndolo a un BitmapData, Que actúa como la película fotográfica..

(Sugerencia: si está dibujando en Flash IDE y luego exportando a FlashDevelop como lo estoy haciendo, asegúrese de que las dimensiones de la BitmapData son lo suficientemente grandes para contener el dibujo.)

aquí, CTree y Gancho Son dos símbolos MovieClip, dibujados en Flash; los "radiografiamos" para obtener una instancia de BitmapData para cada uno:

 coco var privado: CTree, hk: gancho; private var bdat1: BitmapData, bdat2: BitmapData; privado var t1: TextField; función pública Matrix_Bitmap () coconut = new CTree (); addChild (coco); coconut.x = stage.stageWidth * 0.3; coconut.y = stage.stageHeight * 0.2; bdat1 = new BitmapData (150, 150, true, 0x00000000); bdat1.draw (coco); hk = nuevo Hook (); addChild (hk); bdat2 = new BitmapData (100, 50, true, 0x00000000); bdat2.draw (hk); hk.addEventListener (MouseEvent.MOUSE_DOWN, iniciar); hk.addEventListener (MouseEvent.MOUSE_UP, end); t1 = nuevo TextField (); addChild (t1); t1.x = stage.stageWidth * 0.2; t1.y = stage.stageHeight * 0.8; t1.width = 300; t1 altura = 100; stage.addEventListener (Event.ENTER_FRAME, check); 

Así que después de eso, comenzaremos las verificaciones usando el hitTest () método de la BitmapData clase.

En cada fotograma que pase, actualizaremos la ubicación del píxel superior izquierdo para cada mapa de bits antes de colocar instancias de BitmapData a través de estos rigurosos hitTest () cheques Tenga en cuenta también que el rango para alfa La entrada aquí es 0 ~ 255, es decir, no hay umbral. Más sobre la transparencia en el siguiente paso..

 verificación de función privada (e: Evento): void var point1: Point = new Point (coconut.x, coconut.y); // píxel superior izquierdo del árbol var point2: Point = new Point (hk.x, hk.y); // píxel superior izquierdo de gancho si (bdat1.hitTest (point1, 255, bdat2, point2, 255)) // verifica si alguno de los píxeles rellenos se superpone t1.text = "Al menos un píxel ha colisionado" else t1 .text = "Sin colisión"

Aquí hay un ejemplo de la salida de ActionScript anterior. Haga clic en el gancho y acérquelo al árbol de coco y verifique la respuesta en el cuadro de texto. Juega con esto llevando el extremo del gancho cerca del borde de las hojas del árbol de coco, para ver si esta colisión es de una precisión de nivel de píxel.


Paso 4: Nivel de transparencia

Si tiene una imagen que, por ejemplo, desaparece gradualmente (se vuelve transparente), puede indicar a ActionScript en qué nivel de transparencia considera que se ajusta un píxel para realizar las comprobaciones de colisión..

Tome el ejemplo a continuación: hay varios niveles de transparencia en el sprite y, como puede ver, se baja gradualmente a la derecha. Si establecemos el nivel de transparencia en 0,5, cualquier píxel con un alfa de 0,5 ~ 1 se considerará opaco y apto para la detección de colisiones. Aquellos menores de 0.5 serán considerados transparentes. Incluso cuando estos píxeles chocan con los de otro objeto, no registrarán una verdadera colisión..

Otro detalle que acabo de mencionar es que ActionScript BitmapDataLa función hitTest de alfa el valor del parámetro realmente varía de 0 a 255. Entonces, lo que hago es simplemente multiplicar mi valor de umbral por 255 para convertir el rango.

 verificación de función privada (e: Evento): void var point1: Point = new Point (bar1.x, bar1.y); var point2: Point = new Point (bar2.x, bar2.y); umbral de var: Número = 255 * 0.5 si (bdat1.hitTest (punto1, umbral, bdat2, punto2, umbral)) t1.text = "Al menos un píxel ha colisionado" else t1.text = "Sin colisión" 

Paso 5: Optimización

He mencionado que la detección de colisiones a nivel de píxel es computacionalmente costosa. Esto significa que solo debemos optar por él cuando sea estrictamente necesario. Si dos objetos están muy alejados, entonces no hay razón para utilizar este enfoque, y una detección de colisión en el cuadro delimitador normal (hitTestObject ()) hará.

Aquí está la idea:

  1. Utilizar hitTestObject () para ver si los cuadros delimitadores de dos objetos han colisionado.
  2. Si la respuesta es sí, entonces estos dos objetos están bastante cerca. Continuar con la comprobación de nivel de píxel.
  3. Si la respuesta es no, entonces estos dos objetos están muy separados. Terminar las verificaciones de colisión sin verificación de nivel de píxel.
 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); var point2: Point = new Point (hk.x, hk.y); if (bdat1.hitTest (point1, 255, bdat2, point2, 255)) t1.text = "Al menos un píxel ha colisionado" else t1.text = "No collision"

Para una referencia completa de ActionScript, echa un vistazo a Matriz_Bitmap3.as desde la fuente de descarga.


Conclusión

Gracias por la lectura. En el siguiente Consejo rápido, usaremos matrices para transformar BitmapData.