Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Programming
Gamedevelopment

Programar un generador de secuencia en un Paisaje Estelar

by
Difficulty:IntermediateLength:LongLanguages:

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

En mi artículo anterior, expliqué la diferencia entre un generador de números pseudoaleatorios y un generador de secuencias, y examiné las ventajas que tiene un generador de secuencias sobre un PRNG. En este tutorial codificaremos un generador de secuencias bastante simple. Genera una serie de números, manipula e interpreta esta secuencia, y luego la usa para dibujar un paisaje estelar muy simple.

Nota: Aunque este tutorial está escrito en Java, debería poder usar las mismas técnicas y conceptos en casi cualquier entorno de desarrollo de juegos.


Creando e inicializando la imagen

Lo primero que tenemos que hacer es crear la imagen. Para este generador de secuencias, vamos a crear una imagen de 1000 × 1000px para que la generación de números sea lo más simple posible. Diferentes idiomas hacen esto de manera diferente, así que use el código necesario para su plataforma de desarrollo.

Cuando haya creado la imagen con éxito, es hora de darle un color de fondo. Ya que estamos hablando de un cielo estrellado, sería más sensato comenzar con un fondo negro (#000000) y luego agregar las estrellas blancas, en lugar de al revés.


Haciendo un perfil de estrella y un campo de estrellas

Antes de comenzar a trabajar en el generador de secuencias, debes averiguar hacia dónde quieres dirigirte. Esto significa saber lo que quieres crear y cómo las diferentes semillas y números varían lo que quieres crear, en este caso las estrellas.

Para hacer esto, necesitamos crear un perfil de estrella de muestra que contendrá variables de clase que indiquen algunas de las propiedades de las estrellas. Para mantener las cosas simples, vamos a comenzar con solo tres atributos:

  • coordenada x
  • coordenada y
  • tamaño

Cada uno de los tres atributos tendrá valores que van de 0 a 999, lo que significa que cada atributo tendrá tres dígitos asignados. Todo esto será almacenado en una clase Star.

Dos métodos importantes en la clase Star son getSize() y getRadiusPx(). El método getSize() devuelve el tamaño de la estrella, reducido a un número decimal entre cero y uno, y el método getRadiusPx() devuelve qué tan grande debe ser el radio de la estrella en la imagen final.

Descubrí que 4 píxeles constituyen un buen radio máximo en mi demostración, por lo que getRadiusPx() simplemente devolverá el valor getSize() multiplicado por cuatro. Por ejemplo, si el método getSize() devuelve un radio de 0.4, el método getRadiusPx() dará un radio de 1.6px.

También deberíamos hacer una clase muy simple cuyo trabajo es hacer un seguimiento de todas las estrellas en cada secuencia de estrellas. La clase Starfield solo consiste en métodos que agregan, eliminan o recuperan estrellas de un ArrayList. También debería poder devolver el ArrayList.


Planificación del generador de secuencias

Ahora que hemos terminado el perfil de estrella e inicializado la imagen, sabemos algunos puntos importantes sobre el generador de secuencias que queremos crear.

En primer lugar, sabemos que el ancho y el alto de la imagen es 1000px. Esto significa que, para explotar los recursos disponibles, el rango de coordenadas x e y debe caer en el rango 0-999. Dado que dos de los números requeridos se encuentran en el mismo rango, podemos aplicar el mismo rango al tamaño de la estrella para mantener la uniformidad. El tamaño se reducirá más adelante cuando interpretemos la serie de números.

Vamos a utilizar una serie de variables de clase. Estos incluyen: s_seed, un entero entero que define la secuencia completa; s_start y s_end, dos enteros que se generan al dividir la semilla en dos; y s_current, un entero que contiene el número generado más recientemente en la secuencia.

Creating a sequence
Ver esta imagen de mi artículo anterior. 1234 es la semilla, y 12 y 34 son los valores iniciales de s_start y s_end.
Consejo: Tenga en cuenta que cada número generado se origina a partir de la semilla; no hay llamada random(). Esto significa que la misma semilla siempre generará el mismo paisaje de estrellas.

También utilizaremos s_sequence, un String que contendrá la secuencia general. Las dos últimas variables de clase son s_image (de tipo Image - una clase que crearemos más adelante) y s_starfield (de tipo Starfield, la clase que acabamos de crear). El primero almacena la imagen, mientras que el segundo contiene el campo de estrellas.

La ruta que vamos a tomar para crear este generador es bastante simple. Primero, necesitamos hacer un constructor que acepte una semilla. Cuando se hace esto, necesitamos crear un método que acepte un número entero que represente la cantidad de estrellas que debe crear. Este método debería entonces llamar al generador real para obtener los números. Y ahora comienza el trabajo real... creando el generador de secuencias.


Codificando el generador de secuencias.

Lo primero que debe hacer un generador de secuencias es aceptar una semilla. Como se mencionó, dividiremos la semilla en dos: los dos primeros dígitos y los dos últimos dígitos. Por esta razón, debemos verificar si la semilla tiene cuatro dígitos, y rellenarla con ceros si no la tiene. Cuando se hace esto, podemos dividir la cadena semilla en dos variables: s_start y s_end.

Asi que:

  • seed = 1234 significa s_start = 12 y s_end = 34
  • seed = 7 significa s_start = 00 y s_end = 07
  • seed = 303 significa s_start = 03 y s_end = 03

Siguiente en línea: cree otro método que, dados los dos números, genere el siguiente número en la secuencia.

Encontrar la fórmula correcta es un proceso cansado. Por lo general, significa horas de trabajo de prueba y error que intentan encontrar una secuencia que no implique demasiados patrones en la imagen resultante. Por lo tanto, sería más sabio encontrar la mejor fórmula una vez que podamos ver la imagen, en lugar de ahora. Lo que nos interesa en este momento es encontrar una fórmula que genere una secuencia que sea más o menos aleatoria. Por esta razón, usaremos la misma fórmula utilizada en la secuencia de Fibonacci: suma de los dos números.

Cuando se haga esto, ahora podemos continuar y comenzar a crear la secuencia. En otro método, manipularemos la semilla inicial para generar un flujo completo de números, que luego se pueden interpretar como atributos del perfil de estrella.

Sabemos que para una estrella dada necesitamos nueve dígitos: los tres primeros definen la coordenada x, los tres medios definen la coordenada y, y los últimos tres definen el tamaño. Por lo tanto, como fue el caso al alimentar la semilla, para mantener la uniformidad en todo es importante asegurarse de que cada número generado tenga tres dígitos. En este caso, también tenemos que truncar el número si es mayor que 999.

Esto es bastante similar a lo que hicimos antes. Solo necesitamos almacenar el número en una cadena temporal, temp, y luego eliminar el primer dígito. Si el número no tiene tres dígitos, debemos rellenarlo con ceros como hicimos anteriormente.

Con eso envuelto, ahora deberíamos hacer otro método que cree y devuelva un perfil de estrella cada vez que generemos tres números. Usando este método, podemos agregar la estrella a la ArrayList de estrellas.


Poniendolo todo junto

Una vez terminado esto, podemos montar el generador. Debe aceptar la cantidad de estrellas que tiene que generar.

Ya sabemos que para una estrella, necesitamos nueve dígitos, por lo que este generador necesita contar el número de caracteres en las cadenas. El contador, s_counter, almacenará la longitud máxima de la secuencia. Por lo tanto, multiplicamos el número de estrellas por nueve y eliminamos una, ya que un String comienza desde el índice cero.

También debemos contar el número de caracteres que hemos creado desde la última vez que generamos una estrella. Para esta tarea, vamos a utilizar s_starcounter. En un bucle for, que se repetirá hasta que la longitud de la serie sea igual a s_counter, ahora podemos llamar a los métodos que hemos creado hasta ahora.

Consejo: No debemos olvidar reemplazar s_start y s_end, ¡o seguiremos generando el mismo número una y otra vez!

Dibujando estrellas

Ahora que la parte difícil ha terminado, finalmente es hora de pasar a la clase Image y comenzar a dibujar estrellas.

En un método que acepta un Starfield, primero creamos una instancia de un Color, y luego recuperamos el número de estrellas que debemos dibujar. En un bucle for, vamos a dibujar todas las estrellas. Después de hacer una copia de la estrella actual, es importante que recuperemos el radio de la estrella. Dado que el número de píxeles es un entero, debemos agregarlo al radio para convertirlo en un entero.

Para dibujar la estrella, utilizaremos un degradado radial.

An example of a radial gradient
Un ejemplo de gradiente radial.

La opacidad de un degradado radial depende de la distancia de un píxel desde el centro. El centro del círculo tendrá coordenadas (0,0). Usando la convención más común, cualquier píxel a la izquierda del centro tiene una coordenada x negativa, y cualquier píxel debajo tiene una coordenada y negativa.

Debido a esto, los bucles for anidados comienzan con un número negativo. Usando el teorema de Pitágoras, calculamos la distancia desde el centro del círculo y la usamos para recuperar la opacidad. Para las estrellas que tienen el radio más pequeño posible (1px), su opacidad depende únicamente de su tamaño.

Para terminar, necesitamos crear un método que acepte un String y la use para guardar la imagen con ese nombre de archivo. En el generador, debemos crear primero la imagen. Entonces, deberíamos llamar a estos dos últimos métodos desde el generador de secuencias.

En la clase Main, deberíamos crear una instancia del generador de secuencias, asignarle una semilla y obtener un buen número de estrellas (400 deberían ser suficientes). Intente ejecutar el programa, corrija los errores y verifique la ruta de destino para ver qué imagen se creó.

The resulting image with a seed 1234
La imagen resultante con una semilla de 1234.

Mejoras

Todavía hay algunos cambios que podemos hacer. Por ejemplo, lo primero que habrías notado es que las estrellas están agrupadas en el centro. Para arreglar eso, tendrías que encontrar una buena fórmula que elimine cualquier patrón. Alternativamente, puede crear varias fórmulas y alternar entre ellas usando un contador. Las fórmulas que utilizamos fueron estas:

Hay una mejora más simple que podemos implementar. Si miras al cielo, verás algunas estrellas grandes y muchas más pequeñas. Sin embargo, en nuestro caso, el número de estrellas pequeñas es aproximadamente el mismo que el número de estrellas grandes. Para solucionar esto, solo tenemos que volver al método getSize() en la clase Star. Después de hacer que el tamaño sea una fracción de uno, tenemos que aumentar este número a la potencia de un entero, por ejemplo, cuatro o cinco.

Ejecutar el programa una última vez debería darle un resultado satisfactorio.

The final result – a whole starscape procedurally generated by our Sequence Generator!
El resultado final: ¡todo un paisaje estelar generado de manera procesal por nuestro generador de secuencias!

Conclusión

En este caso, utilizamos un generador de secuencia para generar un fondo de manera procesal. Un generador de secuencias como este podría tener muchos más usos; por ejemplo, una coordenada z podría agregarse a la estrella, de modo que en lugar de dibujar una imagen, podría generar estrellas como objetos en un entorno 3D.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.