Autor Tema: Mapas en c64 con scroll  (Leído 1861 veces)

Dashiad

Mapas en c64 con scroll
« en: Mayo 01, 2017, 22:04:55 »
Uno de los motivos que me hacen seguir cacharreando con el c64 es quitarme mi espina clavada:hacer un juego en ensamblador para el commodore.
Y, para empezar, quisiera resolver dos primeros problemas: el mapa, y el scroll.
Lo que quisiera conseguir es tener un mapa continuo, con scroll fino, y con libertad de movimiento del personaje. Es decir, tipo Turrican.

Os comento mi planteamiento inicial, para ver si me podeis echar una mano, e ir acumulando ideas:

Sobre el mapa:
- Basado en tiles: los tiles podrían ser de 8x8 caracteres, o de 4x4 caracteres.
  Si los tiles son de 8x8, la variedad de graficos es poca, es más dificil combinar tiles entre sí, pero los requisitos de memoria, y el numero de indirecciones a hacer en el pintado de la pantalla, es menor.
  La memoria para almacenar los tiles, es mayor que si fueran de 4x4

Si tenemos un máximo de 256 tiles:
 En el caso de 8x8, la memoria requerida es 256*8*8=16Kb
 En el caso de 4x4, la memoria requerida es 256*4*4=  4Kb

Sin tener en cuenta espacio para marcadores:
  Un mapa de 128x32 tiles de 8x8 , equivaldría a unas 25 pantallas de ancho por unas 10 de alto, y ocuparía 4Kb
  Un mapa de 256x64 tiles de 4x4, sería un tamaño de mapa equivalente, y ocuparía 16Kb.

O sea, en uso de memoria para almacenar posiciones, los requisitos de memoria serian iguales.

Sobre el color:
   No me he puesto a estudiar todos los modos posibles, y cual seria el mas adecuado, pero voy a suponer, en principio, que hay 2 comunes, color de fondo, y que libre queda el color de primer plano.
El color de primer plano podría :
   1) Ir asociado al caracter: el caracter se representa con el mismo color esté en el tile en el que esté.
   2) Ir asociado al tile : todos los caracteres de un tile, tiene un mismo color de primer plano.
   3) Ir asociado a la posicion dentro del tile: cada tile tiene, ademas de 8x8 o 4x4 caracteres, 8x8 o 4x4 colores.En el caso de 8x8, necesitaria 16K (8k codificando 2 colores por byte).En el caso de 4x4, necesitaria 4k (2Kb, codificando 2 colores por byte)

Sobre los tipos de objetos del mapa:
El código de caracter debería codificar información del tipo:
- Si codigo & 0x80, el caracter no es atravesable. (Tendríamos 128 caracteres para objetos atravesables, como suelo, etc, y otros 128 para no atravesables)
- Si codigo & 0xC0 == 0xC0, el contacto con el caracter te mata (Tendríamos 64 caracteres para objetos no atravesables que no matan, y otros 64 para caracteres no atravesables que sí matan)
Así se podrían codificar esas propiedades o cualquier otra.

Sobre el dibujado del mapa y el scroll:
  Este modelo no es el tipico de naves donde el scroll es independiente de lo que haga el jugador: el jugador es libre de moverse en cualquier direccion.
El modelo es que existe un rectangulo invisible en el centro de la pantalla.Si el usuario se mueve dentro de ese rectángulo, la pantalla está fija.
Supongamos que el jugador está justo en el borde de ese rectángulo.Si se mueve 1 paso más, se saldría del rectángulo, y provocaría el scroll.
En el turrican, el scroll es inmediato.Se ve que el jugador "empuja" el mapa.El problema es que la nueva pantalla, tiene que ser pintada inmediatamente.
Pero, si miramos el Sams Journey, es diferente: al jugador se le permite atravesar el rectángulo, y el mapa le "seguirá".Eso da tiempo para preparar la nueva pantalla.

Digo "preparar", porque la pantalla nueva debería hacerse con un doble buffer (una pantalla escondida, donde se dibuja mientras se muestra la primera).Sin embargo, en el c64 no se puede hacer doble bufer de la memoria de color.Sin embargo, sí que se podría calcular qué colores hay que pintar en cada posición, y almacenarlo en un buffer de 1k, para que cuando haya que refrescar el mapa de color, sólo haya que copiar el buffer, y no hacer todos los cálculos.

También sería posible hacerlo vía interrupciones: teóricamente, una vez que el raster ha pasado las primeras 8 lineas visibles, ya se puede empezar a pintar, fila a fila, los caracteres.Como pintando vamos a ser más lentos que el raster, no lo vamos a alcanzar.Por seguridad, los sprites sí que habría que actualizarlos cuando el raster esté completamente fuera de la pantalla.

También debería haber una seríe de caracteres especificos que se van a ir actualizando cada x frames, para hacer animaciones, etc

Algun comentario / idea / link de info ?

 

josepzin

Re:Mapas en c64 con scroll
« Respuesta #1 en: Mayo 02, 2017, 00:35:11 »
Este hilo parece que será muy interesante!

Creo que por aquí el único que ha sacado algo así es @riq
Seguro que alguno más tambien hizo algo pero en producto terminado me suena que sólo riq.

Dany Quest

Re:Mapas en c64 con scroll
« Respuesta #2 en: Mayo 02, 2017, 00:57:34 »
A ver, dadle caña, que a mí me interesaría ver un scroll hecho aquí
 

riq

Re:Mapas en c64 con scroll
« Respuesta #3 en: Mayo 02, 2017, 05:21:24 »
nada que agregar... solo que hacelo, equivocate, aprendé y divertite.
usa el editor CharMap para los mapas que te permite reusar los chars que son iguales, reduciendo espacio y te permite crear mapas de tiles y demás.

si te sirve, aca es el código que hice yo: https://github.com/ricardoquesada/c64-the-uni-games
y si tenes alguna duda del código, chiflá.
« última modificación: Mayo 02, 2017, 05:23:42 por riq »
 

Dashiad

Re:Mapas en c64 con scroll
« Respuesta #4 en: Mayo 02, 2017, 12:13:09 »
Buscando info, encontré estos dos links de un modo de scroll puro en hardware, llamado "VSP", que explota un bug en el VIC.
https://www.youtube.com/watch?v=WPxDWDK4gUc


Es interesante seguir la explicación del segundo video, donde entra en profundidad, no en cómo funciona VSP (desgraciadamente),sino en por qué ocurre un bug de la implementación original de VSP....Flipante leerla..
https://www.youtube.com/watch?v=vXcA4OWx0vo

No creo que pueda usarse en juegos (especialmente por el asunto de la corrupción de bits de la que se habla en el segundo video)
 

Dashiad

Re:Mapas en c64 con scroll
« Respuesta #5 en: Mayo 02, 2017, 17:04:07 »
El principio es resolver todas las indirecciones necesarias para ir desde el mapa global de tiles, a los caracteres que hay que poner en pantalla.
He hecho una primera implementación en javascript, sin optimizar nada, que se puede ver aqui:
https://jsfiddle.net/fgL1aaa1/

Es independiente de tamaño de tile , tamaño de mapa, y tamaño de pantalla (aunque esto último requeriria ajustes).Por ahora, no maneja color.

Para pintar el mapa, podriamos ir tile a tile, o fila de pantalla a fila de pantalla.
Tile a tile, el número de veces que hay que calcular offsets en el espacio de mapa y tile, son menos, pero requiere otro bucle interno.Además, si fuera posible pintar durante el raster, hay que dejar tiempo bastante para que el raster haya pasado todas las filas ocupadas por el tile, antes de pintar.

Fila a fila,  se puede empezar a mostrar en pantalla en cuanto el raster ha pasado la octava línea visible, pero el numero de calculos, en principio, es mayor.
Sin embargo, los offsets en espacio de tiles, de cada uno de los tiles en horizontal, se podrian cachear, con lo que solo se calcularian una vez (al pintar la primera fila de cada uno de los tiles).
 

Zub

Re:Mapas en c64 con scroll
« Respuesta #6 en: Mayo 02, 2017, 23:14:20 »
Hola,

Yo te recomiendo que analices en profundidad el siguiente artículo:

https://cadaver.github.io/rants/scroll.html

Básicamente cubre todo lo que necesitas saber. De hecho, Cadaver ofrece el código fuente de sus juegos donde puedes directamente ver como lo implementa. Yo usé como base el scroller del juego BOHF para mi proyecto. Si algo, funciona para qué cambiar?

El esquema que utiliza en sus juegos es de tiles de 4x4, con 1 color por tile. El mapa es un rectángulo de tiles (1 byte por tile, puedes hacer mapas muy grandes). Hay que hacer double buffer usando dos Screens (salvo que tu juego tenga scroll a 50Hz y te de tiempo a hacer todo en un frame). La COLOR RAM hay que actualizarla fuera de frame (antes de que se pinte la nueva Screen). Sus rutinas permiten scroll multidireccional.

 

Dashiad

Re:Mapas en c64 con scroll
« Respuesta #7 en: Mayo 03, 2017, 00:28:38 »
Segunda versión del scroller, aún en javascript.
La diferencia con el anterior, es que los offsets a los tiles a pintar en pantalla, se precalculan y se introducen en una tabla.Con tiles de 5x5, esta tabla contiene hasta 9x6 = 54 direcciones, por lo que son 108 bytes más.
En un primer bucle, se calcula esta tabla.En el segundo, se utiliza para el pintado.

Hay que tener en cuenta que, mientras el tile que se encuentra en la esquina superior izquierda no cambie, no hay que recalcular la tabla de punteros.Incluso si el movimiento es hacia arriba o hacia abajo, sólo hay que añadir por delante, o por detras, los punteros de la nueva fila.

Aun hay que refinarlo, eliminando variables, y, si es posible, eliminar el doble bucle de pintado.
Citar
Hola,

Yo te recomiendo que analices en profundidad el siguiente artículo:

https://cadaver.github.io/rants/scroll.html

Básicamente cubre todo lo que necesitas saber. De hecho, Cadaver ofrece el código fuente de sus juegos donde puedes directamente ver como lo implementa. Yo usé como base el scroller del juego BOHF para mi proyecto. Si algo, funciona para qué cambiar?
Si, ese artículo lo leí hace tiempo...
Pero, lo de "si algo funciona, para qué cambiar"...Um, no estoy de acuerdo (a menos que cambiar cueste demasiadas horas de trabajo/dinero).
Pero, precisamente para el c64..Hay ya miles de juegos que "funcionan"...Para qué vamos a hacer más? Pues por darnos el gustazo de hacerlo...A menos que vayamos a mejorar el turrican, claro.
(Y, siendo más concreto, hay varias cosas en esa explicación, que son optimizables).
 

Zub

Re:Mapas en c64 con scroll
« Respuesta #8 en: Mayo 03, 2017, 13:04:07 »
Por supuesto, se puede y en función del tipo de juego se debería optimizar. En mi caso sólo cambié la forma en la que se actualiza la COLORRAM, y centré mis esfuerzos en otras cosas. ¿Qué pegas le has visto?

Yo también precalculo la dirección de cada fila del mapa. Supongo que se podría optimizar para que ocupe menos (el mapa no lo tengo comprimido) pero complicaría un poco la lógica del "scroll logic", no?
 

Dashiad

Re:Mapas en c64 con scroll
« Respuesta #9 en: Mayo 03, 2017, 17:59:17 »
Dices que precalculas la dirección de cada fila del mapa...Con qué objetivo?
Yo no precalculo eso.Seguramente me he explicado mal.
Para mí, el mapa se almacena como memoria contigua de MAPCOLSxMAPROWS bytes.El byte contiene el id del tile que hay en esa posicion. Si hay 128x128 tiles, son 16Kb de mapa (descomprimido).
Los tiles se almacenan como memoria contigua de 256*(TILEW*TILEH) bytes.En el caso de tiles de 5x5, esto  son 6400 bytes.

Ahora, dada una posicion de pantalla, hay que:
 - 1) Calcular qué tiles hay que dibujar, según el mapa.Calcular el offset en la memoria de tiles, para esos tiles.
 - 2) Para cada tile, ir copiando de la memoria de tiles a la memoria de pantalla, cuando sea necesario hacer scroll.

El paso 1, hay que ejecutarlo cada vez que el primer tile a pintar, cambie.Es decir, cada vez que el jugador recorra 1 tile completo.Con un tile de tamaño 5x5, el numero de tiles que hay que pintar en una pantalla de 40x25 son 8x5=40 tiles, (en realidad, normalmente seran 9x6 = 54 tiles).Son esos punteros los que yo precalculo.

El paso 2, hay que ejecutarlo cada vez que el jugador recorra 1 caracter completo.Pero los punteros no han cambiado, sólo cambia el offset dentro de los tiles desde donde hay que empezar a pintar.

Por otro lado, si se quisiera tener color por cada caracter de cada tile, el cálculo sería idéntico, con un coste de otros 6400 (o 3200) bytes.

Aunque no se puede hacer doble buffer de la memoria de color, sí que se puede dejar 1Kb donde "construir" el siguiente mapa de color, de forma que a la hora de mostrarlo, sólo haya que copiar de una direccion a otra, sin tener que hacer cálculos a la vez.

El calcular que tiles son visibles por un lado, y pintarlos por otro, permite dividir el trabajo entre frames, además de que ese buffer de tiles "activos" se puede usar para, por ejemplo, activar código a ejecutar  (animaciones, trampas) asociados a tiles.

Aún no lo he pasado a ensamblador para ver cuántos ciclos requiere, pero el bucle de pintado (el que se tiene que ejecutar en el paso 2) es ahora mucho más simple.
 

Dashiad

Re:Mapas en c64 con scroll
« Respuesta #10 en: Mayo 04, 2017, 01:48:53 »
Por cierto, que la implementación js con la caché de punteros a tiles, es:
https://jsfiddle.net/zu1fxk7h/
 

Zub

Re:Mapas en c64 con scroll
« Respuesta #11 en: Mayo 04, 2017, 07:23:46 »
Dices que precalculas la dirección de cada fila del mapa...Con qué objetivo?
Hay dos bytes mapx y mapy que te indican en qué posición del mapa estás (la esquina superior izquierda de la pantalla como referencia). Mirando mapy, ya sabes que filas están visibles. Como tienes un puntero a cada fila, basta con acceder a  fila[mapx], fila[mapx+1]...fila[mapx+39] para pintar una línea de tiles entera en la pantalla.

Una pregunta, por qué has elegido tiles de 5x5? Si el tamaño es múltiplo de 2,4 o 8 entonces se simplifica mucho la lógica de como acceder a cada char dentro del tile. Lo habitual es usar 4x4, y algunos juegos usan 2x2. Como te decía, si tienes un bloque cuadrado (ejemplo 4x4) y sabes en qué posición mapx-mapy estás dentro del mapa, sabes en qué offset estás dentro del tile de referencia y es relativamente sencillo apañar la rutina para que lea los chars correctos. Con 5x5 al ser asimétrico y no múltiplo de 4 no sé muy bien cómo lo haría en esamblador...

A no ser que tengas mucha memoria libre y si no es una limitación gráfica importante (depende mucho del tipo de juego) yo pondría un color por tile. De ese modo, al hacer scroll sólo tienes que cambiar la COLORRAM al cambiar de un tile a otro. Es decir si son tiles de 5x5, sólo habría que rellenar nuevo color en el extremo de la pantalla tras scrollear 5 chars enteros. Obviamente si tienes un color por char queda infinitamente más atractivo pero usas mucha memoria y la actualización de scroll te lleva más tiempo de raster.

En cualquier caso si lo consigues implementar te quedaría una chulada. La clave está en que consigas optimizar mucho la rutina de scroll, sobre todo si por el tipo de juego va a haber scroll frecuente, entonces es código que se ejecutará cada frame y necesitarás que no te ocupe demasiado rastertime.




Yo no precalculo eso.Seguramente me he explicado mal.
Para mí, el mapa se almacena como memoria contigua de MAPCOLSxMAPROWS bytes.El byte contiene el id del tile que hay en esa posicion. Si hay 128x128 tiles, son 16Kb de mapa (descomprimido).
Los tiles se almacenan como memoria contigua de 256*(TILEW*TILEH) bytes.En el caso de tiles de 5x5, esto  son 6400 bytes.

Ahora, dada una posicion de pantalla, hay que:
 - 1) Calcular qué tiles hay que dibujar, según el mapa.Calcular el offset en la memoria de tiles, para esos tiles.
 - 2) Para cada tile, ir copiando de la memoria de tiles a la memoria de pantalla, cuando sea necesario hacer scroll.

El paso 1, hay que ejecutarlo cada vez que el primer tile a pintar, cambie.Es decir, cada vez que el jugador recorra 1 tile completo.Con un tile de tamaño 5x5, el numero de tiles que hay que pintar en una pantalla de 40x25 son 8x5=40 tiles, (en realidad, normalmente seran 9x6 = 54 tiles).Son esos punteros los que yo precalculo.

El paso 2, hay que ejecutarlo cada vez que el jugador recorra 1 caracter completo.Pero los punteros no han cambiado, sólo cambia el offset dentro de los tiles desde donde hay que empezar a pintar.

Por otro lado, si se quisiera tener color por cada caracter de cada tile, el cálculo sería idéntico, con un coste de otros 6400 (o 3200) bytes.

Aunque no se puede hacer doble buffer de la memoria de color, sí que se puede dejar 1Kb donde "construir" el siguiente mapa de color, de forma que a la hora de mostrarlo, sólo haya que copiar de una direccion a otra, sin tener que hacer cálculos a la vez.

El calcular que tiles son visibles por un lado, y pintarlos por otro, permite dividir el trabajo entre frames, además de que ese buffer de tiles "activos" se puede usar para, por ejemplo, activar código a ejecutar  (animaciones, trampas) asociados a tiles.

Aún no lo he pasado a ensamblador para ver cuántos ciclos requiere, pero el bucle de pintado (el que se tiene que ejecutar en el paso 2) es ahora mucho más simple.
 

Dashiad

Re:Mapas en c64 con scroll
« Respuesta #12 en: Mayo 04, 2017, 10:12:35 »
Um, pero esa fila inicial a pintar, sólo hay que calcularla 1 vez por cada refresco, no es que quite demasiado trabajo...Lo que no me queda tan claro es si en fila[mapx], lo que hay es un índice al tile (1 byte), o un puntero al tile (2 bytes)
No estoy fijando el tamaño del tile.El tamaño podría ser cualquiera.(8x8,4x4,5x5).Uso 5x5 como referencia porque cubre toda la pantalla.
Yo no veo, por ahora, ventaja en que el tamaño del tile sea múltiplo de 2.Igual lo veo después.
Sobre la COLORRAM, la ventaja que da tener 1 color por tile, es muy muy poca, incluso con el método de cadaver.Me explico:
Con un scroll de 2 pixeles, hay que repintar 1 de cada 4 frames.En el frame que hay que repintar, y usando el metodo de cadaver, lo que hay que recalcular es sólo la nueva fila o columna, o ambas, que entran en pantalla.Del resto, se hace shift, por lo que da lo mismo si se hace shift de colores individuales, o bloques de color.
Es decir, que la limitación de 1 color por tile, permite que un proceso que afecta a , como máximo, (40+24)/40*25 = 0.06 (6%) de los caracteres, en 1 de cada 4 frames, sea más rápido..
El usar 1 color por tile, hace más rápido el primer pintado completo de pantalla.A partir de ahi,si haces shifting, no estás ganando demasiado.
Si, por otro lado, el cálculo de colores se almacena en un doble buffer, del cual luego copias a memoria de color (que es lo que quisiera probar), da lo mismo: lo que ralentiza es el cálculo del doble buffer,y ni siquiera eso, porque los offsets a la memoria de  color de tiles, son los mismos que los offsets a los caracteres de tiles, asi que no se hacen más cálculos.Esto también es así en el método de cadaver: a la vez que se calcula qué caracteres nuevos hay en pantalla, con los mismos offsets (dividido entre 2), tienes el color de esos caracteres.

Sobre si usa mucha memoria...Puede usar la mitad de memoria que los tiles (2 colores por byte), más 1K de doble buffer.En el mismo metodo de cadaver, donde hay 192 tiles de 4x4, usaria 3072 bytes de memoria de caracteres, y la mitad (1536 bytes) para memoria de color...No me parece tanto para lo que se gana..
 

Zub

Re:Mapas en c64 con scroll
« Respuesta #13 en: Mayo 04, 2017, 16:21:37 »
Um, pero esa fila inicial a pintar, sólo hay que calcularla 1 vez por cada refresco, no es que quite demasiado trabajo...Lo que no me queda tan claro es si en fila[mapx], lo que hay es un índice al tile (1 byte), o un puntero al tile (2 bytes)
Ten en cuenta que tienes que acceder a 40 filas en cada frame. Puedes calcular la dirección de la primera y luego ir sumando 40 bytes e ir actualizando los 2 bytes del puntero. Es muy fácil, pero son calculillos que se hacen 40 veces por frame. Si precalculas, gastas 256 bytes de buffer para los punteros (128 filas) pero te ahorras rastertime en cada frame. Es simplemente una optimización para ensamblador.

No estoy fijando el tamaño del tile.El tamaño podría ser cualquiera.(8x8,4x4,5x5).Uso 5x5 como referencia porque cubre toda la pantalla.
Yo no veo, por ahora, ventaja en que el tamaño del tile sea múltiplo de 2.Igual lo veo después.
A nivel conceptual no lo hay. Yo lo decía por la implementación en ensamblador, el ser un numero potencia de 2 te facilita la vida. Pero igual hay una forma elegante de hacerlo con 5 tambien.

Sobre la COLORRAM, la ventaja que da tener 1 color por tile, es muy muy poca, incluso con el método de cadaver.Me explico:
Con un scroll de 2 pixeles, hay que repintar 1 de cada 4 frames.En el frame que hay que repintar, y usando el metodo de cadaver, lo que hay que recalcular es sólo la nueva fila o columna, o ambas, que entran en pantalla.Del resto, se hace shift, por lo que da lo mismo si se hace shift de colores individuales, o bloques de color.
Es decir, que la limitación de 1 color por tile, permite que un proceso que afecta a , como máximo, (40+24)/40*25 = 0.06 (6%) de los caracteres, en 1 de cada 4 frames, sea más rápido..
El usar 1 color por tile, hace más rápido el primer pintado completo de pantalla.A partir de ahi,si haces shifting, no estás ganando demasiado.

No te acabo de seguir. Si tengo un color único por tile, efectivamente me ahorro escribir en los extremos 1 de cada 4 frames. Pero es que además al hacer el shifting solo hace tocar una columna de cada cuatro...

Quiero decir si tengo 3 tiles de diferentes colores (12 chars) y hago shift a la derecha:

RRRRGGGGBBBB

RRRGGGGBBBBY

Solo tengo que tocar 3 chars, no?


Si, por otro lado, el cálculo de colores se almacena en un doble buffer, del cual luego copias a memoria de color (que es lo que quisiera probar), da lo mismo: lo que ralentiza es el cálculo del doble buffer,y ni siquiera eso, porque los offsets a la memoria de  color de tiles, son los mismos que los offsets a los caracteres de tiles, asi que no se hacen más cálculos.Esto también es así en el método de cadaver: a la vez que se calcula qué caracteres nuevos hay en pantalla, con los mismos offsets (dividido entre 2), tienes el color de esos caracteres.
Yo esa parte la hice diferente... precalculo la fila o columna nueva de color en un array, y luego cuando estoy off-screen copio del array a la zona correspondiente de COLORRAM.

Sobre si usa mucha memoria...Puede usar la mitad de memoria que los tiles (2 colores por byte), más 1K de doble buffer.En el mismo metodo de cadaver, donde hay 192 tiles de 4x4, usaria 3072 bytes de memoria de caracteres, y la mitad (1536 bytes) para memoria de color...No me parece tanto para lo que se gana..
Tienes razón. Yo creo que es más una cuestión de rastertime, como te decía si consigues que te calcule todo bien a 25 hz (double buffer) te puede quedar una chulada. Por supuesto hay juegos que tienen tiles de 2x2, y queda super colorista. Si no tienes cálculos complejos (multiplexer, etc) y te cabe en el raster, genial.
 

Dashiad

Re:Mapas en c64 con scroll
« Respuesta #14 en: Mayo 04, 2017, 17:43:21 »
Hay dos bytes mapx y mapy que te indican en qué posición del mapa estás (la esquina superior izquierda de la pantalla como referencia). Mirando mapy, ya sabes que filas están visibles.
Um, en esto no consigo seguirte sigo.Si el mapa lo almacenas como una lista de tiles, la x-y de mapa, lo que me da es 1 tile.Eso no me da las filas visibles.Y, en cada fila, hay varios tiles.
Si a lo que te refieres, es que el mapa está *expandido* en la memoria ( o sea, mapx y mapy sí que son 1 caracter, no 1 tile)....Es eso?...Si es así, el tamaño máximo de mapa se tiene que reducir bastante (con tiles 4x4, el mapa ocupa 16 veces más que si sólo se almacena el tile).

No te acabo de seguir. Si tengo un color único por tile, efectivamente me ahorro escribir en los extremos 1 de cada 4 frames. Pero es que además al hacer el shifting solo hace tocar una columna de cada cuatro...

Quiero decir si tengo 3 tiles de diferentes colores (12 chars) y hago shift a la derecha:

RRRRGGGGBBBB

RRRGGGGBBBBY

Solo tengo que tocar 3 chars, no?
Ah, no había pensado en hacer shifting así...Pero, en el peor de los casos (scroll en diagonal, hay que hacer shift tanto de 1 fila, como de 1 columna.Dependiendo de cómo lo hagas, hay que mover 4+4 o 4+3 posiciones de cada 16 (en tiles 4x4), lo cual es más o menos la mitad.
Que está muy bien, pero, el "overhead" de codigo para hacerlo asi, es mucho?

Si no consigo que se mueva bien el mapa de color, es una buena opción hacerlo así!