Sombras realistas para roguelike

imagen

Buen tiempo, comunidad Habr.

Hace muchos años, me encontré con una publicación (1) . Luego me sorprendió la oportunidad de crear elementos interesantes para el juego en roguelike (2) . Supongamos que el enemigo puede estar detrás de la pared, no lo vemos hasta que lo encontramos en la línea de visión. Pero prefiero la situación cuando nosotros, viajando a lo largo de los corredores de la mazmorra, revelamos las características de la ubicación de los objetos gradualmente en función del alcance.

Más adelante en las publicaciones: (3) , (4) y (5) , se consideraron las cuestiones de la proyección de sombras en juegos 2D. Como señalaron los propios autores, y en los comentarios, que calcular las sombras es bastante voluminoso y no es una tarea fácil, tanto para la calculadora como para el diseño.

De alguna manera, tuve algunos días libres y decidí volver a la cuestión de las sombras más prometedoras. Está claro que la tarjeta de video hace frente a las sombras con éxito y rápidamente, pero en este caso, quería procesar las sombras para un juego en 2D, y parecía superfluo transferir cálculos a la tarjeta de video. Sí, y la potencia del procesador en los últimos años en su conjunto ha crecido, en realidad una publicación sobre lo que sucedió al final.

El programa fue escrito en Pascal, simplemente porque lo conozco bien, y Lazarus es un IDE abierto con una amplia gama de componentes.

La idea original era dibujar líneas, desde el observador a través de cada una de las esquinas del mosaico, y luego oscurecer la figura resultante.

imagen

Sin embargo, tal sombra parece poco natural cuando cambia el ángulo de visión. Las sombras ahora son más anchas, ahora más estrechas.

imagen

La sombra de un objeto redondo se ve mucho mejor. Para construir una sombra de este tipo, debe dibujar dos tangentes desde el punto de observación hasta el círculo y los bordes de la pantalla. El diámetro del círculo corresponderá al tamaño de la baldosa.

En mi programa, utilicé la siguiente función:

//       function tangent_to_circle(x1,y1,x2,y2,r:Single; var x3,y3,x4,y4:Single):Boolean; var l,dx,dy,i,ii,ij:Single; begin dx := x1 - x2; dy := y1 - y2; l := Sqrt(dx*dx + dy*dy); if l<=r then begin tangent_to_circle:=false; exit; end; i := r/l; ii := i*i; ij := i * Sqrt(1 - ii); x3 := x2 + dx*ii - dy*ij; y3 := y2 + dx*ij + dy*ii; x4 := x2 + dx*ii + dy*ij; y4 := y2 - dx*ij + dy*ii; tangent_to_circle:=true; end; 

Donde (x1, y1) es el punto de observación, (x2, y2) es el centro del círculo, ® es su radio, y (x3, y3) y (x4, y4) son los puntos de intersección de las líneas y el círculo. La función devuelve verdadero solo cuando el observador está fuera del círculo.

Como el procesador no es muy amigable con la trigonometría, intenté usarlo al mínimo. En realidad, basándose en una regla simple (modelo aproximado), los expertos le dirán por qué.

(Malo) SIN, COS ..> '/', SQRT> 'DIV', 'MOD'> 'SHR', 'SHL'> '*'> ': =', '+', '-', 'Y ',' XOR '.. (Bien)

Es un placer implementar la parte gráfica de las primitivas en el lienzo, hay muchas bibliotecas y motores que facilitan el trabajo. Al desarrollar en Delphi, tuve que usar la biblioteca Agg2D, en Lazarus está su puerto (6) , y en él decidí darme cuenta de la idea. En realidad, la ganancia de la biblioteca es que se agrega un canal alfa a los colores RGB, y las primitivas se suavizan, y debido al acceso directo a píxeles y varios trucos, el procesamiento es mucho más rápido que el lienzo.

Al dibujar la sombra del mosaico, originalmente iba a llenar el sector con sombra, pero luego la imagen dentro del mosaico era poco distinguible (el sector en cuestión en la Figura 3. está lleno de verde). Después de experimentar con varias opciones, me detuve al seleccionar un sector del área de sombra.

Para dibujar el sector, necesitamos un ángulo en radianes, pero la trigonometría aún no puede funcionar. (arctan2 - función de biblioteca del módulo matemático)

 //     function alpha_angle(x1,y1,x2,y2:Single):Single; begin alpha_angle := arctan2(y1 - y2, x1 - x2); end; 

En realidad, todo está listo para el ensamblaje de imágenes. Tomamos un mapa de mosaicos y en una capa separada aplicamos sombras secuencialmente, una por una. Para los árboles, las sombras son más oscuras, para otros objetos, las sombras son más transparentes.

imagen

La imagen terminada se aplica sobre la capa principal de mosaicos. Un poco de diseño de fondo y recoger mosaicos más genéricos. En realidad, me llevó dos días buscar conjuntos de fichas adecuados, aquellos que son de dominio público o de muy baja calidad o cuestan dinero. Como resultado, dibujó los árboles él mismo y tomó prestados otros elementos del usuario Joe Williamson (7) (estilo excelente).

imagen

Eso habría terminado, pero había algo de sedimento sobre el rendimiento. Con el número de objetos, comienza la reducción de quinientos. Se consideraron varios métodos de optimización, y al dividir en núcleos y restringir el área de dibujo a un cierto radio, cambiando la forma de la sombra (a un costo menor que los arcos), incluso pensé en transferir el cálculo al video.

Como resultado, llegué a la conclusión de que la mejor opción sería reducir el muestreo de la imagen que sirve como máscara de sombra. Dado que el número de cálculos se reduce significativamente, así como el efecto inesperado de la pixelación de los contornos de sombra, lo que le da un cierto encanto de la vieja escuela.

imagen

Me gustó tanto el efecto que tuve que hacer que el escalado sea un proceso dinámico, a través de un parámetro de multiplicidad dado.

imagen

Todo lo que quedaba era crear paredes opacas y presentar el resultado a la comunidad.

imagen

Espero nuevos juegos con este efecto o su desarrollo.
Gracias

Demostración donde puede tocar los controladores (exe para Windows).

Parte 2 , Parte 3

Referencias
1) habr.com/post/16927/
2) en.wikipedia.org/wiki/Roguelike
3) habr.com/post/204782/
4) habr.com/post/305252/
5) habr.com/post/319530/
6) wiki.freepascal.org/BGRABitmap
7) twitter.com/joecreates

Source: https://habr.com/ru/post/es430438/


All Articles