Las paletas personalizables agregan una variedad simple a los personajes de tu juego

Las paletas de imágenes se han utilizado en gráficos de computadora desde el principio y, aunque rara vez se encuentran en juegos modernos, una cierta clase de problemas sería prácticamente imposible sin ellos. En este tutorial vamos a construir un diseñador de personajes MMO para un juego en 2D usando paletas, texturas múltiples y sombreadores..

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

El modelo de personaje utilizado para la demostración fue hecho por Dennis Ricardo Díaz Samaniego, y se puede encontrar aquí: Leviatán. La plataforma fue hecha por Daren Davoux y se puede encontrar aquí: Leviathan Rigged.


Vista previa del resultado final

Haga clic en una sección del modelo de carácter y luego haga clic en cualquier lugar del selector de color. ¿No puedes ver el SWF arriba? Mira este video en su lugar:

Los archivos fuente completos están disponibles en la descarga fuente..


Preparar

La implementación de demostración utiliza AS3 y Flash, con la biblioteca Starling para el procesamiento acelerado por GPU y la biblioteca Feathers para la interfaz de usuario. Nuestra escena inicial contiene una imagen del personaje y el selector de color, que se utilizará para cambiar los colores de la paleta de caracteres..


Paletas en juegos

La representación de colores usando paletas era común en los primeros juegos debido a los requisitos de hardware. Esta técnica asignaría un valor de una imagen a otro valor en una paleta. Por lo general, la imagen tendría un conjunto de valores más pequeño para guardar la memoria y se usaría para buscar el valor real en la paleta..

El sprite de Mario a continuación no está hecho de rojo, naranja y marrón; está compuesto de 1, 2 y 3. Cuando Mario recoge una flor de fuego, la paleta cambia de manera que 1 representa el blanco y 3 representa el rojo. Mario se ve diferente, pero su sprite no cambia realmente.

Dado que el uso de la representación de imágenes en color verdadero de 24 bits ha sido común durante más de una década, quizás se esté preguntando cómo esta técnica podría ser útil hoy en día. El primer caso de uso obvio es en hacer juegos retro, pero rara vez es necesario usar paletas allí. Un artista puede limitarse a un determinado conjunto de colores, pero aún así puede utilizar todo el espectro de 24 bits, ya que es una forma fácil de manejar texturas en hardware moderno..

Una técnica utilizada durante los días de las imágenes de paleta fue el intercambio de paletas: se usó para cambiar la imagen existente a un esquema de color diferente. Y muchos de ustedes recordarán cómo los niveles de monstruos en los juegos de rol antiguos tenían un color diferente; esto ahorró tiempo para los artistas y usó menos memoria, lo que permite una mayor variedad de diseños de monstruos. (Podría decirse que esto podría hacer que el juego parezca repetitivo, sin embargo).

Esto nos lleva al objetivo de este tutorial, que es permitirte tener más variedad en los recursos de tu juego. Simularemos un creador de personajes MMO, con colores personalizables para las partes de los personajes, utilizando una paleta..


Haciendo Imágenes Paleteadas

Hacer imágenes con paleta es un poco más difícil que hacer imágenes normales. Hay algunas limitaciones y detalles técnicos a tener en cuenta..

En primer lugar: las paletas tienen un alcance limitado; en este tutorial, cada sprite de caracteres tiene 8 bits, es decir, 256 valores posibles, de los cuales el valor '255' se usará para denotar 'transparente'.

Para el arte de píxeles, esto no es un gran problema ya que generalmente se basa en una paleta limitada elegida por un artista. Mientras dibuja, los colores de la paleta se definen y aplican a la imagen..


La imagen de la derecha tiene una paleta modificada que se mostrará durante la noche en el juego..

En el ejemplo estoy usando un modelo 3D; Hice esto en capas separadas, luego asigné cada capa al espectro de una paleta. Esto se puede hacer manualmente editando los niveles de la imagen para que se ajusten a la parte deseada de la paleta. Aquellas partes de la paleta que representaremos como pequeños gradientes, para permitir el mapeo de sombras a resaltes.


El panel Niveles muestra claramente cómo cada componente de carácter toma un segmento de valores de imagen.

Esto reducirá la profundidad general de la imagen de una imagen. Si está haciendo un juego de dibujos animados (cel shaded), esto puede estar bien, pero puede que le falte un estilo más realista. Podemos remediarlo de alguna manera sacrificando la memoria mientras aumentamos la profundidad de una imagen a 16 bits: la paleta podría tener el mismo tamaño y utilizaríamos la interpolación para proporcionarnos más variedad..


Implementación

Nuestra implementación utiliza una única textura de canal de 8 bits para el personaje. Para simplificar, lo haremos usando una imagen PNG normal, cuyos canales verde y azul están configurados en 0, y todo está almacenado en el canal rojo (por lo que la imagen de ejemplo está en rojo). Dependiendo de su plataforma, puede guardarlo en un formato de canal único apropiado. Una paleta inicial se guarda como una imagen 1D, con un ancho de 256 para representar todos los valores que mapearemos.


Imagen del personaje principal.
Paleta de inicio para el personaje en nuestro ejemplo..

La parte principal no es tan complicada. Vamos a buscar ciertos valores en una textura basada en otra textura. Para esto vamos a utilizar texturas múltiples y un sombreador de fragmentos simple.

(Múltiples texturas esencialmente significa usar múltiples texturas mientras se dibuja una única pieza de geometría, y es compatible con GPU).

 // configurando texturas múltiples, el contexto es un contexto Stage3D context.setTextureAt (0, _texture.base); // fs0 en el shader context.setTextureAt (1, _palette.base); // fs1 en el shader

Otra cosa que debemos tener en cuenta es que necesitamos una forma de hacer transparentes partes de las imágenes. Anteriormente mencioné que esto se hará usando un valor especial (255) que significa transparente. Los sombreadores de fragmentos tienen un comando que descartará el fragmento, haciéndolo invisible. Haremos esto detectando cuando el valor es 255.

Este ejemplo utiliza el lenguaje de sombreado AGAL. Puede ser un poco difícil de entender, ya que es un lenguaje de tipo ensamblador. Puede obtener más información al respecto en Adobe Developer Connection.

 // leer el valor de la textura regular (a ft1) tex ft1, v1, fs0 <2d, linear, mipnone, repeat> // restar el valor de la textura (ft1.x) del // umbral alfa (fc0.x definido como 0.999 en el código principal) sub ft2.x, fc0.x, ft1.x // descartar el fragmento si representa la máscara, // 'kil' hace eso si el valor es menor que 0 kil ft2.x // lee el color de la paleta usando el valor de la textura regular (ft1) tex ft2, ft1, fs1 <2d, nearest, mipnone, repeat> // multiplica el color del vértice con el color de la paleta y guárdalo en la salida mul oc, ft2, v0

Ahora se puede encapsular en un objeto de visualización de Starling personalizado que contiene la textura y la imagen de la paleta. Así es como se implementa en el código fuente de ejemplo..

Para cambiar el actual colores Del personaje, tendremos que cambiar la paleta. Si quisiéramos cambiar el color de una parte particular del personaje, tomaríamos la paleta de escala de grises original y cambiaríamos el color del segmento que corresponde a esa parte..


La parte de la paleta correspondiente al cabello ahora está coloreada..

El siguiente código recorre la paleta y aplica el color apropiado a cada parte:

 // ir a través de la paleta para (var i: int = 0; i < _paletteVector.length; i++)  // 42 is the length of a segment in the palette var color:uint = _baseColors[int(i / 42)]; // extract the RGB values from the segment color value and // multiply original grayscale palette var r:uint = Color.getRed(color) * _basePaletteVector[i]; var g:uint = Color.getGreen(color) * _basePaletteVector[i]; var b:uint = Color.getBlue(color) * _basePaletteVector[i]; // create a new palette color by joining color components _paletteVector[i] = Color.rgb(r, g, b); 

Nuestros colores se guardan como enteros sin signo que abarcan 8 bits de valor rojo, verde y azul. Para sacarlos tendríamos que hacer algunas operaciones a nivel de bits, por suerte Starling ofrece métodos de ayuda para eso en el Color clase.

Para habilitar la selección de partes de caracteres, mantenemos los datos de la imagen en la memoria. Luego podemos determinar la sección del carácter al que corresponde un clic del ratón leyendo el valor de píxel en ese punto.

 var characterColor: uint = _chracterBitmapData.getPixel (touchPoint.x, touchPoint.y); // toma el valor rojo var characterValue: uint = Color.getRed (characterColor); // 255 significa transparente, así que lo usaremos como deselección si (characterValue == 255) _sectionSelection = SECTION_NONE;  else // calcular la sección, cada sección toma 42 píxeles de la paleta _sectionSelection = int (characterValue / 42) + 1; 

Podemos hacer mas

Esta implementación tiene algunas limitaciones, que se pueden resolver con un poco más de trabajo según sus necesidades. La demostración no muestra la animación de la hoja de sprites, pero se puede agregar sin modificar la clase de paleta principal.

Es posible que hayas notado que la transparencia se maneja como visible y no visible. Esto causa bordes ásperos que pueden no ser adecuados para todos los juegos. Esto se puede resolver utilizando un máscara - una imagen en escala de grises que representa el valor de transparencia, desde negro (totalmente opaco) a blanco (totalmente transparente). Esto aumentará un poco los requisitos de memoria, sin embargo.

Una técnica alternativa que se puede usar para cambiar el color de las partes del objeto es usar una textura adicional o un canal. Se utilizan como máscaras o tablas de búsqueda (que son similares a las paletas) para encontrar valores de color que se multiplicarán con la textura original. Un ejemplo de esto se puede ver en este video:

Se pueden lograr efectos realmente interesantes animando la paleta. En la demostración de ejemplo, esto se usa para representar la parte del personaje donde se está cambiando el color. Podemos hacerlo cambiando el segmento de la paleta que representa una sección del personaje, creando un movimiento circular:

 // guarda el valor inicial de la sección de paleta var tmp: int = _paletteVector [inicio]; // recorra el segmento de la sección de la paleta y desplace los valores a la izquierda para (var i: int = start; i < end; i++)  _paletteVector[i] = _paletteVector[i + 1];  // use saved staring value, wrapping around _paletteVector[end] = tmp;

Un ejemplo bastante sorprendente de esto se puede ver aquí: Canvas Cycle.

Una palabra de precaución: Si confía mucho en esta técnica de animación, puede ser mejor hacerlo en la CPU, porque cargar la textura en la GPU para cada cambio de paleta puede ser costoso..

Para obtener algo de rendimiento, podemos agrupar varias paletas de caracteres (1D) en una sola imagen (2D); esto nos permitiría agregar una variedad de caracteres con cambios mínimos en el estado de representación. El caso de uso perfecto para esto es un juego MMO..


Conclusión

La técnica descrita aquí puede ser muy efectiva en entornos MMO 2D, especialmente para juegos web en los que el tamaño de descarga es muy importante. Espero haber logrado darle algunas ideas de lo que puede hacer si piensa en sus texturas de una manera diferente. Gracias por leer!