Una línea:
usePostProcess(dither({ levels: COLOR_LEVELS.genesis }));Quítala y vuelve a ponerla. Es el capítulo entero.
Post-proceso: un filtro sobre la imagen terminada
Todo lo que has hecho hasta ahora le dice al motor qué dibujar. El post-proceso es distinto: el motor dibuja el fotograma completo y después le pasa un shader por encima, a la imagen entera, antes de enseñarla.
Por eso usePostProcess no va asociado a ningún sprite. No es de la pala ni de la bola:
es de la pantalla.
Qué hace el dithering
levels: 8 significa ocho tonos por canal de color en vez de los 256 que tiene tu
monitor. Eso es exactamente lo que podía enseñar una Mega Drive: 3 bits por canal.
Reducir los colores a lo bruto produce bandas feas: escalones visibles donde antes había un degradado suave. El dithering es el truco de la época para taparlas: mezcla los dos tonos disponibles más cercanos en un patrón regular y deja que tu ojo los promedie. Grano en lugar de escalones.
El detalle que hace que esto funcione está dentro y no lo tienes que tocar: la amplitud
del ruido tiene que valer exactamente un escalón de cuantización. Si te quedas corto, las
bandas siguen ahí debajo del grano; si te pasas, los píxeles bailan entre colores que no
son vecinos y parece interferencia. Aquí ese número sale de levels, así que los dos no
se pueden desincronizar.
Prueba otros:
COLOR_LEVELS.genesis // 8 tonos: Mega Drive
COLOR_LEVELS.snes // 32: Super Nintendo
COLOR_LEVELS.ps1 // 32: PlayStation, Saturn
COLOR_LEVELS.poster // 4: no es ninguna consola, pero queda bienY strength, que escala el efecto sin romper esa relación:
usePostProcess(dither({ levels: COLOR_LEVELS.poster, strength: 0.5 }));Por qué esto es el motor y no un shader tuyo
Podrías escribir el shader a mano: el motor te deja. Pero acertar con la amplitud del ruido es la parte difícil, y equivocarse no da un error: da una imagen que casi está bien y no sabes por qué.
Esa es la idea de todo NacatamalOn, y ahora ya la has visto por dentro: la limitación (paleta corta, poca geometría, grano) no es una carencia que haya que disimular. Es el estilo, y viene puesta.
¿Qué es un shader?
Un programa pequeñísimo que se ejecuta en la tarjeta gráfica, una vez por píxel, en paralelo para todos a la vez.
Aquí el shader recibe el color que iba a tener un píxel y devuelve otro: el mismo, redondeado a uno de los ocho tonos disponibles, más un pelín de ruido ordenado. Multiplica eso por los 153.600 píxeles de un juego de 480×320 y sesenta veces por segundo, y entiendes por qué esto lo hace la gráfica y no el procesador.
No hace falta que sepas escribirlos para usar el motor. dither y sus compañeros vienen
hechos.
Ya está
Tienes un juego. Se puede jugar, se puede ganar, se puede perder, y tiene el aspecto que querías.
Repasa lo que has aprendido, porque no es poco: el bucle y dt, sprites y coordenadas,
teclado, colisión de cajas, crear objetos con un bucle, destruirlos de forma diferida,
texto de mapa de bits y post-proceso. Con eso se hace un juego de verdad, no un juego de
tutorial.
Y no has instalado nada.