Isshokuta; 20160517

El scroller.

Como este juego sólo avanza, no necesitamos liarla parda para calcular la dirección de la siguiente columna que tenemos que dibujar. El juego empezará siempre en la columna 0 y se le sumará 6 cada vez que necesitemos una nueva columna.

La dirección de la columna actual siempre irá en chunk_address. El trozo de columna que vamos a dibujar irá en col_idx, que valdrá 0-3 para las columnas de patrones y 4 para la columna de atributos. Lo mismo ocurre con la posición col_adr en los nametables. Se puede llevar en avance continuo. Llevaré una X, cunando valga 32 la pongo a 0 y cambio el nametable:

$2000, $2004, $2008, $200C, $2010, $2014, $2018, $201C -> 
$2400, $2404, $2408, $240C, $2410, $2414, $2418, $241C -> 
$2000 ...

O sea,

col_add += 4;
if (col_addr == $2020) col_addr = $2400;
if (col_addr == $2420) col_addr = $2000;

Creo que más sencillo no puede hacerse. Sí, sí que puede:

col_add = (col_add + 4) & 0xff1f;		// cicla el MSB 0-28
if (!(col_add & 0x8f)) col_add ~= 0x0400;	// Cuando el MSB es 0, cambia el bit 11

Aunque ahora no me queda claro cuál será más rápida.

OJALCUIDAO, que hay que pintar a partir de la fila 4, o sea, con un offset de 80:

$2080, $2084, $2088, $208C, $2090, $2094, $2098, $209C -> 
$2480 ... $249C -> 
$2080 ...

O sea,

col_add += 4;
if (col_addr == $20A0) col_addr = $2480;
if (col_addr == $24A0) col_addr = $2080;

Creo que más sencillo no puede hacerse. Sí, sí que puede:

col_add = (col_add + 4) & 0xff9f;		// cicla el MSB 0-28
if (!(col_add & 0x8f)) col_add ~= 0x0400;	// Cuando el MSB es 0, cambia el bit 11

No hace falta ser muy listo para saber que puedo hacer lo mismo con la dirección de los atributos. Estoy llevando demasiadas variables que tengo que tener sincronizadas para adelante, pero NECESITO que esto me entre en las 32 primeras lineas para poder tenerlo antes del split. ¿Estoy soñando?

$23C8, $23C9, $23CA, $23CB, $23CC, $23CD, $23CE, $23CF -> 
$27C8, $27C9, $27CA, $27CB, $27CC, $27CD, $27CE, $27CF
attr_add ++;
if (attr_add == $23D0) attr_add = $27C8;
if (attr_add == $27D0) attr_add = $23C8;

~~~

Aún no tengo ni idea de cómo írá el movimiento, tengo que jugar a algunos juegos de estos para verlo, pero con mi rutina de avance tendré problemas si hay desincronía con las potencias de 2 o los divisores de 8 (que son potencias de 2, es tontería). Estoy dándole vueltas sobre cómo hacerlo.

Está claro que, programáticamente “de cara al engine”, lo más cómodo es tener una cam_pos que pueda dejar quieta o actualizar a la posición “x” del jugador, si esto fuera necesario. El problema es que eso me limitaría, si no me invento algo, a que el avance del jugador tenga que ser de pixel en pixel o de 2 en 2 pixels, o de 0.5 en 0.5 píxels… divisores o multiplicadores enteros, se entiende.

Lo suyo es lograr un método en el que, dado un nuevo “cam_pos” (que debería ser siempre mayor o igual al actual), calcule el delta, haga las actualizaciones necesarias, y mueva el punto de scroll.

Teniendo en cuenta que voy a dibujar “el décimo chunk”, o sea, el que viene después de los 9 que pueden verse parcialmente en la pantalla, tendría que asegurarme de actualizar el scroll siempre que, tras aplicar el delta, cam_pos pase al siguiente múltiplo de 32.

if (cam_pos > cam_pos_old) {
	// Cambio de múltiplo de 32 si haciendo una máscara 0xffe0 los resultados son distintos
	// 0xffe0 -> 1111 1111 1110 0000. Los bits a 1 son los que cambian al cambiar de múltiplo
	if ((cam_pos & 0xffe0) != (cam_pos_old &0xffe0)) {
		// actualiza todas las posiciones.
		// ...
		// Y luego:
		col_idx = 0;
	}
	cam_pos_old = cam_pos;
}

En cada llamada a do_scroll (que es la función que hará todo esto) debe llamarse a la función que pinta la columna si es necesario:

if (col_idx < 5) scroll_draw_column ();

Bueno, creo que estoy preparado para poner todo esto en práctica, pero antes voy a hacer algo más sencillo pero necesario: rellenar las 9 columnas iniciales y empezar el proceso.

~~~

Bueno, pues ya tengo el scroll… Pero mi gozo en un pozo. Tardo más de 32 rasters (de hecho, bastantes más: 48). La cosa es que no gasto ni un ciclo en scrollear hasta que llega el momento de pintar la nueva columna. No sé si podría reoriganizar el código para hacer el trabajo más distribuido (tengo 32 frames de ventaja). Voy a darle un volt.

~~~

Con pruebas tontas he visto que si hago solo dos metatiles (en lugar de 6), aunque deje todos los atributos, el tema dura menos de 32 rasters. Tendría que hacer el triple de llamadas para construirlo todo. Lo que tengo que ver es cómo me las apaño.

0 -> inicializar columna, volcar
1 -> volcar
2 -> volcar

3 -> inicializar columna, volcar
4 -> volcar
5 -> volcar

6 -> inicializar columna, volcar
7 -> volcar
8 -> volcar

9 -> inicializar columna, volcar
10 -> volcar
11 -> volcar

12 -> atributos

HUM… ¿Y si lo hago así? Puede ser más cómodo de programar:

0 -> inicializar columna de patrones, volcar
1 -> volcar
2 -> volcar
3 -> inicializar columna de atributos

4 -> inicializar columna de patrones, volcar
5 -> volcar
6 -> volcar
7 -> volcar atributos

8 -> inicializar columna de patrones, volcar
9 -> volcar
10 -> volcar
11 -> volcar atributos

12 -> inicializar columna de patrones, volcar
13 -> volcar
14 -> volcar
15 -> volcar atributos

Son 16 frames, pero como voy 32 por delante puedo permitírmelo. Creo que lo voy a probar…

~~~~

Después de MÁS DE UN dolor de cabeza, ¡lo tengo! Se ejecuta en menos de 32 rasters (¡muchos menos, guay!), por lo que puedo meterlo antes del split. Mi picha es magna. Jodó, voy a celebrarlo.

Pero antes voy a hacer el split para demostrar que de verdad funciona 🙂

~~~~

El split funciona, pero hay un glitch muy feo nada más empezar. La pantalla “salta” o “parpadea” por un momento, como si me estuviera colando de tiempo por alguna razón. No sé si me faltará actualizar algo…

Aunque me cargue el bucle principal que hace el scroll y el split, me pega el glitch, así que debe ocurrir en la función de inicialización.

Vale – creo que estoy mandando demasiada mierda en el update list. Probablemente lo esté petando. Mando (3+8)*3 + 3*6 = 51 bytes. Está claro que 51 bytes son muchos.

Um – no parece ser eso. Si parto la carga y hago más updates sigue pasando – además que sólo pasa al final, cuando toda la pantalla se ha dibujado. Jodó, qué raro. Verás cuando dé con lo que es y vea que es una puta tontería.

~~

Ya en casa, he visto que me hace un glitch al avanzar a la parte del mapa que no hay nada porque el split ocurre unas cuantas lineas tarde. Voy a hacer pruebas subiendo la colisión con el sprite 0 a ver qué tal se da. Para ello, subo el pixel que hay en el tile 63 unas cuantas posiciones, y subo la “y” del sprite 0 la misma cantidad.

~

Perfect, subiendo un par de pixels se soluciona el tema que te quema. Ahora otra cosa…

sprites

Por un lado, los metasprites tienen diferentes tamaños. Por otro, aunque he intentado alinear a tile al máximo, hay un par de frames en los que esto me ha resultado imposible. No sé muy bien qué hacer para aprovechar al máximo los patrones que tengo disponibles. La protagonista tiene un montón de frames (y me queda por hacer el salto y la patada voladora) y tiene que quedar sitio para enemigos… Voy a examinar a ver qué hago. Montar los metasprites a mano es algo que quiero evitar por todos los medios.

No sé si al final modifiqué mkts para que obvie las “casillas” totalmente vacías y no las añada a los metasprites. Esto es primordial si quiero minimizar parpadeos / desapariciones y sobre todo para no quedarme sin sprites.

Voy a ponerme a mirar los gráficos que hice largo y tendido.

~~

Creo que por el momento voy a confiar en el conversor automático para esto, la mayoría están super bien alineados y en muchos casos hay uno o dos sprites máximo en linea, aunque ¡5! en el peor caso.

Una cosa que me viene dando por culo desde hace tiempo es encontrar la forma de que los sprites individuales (no los metasprites) se envíen a la OAM en un orden diferente cada vez. En Super Uwol lo hago a nivel de metasprites porque es suficiente, pero en un juego así lo suyo es que sea a nivel de sprites.

Lamentablemente mi kung fu no es lo suficientemente bueno, porque esto debería poder hacerlo en la propia OAM… Y además, saltándome el sprite 0, que tiene que estar fijo para hacer el split.

Poniéndonos a divagar, si tuviese un contador interno que sumase 32 bytes en lugar de 4 cada vez que se añade un sprite, haciendo módulo 252 (63 sprites), lo tendría hecho. La cosa sería tener una especie de “oam_meta_sprites” alternativo que haga esta parafernalia. ¿Podré hacerlo? ¿La liaré? voy a mirar el código de neslib…

Antes de mirar, el principal problema va a ser el módulo, ya que tendré probablemente un byte para apuntar (255 máximo) y estoy sumando 32 cada vez.
256-32 = 224. Si la posición actual es > 192, tengo que irme a saturar. Pensemos. No es tan difícil. ¿Dónde están mis conocimientos de álgebra cuando los necesito?

224->4, 228->8, 232->12, 236->16, 240->20, 244->24, 248->28, 252->32

En este *caso concreto* creo que me valdría con hacer esto (pseudocódigo):

if N < 224 Then N = N + 32 else N = N - 220

En realidad el código del nuevo oam_meta_sprites_ooo (out of order) sería igual, con la salvedad del incremento. Veamos el código de neslib.

Veo que después de hacer sus fullerías incrementa el registro X 4 veces:

	inx
	inx
	inx
	inx

Creo que el registro X apunta a la posición en la OAM, voy a asegurarme . . . Sí, así es.

La cosa que me raya es que no sé qué carajo hace al final, cuando ha terminado de hacerlo todo (cuando encuentra 0x80 en la estructura). En este caso salta a @2 y hace esto:

@2:

	lda <sp
	adc #2			;carry is always set here, so it adds 3
	sta <sp
	bcc @3
	inc <sp+1
@3:

	txa
	rts

Veo que le suma “con carry” el valor 3 a algo… pero ¿a qué?

Probablemente sean fullerías para volver limpiamente de forma compatible con cómo la lía cc65 con su propio stack pointer (que es adonde apunta “sp”, a la pila “por software” que mantiene cc65). Voy a ver si esto aparece en otros sitios. Miraré otras funciones que tengan muchos parámetros, como oam_spr . . . Sí, eso es. En oam_meta_spr, suma 5 porque tiene 2 bytes más de parámetros.

Ole la ingeniería inversa.

Entonces creo que podría copiarla de patilla sencillamente cambiando el incremento X+=4 por la mierda de arriba. Otra cosa es que me salga, voy a buscar un tutorial rápido de 6502…

Luego haré dos bolas de sprites para probar a ver si es verdad que funciona. Como funcione, la lío.

~~~

Al final he escrito este chorapio:

	; and here is my modification:
	; if N < 224 Then N = N + 32 else N = N - 220

	txa				; a = x
	cmp #224		; if A < 224, bcc will branch
	bcc @2
	; did not branch, thus we are in the ELSE
	sec
	sbc #220
	jmp @3
@2:
	; did branch, so we are in the THEN
	clc
	adc #32
@3:
	tax 			; result back in X 

	; End of my modification

Que sabe dios la que liará. Lo probaré. Voy a generar dos metarsprites muy anchos y sentarme a ver como parpadea todo. Vamos al plan.

0 1 2  3  4  5  6  7
4 8 12 16 20 24 28 32 ... 36 ->0

Nop. La lía con el split por alguna misteriosa razón. ¿Estaré afectando a otras partes del código con mi mierda?

~~

Vale, el oam_clear. Tengo que recrear mi sprite 0…

~~

Funciona, pero no me gusta el efecto. El parpadeo es muy lento, no consigo encontrar los valores idóneos. Lo dejo aparcado por ahora. Bonita pérdida de tiempo. Pero he programado en 6502. Otra cosa es que haya sido para nada, pero mi programa era correcto XD

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s