Formato que genera mapcnv

Soporte técnico sobre los lanzamientos de MojonTwins y comentarios sobre los güegos. Ofrecemos soporte técnico con Fourspriter, te ayudamos con ZX Basic o Z88DK, te damos pistas some cómo saltarse un bicho y cosas así.

Moderador: na_th_an

Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: Formato que genera mapcnv

Mensajepor na_th_an » Vie, 10 Jul 2015, 11:57

SP1 y splib2 siguen la misma filosofía. Era más para entender conceptos que para otra cosa.
Como diría Rorshach: "Urm..."
Avatar de Usuario
Javi Perez
Mensajes: 13
Registrado: Lun, 08 Jun 2015, 11:47

Re: Formato que genera mapcnv

Mensajepor Javi Perez » Lun, 13 Jul 2015, 14:02

na_th_an escribió:SP1 y splib2 siguen la misma filosofía. Era más para entender conceptos que para otra cosa.


Bueno, pues efectivamente pintar fondos es mucho más fácil y eficiente usando tiles asociados a caracteres del charset, que mediante sprites como hacía al principio...lo que se aprende haciendo burradas!

Ahora me encuentro con otro follón, he estado haciendo pruebas y pasa lo siguiente: imaginemos que defino un tile de 32 bytes (2x2 caracteres), y lo exporto desde SevenuP (ordenado por caracteres y filas):

Código: Seleccionar todo

unsigned char tile_bytes_1[32] = {
   64, 253, 255, 95, 191, 95, 63, 95,
   28, 127, 255, 255, 255, 158, 238, 240,
   63, 95, 190, 127, 95, 191, 90, 52,
   239, 31, 255, 255, 255, 255, 255, 30 };

Entiendo que tengo que asignar cada uno de los 4 caracteres a un carácter del charset. Entonces lo que hago es descomponer el array en cuatro (cuadrícula de 8x8 píxeles):

Código: Seleccionar todo

uchar tile1_0[] = { 64, 253, 255, 95, 191, 95, 63, 95 };
uchar tile1_1[] = { 28, 127, 255, 255, 255, 158, 238, 240 };
uchar tile1_2[] = { 63, 95, 190, 127, 95, 191, 90, 52 };
uchar tile1_3[] = { 239, 31, 255, 255, 255, 255, 255, 30 };

Defino a continuación un array de 4 caracteres para este tile:

Código: Seleccionar todo

char char_set_1[4] = { 'a', 'b', 'c', 'd' };

Y mapeo cada carácter de ese array a cada uno de los 4 arrays del tile:

Código: Seleccionar todo

sp1_TileEntry(char_set_1[0], tile1_0);
sp1_TileEntry(char_set_1[1], tile1_1);
sp1_TileEntry(char_set_1[2], tile1_2);
sp1_TileEntry(char_set_1[3], tile1_3);

Ahora para pintar esos 4 caracteres del tile en la posición (y,x) hago:

Código: Seleccionar todo

sp1_PrintAt(y, x, INK_BLACK | PAPER_WHITE, char_set_1[0]);
sp1_PrintAt(y, x + 1, INK_BLACK | PAPER_WHITE, char_set_1[1]);
sp1_PrintAt(y + 1, x, INK_BLACK | PAPER_WHITE, char_set_1[2]);
sp1_PrintAt(y + 1, x + 1, INK_BLACK | PAPER_WHITE, char_set_1[3]);


Sobre este proceso, tengo dos dudas:
1. ¿Existe alguna forma de no duplicar la info del tile? (array de 32 bytes generado por SevenuP, más los 4 sub-arrays para cara uno de los caracteres del tile)
2. Si por ejemplo defino 20 tiles, tendría que crear 20 arrays char_set_N[4], o sea 40 caracteres, supongo del rango imprimible del charset. ¿Tendría que ir cogiendo esos 40 caracteres e ir creando a manualmente esos 20 arrays?
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: Formato que genera mapcnv

Mensajepor na_th_an » Lun, 13 Jul 2015, 14:28

Yo es que lo hago todo de un tirón. Exporto los 2048 bytes del charset y se los encasqueto a las 256 definiciones de un tirón. Desconozco SP1 pero viendo tu código supongo que sería tan fácil como, tiendo en "tile_bytes" todos los caracteres, hacer esto al principio:

Código: Seleccionar todo

unsigned char *ptr;
unsigned char it;
[...]

ptr = tile_bytes_1;
it = 0; do {
    sp1_TileEntry (it, ptr);
    ptr += 8;
} while (++ it);


Ese bucle itera de 0 a 255 (como it es unsigned char, podemos hacer el truco ese para ahorrar código y CPU, cuando incrementa 255 vuelve a 0 y se sale del bucle) y usa ptr para ir apuntando dentro del array tile_bytes_1 de 8 en 8 bytes.

Una vez tienes todos los chars, todo depende de como te lo montes. Por un lado puedes tener, como en Churrera/MK2, tiles sencillos en los que un tile N lleva los cuatro caracteres a partir de 64+N*4 y tener esto:

Código: Seleccionar todo

unsigned char gpi; // globales estáticas: más rápidas. Menos código ASM.
void tile_print_ctr (unsigned char x, unsigned char y, unsigned char c, unsigned char n) {
    gpi = 64 + (n << 2);
    sp1_PrintAt(y, x, c, gpi++);
    sp1_PrintAt(y, x + 1, c, gpi++);
    sp1_PrintAt(y + 1, x, c, gpi++);
    sp1_PrintAt(y + 1, x + 1, c, gpi);
}


También puedes hacer un mapeo, definiendo un array que diga que para cada tile qué cuatro caracteres lleva y de qué colores. Las posibilidades son infinitas y todo depende de tus necesidades.

En Severin Sewers tenemos uno de estos arrays (por ejemplo):

Código: Seleccionar todo

// ATTR CHAR ATTR CHAR ATTR CHAR ATTR CHAR
unsigned char metatiles [] = {
    7, 4, 71, 5, 7, 4, 71, 6,
    6, 4, 72, 5, 6, 4, 72, 6,
    ...
}

unsigned char *gpi;
void print_metatile (unsigned char x, unsigned char y, unsigned char n) {
   gpi = metatiles + (n << 2);
   sp1_PrintAt(y, x, *gpi ++, *gpi ++);
   sp1_PrintAt(y, x + 1, *gpi ++, *gpi ++);
   sp1_PrintAt(y + 1, x, *gpi ++, *gpi ++);
   sp1_PrintAt(y + 1, x + 1, *gpi ++, *gpi);
}


(como almacenamos secuencialmente aributo y caracter y sp1_PrintAt toma los parámetros en ese orden, podemos usar un puntero e incrementarlo sin tener que hacer mucho más).

Las bibliotecas sólo tienen un array que dice dónde está el gráfico para cada tile. En realidad sólo tienes los gráficos en un sitio, y con sp1_TileEntry dices dónde está cada gráfico para cada caracter. Esto es una de las cosas que no me gustan. Permite mucha versatilidad pero, a fin de cuentas, el 100% de las veces que he usado splib2 (y SP1 es igual en este respecto) he tenido los gráficos juntos, con lo que en realidad bastaría con almacenar la dirección donde empieza TODO el charset, y no la dirección donde está cada char. En estos supuestos, ese array de asignación es tirar 512 bytes. Pero bueno, es así como está hecho :D
Como diría Rorshach: "Urm..."
Avatar de Usuario
Javi Perez
Mensajes: 13
Registrado: Lun, 08 Jun 2015, 11:47

Re: Formato que genera mapcnv

Mensajepor Javi Perez » Mar, 21 Jul 2015, 14:28

Bueno, pues resueltas las dudas sobre los mapas y comprobado que funciona todo, paso a comentar otro "desafío" al que me enfrento. Se trata de cómo "mapear el mapa". Es decir, cómo es la lógica para realizar el cambio de pantalla (hablo de juegos sin scroll).

A nivel de estructura de datos, es muy sencillo, con un simple array de estructuras se puede indicar, para una pantalla cualquiera, a qué pantalla se ha de saltar en cada unos de las cuatro direcciones (arriba, abajo, izquierda y derecha). Lo que me gustaría saber es cuál es la forma de implementar esto que resulte más eficiente. He pensado en añadir una condición extra dentro de la rutina que controla el movimiento del sprite del protagonista, de manera que comprobemos si estamos dentro de las coordenadas que representan el paso a otra pantalla, y qué típicamente tenga un fondo de puerta, agujero, etc. La pega que veo es que añadimos complejidad a las condicionales con la penalización de tiempo de CPU correspondiente. También se podría crear un tile especial que indique "salto" a otra pantalla, pero a la postre también supone añadir condiciones dentro del bucle de movimiento.

¿Qué otras formas se os ocurren?
Avatar de Usuario
na_th_an
Mensajes: 26412
Registrado: Vie, 09 Ene 2009, 12:18

Re: Formato que genera mapcnv

Mensajepor na_th_an » Mié, 22 Jul 2015, 12:49

En nuestros motores siempre detectamos los bordes de la pantalla. Cuatro comprobaciones, si las anidas, no implican tanto tiempo de proceso:

Código: Seleccionar todo

if (x está en el borde derecho) {
   if (vx es positivo) {
      if (no nos salimos del mapa) {
         pantalla ++;
      }
   }
} else if (x está en el borde izquierdo) {
...


Nosotros tenemos todos nuestros mapas rectangulares, así que es lo más sencillo (sumas el ancho para bajar una pantalla, restas uno para ir a la izquierda...).

Con un array de mapeos lo mejor es hacérselo lineal (bajo nivel, siempre al más bajo nivel), con orden por ejemplo izquierda, derecha, arriba, abajo, con una entrada por pantalla. Así, la conexión desde n_pant a la derecha estará en [(n_pant << 2) + 1]. Con asignar ese valor ya lo tienes conectado.

Para saltos en medio de la pantalla, nosotros en MK2 y la Churrera usamos el script. La forma que menos CPU implica es sencilla: exige que el jugador pulse la tecla de acción. Detectar esa pulsación es solo un if en tu bucle principal, y dentro del if ya te puedes montar la historia que quieras para conectar. Al ser una conexión, no importa que tarde un poco más porque no se notará un salto (el cambio de pantalla es relativamente lento y unos milisegundos más no se notarán).
Como diría Rorshach: "Urm..."
Avatar de Usuario
Javi Perez
Mensajes: 13
Registrado: Lun, 08 Jun 2015, 11:47

Re: Formato que genera mapcnv

Mensajepor Javi Perez » Mié, 22 Jul 2015, 13:45

na_th_an escribió:En nuestros motores siempre detectamos los bordes de la pantalla. Cuatro comprobaciones, si las anidas, no implican tanto tiempo de proceso:

Peeerfecto....condiciones anidadas, no había caído en la cuenta, así es mucho más eficiente!
na_th_an escribió:Nosotros tenemos todos nuestros mapas rectangulares, así que es lo más sencillo (sumas el ancho para bajar una pantalla, restas uno para ir a la izquierda...).

Con un array de mapeos lo mejor es hacérselo lineal (bajo nivel, siempre al más bajo nivel), con orden por ejemplo izquierda, derecha, arriba, abajo, con una entrada por pantalla. Así, la conexión desde n_pant a la derecha estará en [(n_pant << 2) + 1]. Con asignar ese valor ya lo tienes conectado.

Este es exactamente el mecanismo que he pensado, es un poco ad-hoc a bajo nivel, pero es muy rápido. En las pruebas que he hecho, pinto todo el grid de tiles (32x24) y es súper-rápido, se nota una pequeña latencia de ms, pero apenas perceptible.
na_th_an escribió:Para saltos en medio de la pantalla, nosotros en MK2 y la Churrera usamos el script. La forma que menos CPU implica es sencilla: exige que el jugador pulse la tecla de acción. Detectar esa pulsación es solo un if en tu bucle principal, y dentro del if ya te puedes montar la historia que quieras para conectar. Al ser una conexión, no importa que tarde un poco más porque no se notará un salto (el cambio de pantalla es relativamente lento y unos milisegundos más no se notarán).

Con este tipo de conectores todavía no me he metido, supongo que será tan sencillo como controlar una coordenada determinada, o un tile de "salto"...

Gracias!

Volver a “Ayuda”

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado