Implementé una rutina simple de detección de colisiones usando AABB entre mi sprite principal del juego y varias plataformas (consulte el código a continuación). Funciona muy bien, pero ahora estoy introduciendo la gravedad para hacer que mi personaje se caiga y se muestran algunos problemas con mi rutina de CD.
Creo que el quid de la cuestión es que mi rutina de CD mueve el sprite hacia atrás a lo largo del eje en el que ha penetrado la menor cantidad en el otro sprite. Entonces, si está más en el eje Y que en el X, entonces lo moverá hacia atrás a lo largo del eje X.
Sin embargo, ahora mi sprite (héroe) ahora está cayendo a una velocidad de 30 píxeles por fotograma (es una pantalla de 1504 de altura; necesito que caiga tan rápido como quiero intentar simular la gravedad 'normal', cualquier más lento solo se ve raro ) Estoy teniendo estos problemas. Intentaré mostrar lo que está sucediendo (y lo que creo que lo está causando, aunque no estoy seguro) con algunas imágenes: (Codifique las imágenes a continuación).
Agradecería algunas sugerencias sobre cómo solucionar este problema.
Para aclarar, en la imagen de arriba a la derecha, cuando digo que la posición se corrige 'incorrectamente', eso puede ser un poco engañoso. El código en sí está funcionando correctamente para la forma en que está escrito, o dicho de otra manera, el algoritmo en sí mismo si se comporta como lo esperaría, pero necesito cambiar su comportamiento para evitar que este problema suceda, espero que aclare mis comentarios en la imagen .
Mi código
public boolean heroWithPlatforms(){
//Set Hero center for this frame
heroCenterX = hero.xScreen+(hero.quadWidth/2);
heroCenterY = hero.yScreen+(hero.quadHeight/2);
//Iterate through all platforms to check for collisions
for(x=0;x<platformCoords.length;x+=2){
//Set platform Center for this iteration
platformCenterX = platformCoords[x]+(platforms.quadWidth/2);
platformCenterY = platformCoords[x+1]+(platforms.quadHeight/2);
// the Dif variables are the difference (absolute value)
// of the center of the two sprites being compared (one for X axis difference
//and on for the Y axis difference)
difX = Math.abs(heroCenterX-platformCenterX);
difY = Math.abs(heroCenterY-platformCenterY);
//If 1
//Check the difference between the 2 centers and compare it to the vector (the sum of
//the two sprite's widths/2. If it is smaller, then the sprites are pverlapping along
//the X axis and we can now proceed to check the Y axis
if (difX<vecXPlatform){
//If 2
//Same as above but for the Y axis, if this is also true, then we have a collision
if(difY<vecYPlatform){
//we now know sprites are colliding, so we now need to know exactly by how much
//penX will hold the value equal to the amount one sprite (hero, in this case)
//has overlapped with the other sprite (the platform)
penX = vecXPlatform-difX;
penY = vecYPlatform-difY;
//One sprite has penetrated into the other, mostly in the Y axis, so move sprite
//back on the X Axis
if (penX < penY){hero.xScreen-=penX*(heroCenterX-platformCenterX>=0 ? -1 : 1);}
//Sprite has penetrated into the other, mostly in the X asis, so move sprite
//back on the Y Axis
else if (penY < penX) {hero.yScreen-=penY*(heroCenterY-platformCenterY>=0 ? -1 : 1);}
return true;
}//Close 'If' 2
} //Close 'If' 1
}
//Otherwise, no collision
return false;
}
fuente
//One sprite has penetrated into the other, mostly in the Y axis, so move sprite //back on the X Axis
Respuestas:
Durante mis experimentos con HTML5 Canvas y AABB encontré exactamente lo que estás experimentando. Esto sucedió cuando intenté hacer una plataforma desde cajas adyacentes de 32x32 píxeles.
Soluciones que intenté por orden de mi preferencia personal
1 - Divide movimientos por eje
Mi actual, y creo que continuaré mi juego con él. Pero asegúrese de verificar lo que llamo Phantom AABB si necesita una solución rápida sin tener que modificar mucho su diseño actual.
Mi mundo del juego tiene una física muy simple. No existe un concepto de fuerzas (todavía), solo desplazamiento. La gravedad es un desplazamiento constante hacia abajo. Sin aceleración (todavía).
Los desplazamientos se aplican por eje. Y en esto consiste esta primera solución.
Cada cuadro del juego:
move_x se establece de acuerdo con el procesamiento de entrada, si no se presiona la tecla de flecha, entonces move_x = 0. Los valores inferiores a cero significan izquierda, de lo contrario a la derecha.
Procese todos los demás sprites móviles y decida los valores de su componente "movimientos", también tienen el componente "movimientos".
Nota: después de la detección y respuesta de colisión, el componente de movimiento debe establecerse en cero, ambas propiedades x e y, porque al siguiente tic la gravedad se agregará nuevamente. Si no reinicia, terminará con movimientos.y de dos veces la gravedad deseada, y en el siguiente tic será tres veces.
Luego ingrese el código de aplicación de desplazamiento y de detección de colisión.
El componente de movimientos no es realmente la posición actual del sprite. El sprite aún no se ha movido, incluso si se ha movido move.x o y. El componente "movimientos" es una forma de guardar el desplazamiento que se aplicará a la posición del sprite en la parte correcta del bucle del juego.
Procese primero el eje y (mueve.y). Aplica movimientos.y a sprite.position.y. Luego aplique el método AABB para ver si el sprite en movimiento que se está procesando se superpone a una plataforma AABB (ya entendió cómo hacerlo). Si se superpone, mueve el sprite nuevamente en el eje y aplicando penetración. Sí, a diferencia de mi otra respuesta, ahora este método de movimiento evolucionado ignora la penetración.x, para este paso del proceso. Y usamos penetration.y incluso si es mayor que penetration.x, ni siquiera lo estamos comprobando.
Luego procese el eje x (moves.x). Aplica movimientos.x a sprite.position.x. Y haz lo contrario que antes. Esta vez moverás el sprite solo en el eje horizontal aplicando penetración.x. Como antes, no le importa si penetration.x es menor o mayor que penetration.y.
El concepto aquí, es que si el sprite no se mueve, y inicialmente no estaba colisionando, permanecerá así en el siguiente cuadro (sprite.moves.x e y son cero). El caso especial en el que otro objeto del juego se teletransporta mágicamente a una posición que se superpone al comienzo del sprite procesado es algo que manejaré más adelante.
Cuando un sprite comienza a moverse. Si se mueve solo en el eje x, entonces solo nos interesa si penetra algo por la izquierda o la derecha. Como el sprite no se mueve hacia abajo, y sabemos esto porque la propiedad y del componente de movimientos es cero, ni siquiera verificamos el valor calculado para la penetración. Y, solo vemos en penetración.x. Si existe penetración en el eje de movimiento, aplicamos corrección, de lo contrario simplemente dejamos que el sprite se mueva.
¿Qué pasa con el desplazamiento diagonal, no necesariamente en un ángulo de 45 grados?
El sistema propuesto puede manejarlo. Solo necesita procesar cada eje por separado. Durante tu simulación. Se aplican diferentes fuerzas al sprite. Incluso si su motor aún no comprende las fuerzas. Estas fuerzas se traducen en un desplazamiento arbitrario en el plano 2D, este desplazamiento es un vector 2D en cualquier dirección y de cualquier longitud. De este vector puede extraer el eje y y el eje x.
¿Qué hacer si el vector de desplazamiento es demasiado grande?
No quieres esto:
A medida que nuestros nuevos movimientos que aplican la lógica procesan primero un eje y luego el otro, el código de colisión puede terminar viendo un gran desplazamiento como una gran L, esto no detectará correctamente las colisiones con objetos que se cruzan con su vector de desplazamiento.
La solución es dividir grandes desplazamientos en pequeños pasos, idealmente el ancho o la altura del sprite, o la mitad del ancho o la altura del sprite. Para obtener algo como esto:
¿Qué hay de teletransportarse sprites?
Si representa la teletransportación como un desplazamiento grande, utilizando el mismo componente de movimientos que un movimiento normal, entonces no hay problema. Si no lo hace, debe pensar en una forma de marcar el sprite de teletransportación para que sea procesado por el código de colisión (¿agregarlo a una lista dedicada a este propósito?), Durante el código de respuesta puede desplazar directamente el sprite permanente que estaba en la forma en que ocurrió la teletransportación.
2 - Phantom AABB
Tenga un AABB secundario para cada sprite afectado por la gravedad que comienza en la parte inferior del sprite, tiene el mismo ancho que el AABB del sprite y una altura de 1 píxel.
La idea aquí es que este AABB fantasma siempre está colisionando con la plataforma debajo del sprite. Solo cuando este AABB fantasma no se superpone, se permite aplicar gravedad al sprite.
No necesita calcular el vector de penetración para el AABB fantasma y la plataforma. Solo le interesa una prueba de colisión simple, si se superpone a una plataforma, no se puede aplicar la gravedad. Su sprite puede tener una propiedad booleana que le dice si está sobre una plataforma o en el aire. Estás en el aire cuando tu AABB fantasma no está colisionando con una plataforma debajo del jugador.
Esta:
Se convierte en algo como esto:
Muy simple y confiable.
Dejas tu implementación de AABB como está.
El AABB fantasma puede tener un bucle dedicado que los procese, que solo compruebe las colisiones simples y no pierda el tiempo calculando el vector de penetración. Este bucle debe ser anterior al código de colisión y respuesta normal. Puede aplicar gravedad durante este ciclo, ya que los sprites en el aire aplican gravedad. Si el AABB fantasma es en sí mismo una clase o estructura, puede tener una referencia al sprite al que pertenece.
Esto es muy similar a la otra solución propuesta donde verificas si tu jugador está saltando. Doy una posible forma de detectar si el jugador tiene los pies sobre una plataforma.
fuente
Combinaría los mosaicos de la plataforma en una sola entidad de plataforma.
Por ejemplo, supongamos que toma las 3 fichas de las imágenes y las combina en una sola entidad, su entidad tendría un cuadro de colisión de:
Usar eso para colisión te dará la y como el eje menos penetrante en la mayor parte de la plataforma y al mismo tiempo te permitirá tener fichas específicas dentro de la plataforma.
fuente
Problemas como estos son comunes con los nuevos métodos de detección de colisiones.
No sé demasiado acerca de su configuración de colisión actual, pero así es como debería ir:
En primer lugar, haga estas variables:
En segundo lugar, haga un temporizador para calcular el delta que afecte la velocidad del objeto.
Tercero, haz esto:
Si haces esto, no debería haber ningún problema. ¡Espero que esto ayude!
fuente