Consejo rápido Detección de colisión entre un círculo y un segmento de línea

Cubrimos la detección de colisiones entre una línea y un círculo infinitos en nuestro Consejo rápido anterior. Sin embargo, el problema que surgió fue que la línea se extiende más allá del segmento de línea visible; de hecho, se extiende en un hiperplano. En este Consejo rápido, limitaremos nuestra detección de colisión a la de una línea segmento solamente.


Vista previa del resultado final

Vamos a trabajar hacia este resultado:

Haga clic en el botón Reiniciar para volver a colocar los círculos en la parte superior del escenario..


Paso 1: Dos enfoques

Existen numerosos enfoques para limitar la detección de colisiones dentro de un segmento de línea. Vamos a ver dos enfoques esta vez. El primer enfoque es un poco más riguroso matemáticamente que el segundo, pero estos son conceptos que, si lo captas con éxito, seguramente te beneficiarán en el futuro. Ambos enfoques manipulan la característica del producto de puntos de ser una medida de cómo son paralelos dos vectores dados.

Echemos un vistazo a la primera aproximación. Supongamos que A y B son vectores. Si A y B son paralelos, o al menos apuntan en la misma dirección, el producto de punto entre A y B producirá un número positivo. Si A y B están apuntando directamente uno frente al otro, o al menos apuntando en direcciones opuestas, el producto de punto entre A y B producirá un número negativo. Si A y B son ortogonales (formando 90 ° entre sí), entonces el producto punto producirá 0.

El siguiente diagrama resume esta descripción..


Paso 2: Relacionar el producto de puntos con las condiciones

Tendremos que formar los vectores B y C desde ambos extremos del segmento de línea para que su producto puntual con el vector del segmento de línea, A, pueda determinar si el círculo está dentro del segmento..

Observe el siguiente diagrama. Si el círculo está dentro del segmento, entonces el valor del producto de punto entre A y B es positivo y entre A y C es negativo.

El diagrama a continuación muestra cómo cambia el producto de puntos dependiendo de si el círculo está más allá o dentro del segmento de línea. Note las diferencias en el valor del producto punto..

También tenga en cuenta que "dentro del segmento de línea" no significa que el círculo esté necesariamente intersectando el segmento de línea, solo que cae dentro de las dos líneas delgadas en el diagrama de arriba..

Entonces, cuando se produce una colisión entre la línea y el círculo, como hemos visto en el Consejo rápido anterior, tenemos que investigar más a fondo si el círculo está posicionado dentro del segmento de línea. Si lo es, entonces estamos seguros de que existe una intersección genuina.


Paso 3: Implementación

El paso 2 explicó el concepto que utilizamos para restringir la detección de colisiones para que esté dentro del segmento de línea. Sin embargo, todavía hay una falla en la precisión. Verás, el área definida está un poco inclinada; Debemos tratar de utilizar el área definida de acuerdo con el diagrama a continuación..

Esto es fácil: simplemente calculamos D como la proyección horizontal de A. Luego, en lugar de utilizar A, usamos D para salpicar el producto con B y C. Todas las condiciones como se explicaron en el Paso 2 siguen vigentes, pero en lugar de un segmento inclinado , hemos definido un área vertical.

Esta corrección se puede apreciar visualmente si el círculo es grande; Si el círculo fuera pequeño, su centro estaría tan cerca de la línea que sería difícil detectar este defecto visual, por lo que podríamos salir con esa área ligeramente inclinada y ahorrarnos algo de poder de procesamiento..

Sin embargo, trataré de hacer las cosas de la manera correcta. Puede elegir su enfoque modificando la condición ligeramente.


Paso 4: Implementación

El primer fragmento de código de ActionScript aquí configura el vector D (v_line_onX)

 // Att2: obteniendo el vector horizontal var line_onX: Number = line.projectionOn (nuevo Vector2D (1, 0)); v_line_onX = nuevo Vector2D (1, 0); v_line_onX.setMagnitude (line_onX);

Nota: Estamos usando clases de mis tutoriales anteriores aquí. Vector2D se introdujo en Gravity in Action, pero no necesita leer eso para usar la clase, se incluye en la descarga de origen.

El segundo fragmento de ActionScript aquí configura B (c1_círculo) y C (c2_círculo) y verifica la colisión y si el círculo está dentro del segmento o no.

 actualización de funciones privadas (e: Evento): void for (var i: int = 0; i < circles.length; i++)  //calculating line's perpendicular distance to ball var c1_circle:Vector2D = new Vector2D(circles[i].x - x1, circles[i].y - y1); var c1_circle_onNormal:Number = c1_circle.projectionOn(leftNormal); //Att2: get vector from c2 to circle var c2_circle:Vector2D = new Vector2D(circles[i].x - x2, circles[i].y - y2); circles[i].y += 2; if ( c1_circle_onNormal <= circles[i].radius && v_line_onX.dotProduct(c1_circle) > 0 && v_line_onX.dotProduct (c2_circle) < 0 ) //if collision happened, undo movement circles[i].y -= 2;   

Paso 5: El resultado

Aquí está el resultado para el primer acercamiento. Haga clic en el botón para restablecer las posiciones de todos los círculos en la parte superior del escenario.


Paso 6: Segundo enfoque

El segundo enfoque es mucho más simple. Voy a tratar de trabajar hacia atrás desde el final esta vez.

Observe el siguiente diagrama. El segmento de línea es de c1 a c2. Está claro que collide1 y collide3 Están ambos fuera del segmento de línea, y eso solo collide2 está dentro del segmento de línea.

Sean v1, v2 y v3 vectores de c1 a los respectivos círculos. Solo v2 y v3 son paralelos, o al menos apuntan en direcciones similares al vector de línea (c1 a c2). Al verificar un valor positivo en el producto de puntos entre el vector de línea y cada uno de esos vectores desde c1 hasta los centros circulares correspondientes (v1, v2, v3), podemos determinar fácilmente que collide1 está más allá del segmento de línea. En otras palabras, c1 v1 .

A continuación, idearemos un método para determinar que collide3 está fuera del segmento de línea. Esto debería ser fácil. Es obvio que la proyección de v3 a lo largo del vector de línea excederá la longitud del segmento de línea. Usaremos esta característica para eliminar las malas hierbas..

Así que permítanme resumir el segundo enfoque:

  • Primero comprobamos una intersección entre la línea infinita y el círculo.
  • Si hay una intersección, investigue más a fondo lo siguiente para determinar si ocurre dentro del segmento de línea:
    • Compruebe que se produce un valor positivo cuando tomamos el producto punto del vector de c1 al círculo y al vector de línea, y
    • Compruebe que la magnitud de la proyección del vector a lo largo del vector de línea sea más corta que la longitud del segmento de línea.

Paso 7: Implementación

Aquí está la implementación de ActionScript de lo anterior:

 actualización de funciones privadas (e: Evento): void for (var i: int = 0; i < circles.length; i++)  //calculating line's perpendicular distance to ball var c1_circle:Vector2D = new Vector2D(circles[i].x - x1, circles[i].y - y1); var c1_circle_onNormal:Number = c1_circle.projectionOn(leftNormal); //Att2: getting the relevant vectors var c1_circle_onLine:Number = c1_circle.projectionOn(line); circles[i].y += 2; if ( Math.abs(c1_circle_onNormal) <= circles[i].radius && line.dotProduct(c1_circle) > 0 && c1_circle_onLine < line.getMagnitude() ) //if collision happened, undo movement circles[i].y -= 2;   

Paso 8: El Resultado

Esencialmente, producirá el mismo resultado que el anterior pero como hay algunas líneas de código más cortas en el segundo enfoque, creo que es mejor.

Conclusión

Espero que esto haya ayudado. Gracias por leer. A continuación, veremos la reacción de colisión..