Estoy desarrollando un juego con un terreno similar a Minecraft hecho de bloques. Dado que la renderización básica y la carga de fragmentos se realizan ahora, quiero implementar la selección de bloques.
Por lo tanto, necesito averiguar a qué bloque se enfrenta la cámara en primera persona. Ya escuché que no se proyectó toda la escena, pero decidí no hacerlo porque suena raro y no es exacto. Quizás de alguna manera podría lanzar un rayo en la dirección de la vista, pero no sé cómo verificar la colisión con un bloque en mis datos de vóxel. Por supuesto, estos cálculos deben hacerse en la CPU ya que necesito los resultados para realizar operaciones de lógica de juego.
Entonces, ¿cómo podría averiguar qué bloque está delante de la cámara? Si es preferible, ¿cómo podría lanzar un rayo y verificar las colisiones?
fuente
Respuestas:
Cuando tuve este problema mientras trabajaba en mis Cubos , encontré el artículo "Un algoritmo de desplazamiento rápido de vóxel para el trazado de rayos" de John Amanatides y Andrew Woo, 1987, que describe un algoritmo que se puede aplicar a esta tarea; es precisa y solo necesita una iteración de bucle por vóxel intersectado.
He escrito una implementación de las partes relevantes del algoritmo del artículo en JavaScript. Mi implementación agrega dos características: permite especificar un límite en la distancia de la emisión de rayos (útil para evitar problemas de rendimiento, así como definir un 'alcance' limitado), y también calcula qué cara de cada vóxel ingresó el rayo.
El
origin
vector de entrada se debe escalar de modo que la longitud lateral de un vóxel sea 1. La longitud deldirection
vector no es significativa, pero puede afectar la precisión numérica del algoritmo.El algoritmo funciona mediante el uso de una representación parametrizada del rayo,
origin + t * direction
. Para cada eje de coordenadas, hacemos un seguimiento de lat
valor que tendríamos si tomamos un paso suficiente para cruzar un límite voxel largo de ese eje (es decir, cambiar la parte entera de la coordenada) en las variablestMaxX
,tMaxY
ytMaxZ
. Luego, damos un paso (usando las variablesstep
ytDelta
) a lo largo del eje que tenga menostMax
, es decir, el límite de vóxel más cercano.Enlace permanente a esta versión de la fuente en GitHub .
fuente
function intbounds(s,ds) { return (ds > 0? Math.ceil(s)-s: s-Math.floor(s)) / Math.abs(ds); }
. ComoInfinity
es mayor que todos los números, tampoco creo que deba protegerse de que ds sea 0 allí.1/ds
causando que se incremente uno de los otros ejes. La solución es escribirintfloor
para verificar si ambosds
son negativos y sis
es un valor entero (mod devuelve 0), y devuelve 0.0 en ese caso.Tal vez busque en el algoritmo de línea de Bresenham , especialmente si está trabajando con bloques de unidades (como suelen hacer la mayoría de los juegos de minecraft).
Básicamente, esto toma dos puntos, y traza una línea continua entre ellos. Si lanzas un vector desde el jugador a su distancia máxima de selección, puedes usar esto, y los jugadores se posicionan como puntos.
Tengo una implementación 3D en python aquí: bresenham3d.py .
fuente
Para encontrar el primer bloque frente a la cámara, cree un bucle for que recorra de 0 a cierta distancia máxima. Luego, multiplique el vector de avance de la cámara por el contador y verifique si el bloque en esa posición es sólido. Si es así, almacene la posición del bloque para su uso posterior y detenga el bucle.
Si también desea poder colocar bloques, la selección de caras no es más difícil. Simplemente retroceda desde el bloque y encuentre el primer bloque vacío.
fuente
Hice una publicación en Reddit con mi implementación , que utiliza el Algoritmo de línea de Bresenham. Aquí hay un ejemplo de cómo lo usarías:
Aquí está la implementación en sí:
fuente