Memento. Work in mucho process.

Started by Fede, June 20, 2016, 08:56:08 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Fede

Os he hablado de que yo también quiero hacer un juego con tiles. Pues si es así, ni me acuerdo. :)

Lo primero: Perdoooon.

La explicación que di en el otro hilo de la forma en que hago un tile-scroll no es la que utilizo.

Creo recordar que tras hacer varias pruebas, la complejidad que alcanzaba hacer una pantalla tan sólo pintando los bordes no merecía la pena en fps. Así que opte por la sencilla.

La cabeza la tengo fatal.

Uso el plan 'B'.

Pantalla virtual + repintada completa por cada ancho de tile.

O sea, que uso una pantalla virtual más grande que la real y voy haciendo scroll.
En el momento que calculo que nos vamos a salir de la pantalla virtual, la repinto y empiezo de nuevo.

Y disculpadme de nuevo, que esto lo programe a finales del 2011.

Los dos funciones meollo de todo esto son:


function ActualizaPantallaVirtual (int iTileX, int iTileY)
/*
Función que actualiza la pantalla virtual en el mapa iIdePantallaVirtual.

Parámetros:

iTileX: Coordenada X dentro del mapa de tiles del primer tile a dibujar.
iTileY: Coordenada Y dentro del mapa de tiles del primer tile a dibujar.
*/

private
    int iContadorX;
    int iContadorY;
    int iTileADibujar;
    int iMedioAnchoTile;
    int iMedioAltoTile;
end

begin
    iMedioAnchoTile=iAnchoTile/2;
    iMedioAltoTile=iAltoTile/2;
    //Borramos la pantalla virtual.
    map_clear(iIdeFpg,999,rgb(0,0,0));
    //La repintamos.
    iContadorX=0;
    //z=0;
    //MapPut dibuja un gráfico dentro de otro. Influye la z del primer gráfico. Como es un scroll, influye la del scroll.
    while (iContadorX<iTilesX)
        iContadorY=0;
        while(iContadorY<iTilesY)
            //Calculando tile a dibujar.
            iTileADibujar=iATmfMapa[iTileY+iContadorY][iTileX+iContadorX];
            if (iTileADibujar>0)
                Map_put(iIdeFpg,999,iTileADibujar,(iContadorX*iAnchoTile)+iMedioAnchoTile,(iContadorY*iAltoTile)+iMedioAltoTile);
            end
            iContadorY++;
        end
        iContadorX++;
    end
end



function PintaPantalla(int iCoordenadaX, int iCoordenadaY)

/*
Dibuja la pantalla que pertenece a las coordenadas iCoordenadaX, iCoordenadaY del mapa de tiles.
Centra la pantalla en esas coordenadas.
*/
private
    int iTileX;
    int iTileY;
end

begin
    if (iCoordenadaX<cAnchoPantalla/2)
        iCoordenadaX=cAnchoPantalla/2;
    end
    if (iCoordenadaY<cAltoPantalla/2-iAltoTile/2)
        iCoordenadaY=cAltoPantalla/2-iAltoTile/2;
    end
    if (iCoordenadaX>iAnchoMapa-cAnchoPantalla/2)
        iCoordenadaX=iAnchoMapa-cAnchoPantalla/2;
    end
    if (iCoordenadaY>iAltoMapa-cAltoPantalla/2-iAltoTile/2)
        iCoordenadaY=iAltoMapa-cAltoPantalla/2-iAltoTile/2;
    end

    iTileX=(iCoordenadaX/iAnchoTile-cAnchoPantalla/iAnchoTile/2);
    Scroll[0].X0=iCoordenadaX%iAnchoTile;
    iTileY=(iCoordenadaY/iAltoTile-cAltoPantalla/iAltoTile/2);
    Scroll[0].Y0=iCoordenadaY%iAltoTile;
    ActualizaPantallaVirtual(iTileX,iTileY);


Y el resultado es más o menos el ejemplo que acoplaño.

Los tres números que veis son fps y coordenadas.

Se mueve con los cursores.

El mapa es original del Echo. Con permiso del autor. :D
Si quieres ser sabio, aprende a interrogar razonablemente, a escuchar con atención, a responder serenamente y a callar cuando no tengas nada que decir.

Fede

#1
¡ANDA! Si ya tenía abierto otro hilo.  :P

http://forum.bennugd.org/index.php?topic=2936.0

Veis. Lo que yo decía, principios del 2012.

Joder como pasa el tiempo.

Y ya encontré lo que faltaba.

http://forum.bennugd.org/index.php?topic=3799.msg63002#msg63002

Pues eso, que cuando termine con mi editor y con el editor de mapas de tiles. Continuaré.  :P
Si quieres ser sabio, aprende a interrogar razonablemente, a escuchar con atención, a responder serenamente y a callar cuando no tengas nada que decir.

Drumpi

¡Pero qué es esto! Usando mapas sin permiso. Te vas a cagar cuando mis abogados te metan un puro por usar mis mapas con motivos educacionales y sin ánimo de lucro como pone en la licencia de us...
Drumpi ¿has visto mis pastillas? :D

No está mal como táctica, pero ya sabes que cualquier función PUT es lenta. Más o menos he entendido lo que haces -mentira, sólo te has leido el código por encima-, pero un refresco completo no sé si es buena idea, aunque uses un mapa más grande que la pantalla y/o lo refresques entero tras cada desplazamiento de fila o columna (no sé si lo has probado en Wiz, pero sé que un PUT a pantalla completa es insostenible por encima de los 30fps).

Algunos consejos:

- No metas el mapa del tamaño de la ventana en el FPG, créalo en runtime con new_map y lo añades al FPG con FPG_ADD, aunque luego no lo salves (aunque no recuerdo si se salvaba automáticamente). Si no, siempre puedes crear un array de 999 ints y hacer map_clone de cada mapa para poder usar MAP_PUT.

- Esto te va a encantar, aun lo estoy perfeccionando para mi motor: crea un array bidimensional con las mismas posiciones que tiles tienes en ese mapa del tamaño de pantalla, y almacena qué tile tienes en cada posición. Cuando refresques la pantalla, en lugar de borrar toda la pantalla comprueba si ese tile cambia respecto a la info del array: si cambia haces un MAP_PUT con el flag NO_COLOR_KEY (para que pinte los pixels transparentes también), y si no cambia, no lo tocas y trabajo que ahorras. Y si tienes que borrar un tile, usas DRAW_BOX con color 0. Quieras que no, puede ahorrarte la mitad del pintado.

¡Drumpi! ¡Y las pastillas!
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

Fede

Quote from: Drumpi on June 21, 2016, 12:11:31 AM
No está mal como táctica, pero ya sabes que cualquier función PUT es lenta. Más o menos he entendido lo que haces -mentira, sólo te has leido el código por encima-, pero un refresco completo no sé si es buena idea, aunque uses un mapa más grande que la pantalla y/o lo refresques entero tras cada desplazamiento de fila o columna (no sé si lo has probado en Wiz, pero sé que un PUT a pantalla completa es insostenible por encima de los 30fps).

No hago un PUT a pantalla completa, tan sólo lo hago en la pantalla virtual cuando la tengo que repintar. La pantalla la repinta Bennu automáticamente.
Y por ahora en la Wiz va bien. Ya veremos cuando empiece a añadir millones de enemigos. :)
Pero esto ya lo tengo pensado: Los enemigos no se moverán si no estás cerca de 'x' tiles.

Quote from: Drumpi on June 21, 2016, 12:11:31 AM
Algunos consejos:

- No metas el mapa del tamaño de la ventana en el FPG, créalo en runtime con new_map y lo añades al FPG con FPG_ADD, aunque luego no lo salves (aunque no recuerdo si se salvaba automáticamente). Si no, siempre puedes crear un array de 999 ints y hacer map_clone de cada mapa para poder usar MAP_PUT.

Creo que eso es lo que hago...

La pantalla virtual la creo en tiempo real en el gráfico 999. Yo trabajo sobre este gráfico y Bennu se encarga del refresco.

Quote from: Drumpi on June 21, 2016, 12:11:31 AM
- Esto te va a encantar, aun lo estoy perfeccionando para mi motor: crea un array bidimensional con las mismas posiciones que tiles tienes en ese mapa del tamaño de pantalla, y almacena qué tile tienes en cada posición. Cuando refresques la pantalla, en lugar de borrar toda la pantalla comprueba si ese tile cambia respecto a la info del array: si cambia haces un MAP_PUT con el flag NO_COLOR_KEY (para que pinte los pixels transparentes también), y si no cambia, no lo tocas y trabajo que ahorras. Y si tienes que borrar un tile, usas DRAW_BOX con color 0. Quieras que no, puede ahorrarte la mitad del pintado.


Al principio me areció una chorrada, pero luego te lo he pillado.  ;D

Lo estas utilizando para tener tiles animados. ¿A que sí?  ;)

Lo que pasa es que yo no me lo he planteado de esta forma, pero me lo apunto.

Por ahora los tiles animados son procesos independientes que van con la pantalla.

Por ejemplo: Si quisiera dibujar una cascada, pues haría un 'enemigo' que no hace nada, sólo está ahí para verlo y ni ataca ni se defiende.


Y muuuuchas gracias. ¡Karma Up!
Si quieres ser sabio, aprende a interrogar razonablemente, a escuchar con atención, a responder serenamente y a callar cuando no tengas nada que decir.

Drumpi

No, no. No son para tiles animados.
Me refiero a que cuando quieres refrescar la pantalla de tiles, primeros haces un clear_map y después pintas los tiles.
Yo te recomiendo que no uses clear_map, sino que sepas qué tile tienes pintado en casa parte del mapa 999 mediante un array bidimensional, y en vez de clear_map, vayas tile por tile a ver si el que tienes que poner es diferente al que tenías. Que lo es, pues lo cambias, que no, pues lo dejas.

Ejemplo: imagina que tienes este mapa
0 0 0
0 0 1
1 1 1

y lo mueves a la izquierda un tile para que te quede así

0 0 0
0 1 1
1 1 1

Si borras y vuelves a pintar, tienes que repintar 5 tiles ( (1,1), (2,1), (0,2), (1,2) y (2,2) ) que son 6 operaciones (redondeando, porque el clear_screen fácilmente equivale por 3 PUTs)
Si comparas el array anterior con el actual sólo cambia 1 tile (el (1,1)), así que haces un único put y te ahorras 5 operaciones PUT (son 9 IFs en un doble bucle, pero siempre son menos costosos que los PUT).
Y si el cambio fuera al revés, sustituir el tile (1,1) por el tile 0, haces un DRAW_BOX en esa posición con el color 0 y de nuevo sólo es una operación.

Para tiles animados creo como tu, que lo suyo sea un proceso aparte, que se cree cuando el scroll lee esa posición (borrando el dato en el mapa, para que no se vuelva a crear en el siguiente frame), y que se borre al salir de pantalla (volviendo a modificar el mapa para que aparezca de nuevo cuando la cámara se acerque). En mi caso, en el Echo, si el tile es <150, el motor crea un tile normal, pero si no, creará un enemigo, un tile animado, un botón... lo que sea, y el propio objeto pone esa casilla del mapa a 0 hasta que sale de pantalla, que vuelve a poner su valor >150 en dicha posición. No sé si me explico.



Lo que no termino de pillar es tu "scroll". Pensaba que hacías un map_block_copy una línea más hacia arriba/abajo, y luego pintabas lo nuevo con write o write_in_map.
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

Fede

¡Ahora sí!

Ya te he entendido. Parece fácil de implementar y algo se rasca. Voy a añadirlo al algoritmo de scroll tileado.
......

Voy a volver a explicar la manera en que trabaja mi TileScroll.

Trabajo con una pantalla real de 320x240 pixeles.

Si mis tiles miden 16x16 pixeles, hacen 20 tiles de ancho por 15 tiles de alto.

Creo en tiempo real el gráfico 999 en el fpg de turno que mide lo mismo que la pantalla más 1 tile por cada lado. Esto es 22 tiles de ancho por 17 tiles de alto.

Este gráfico lo muevo en pantalla con la orden scroll, de tal manera que...

Si andas hacia un lado de la pantalla tengo 16 pixeles (1 tile) para scrollear. Lo mismo para el otro lado, para arriba y para abajo.

Esto me da que hasta que no hayas avanzado 16 píxeles en cualquier dirección, no es necesario repintar la pantalla virtual. Y mientras te muevas en el rango de los 352x272, que es lo que mide la pantalla virtual, no tengo que repintar nada. Todo el trabajo lo hace el scroll.

Según esto, en un mapa de 300 tiles de ancho, si te mueves de una punta a la otra, tendría que dibujar la pantalla virtual sólo 300 veces. El resto del trabajo lo hace scroll.

Creo recordad que es por esto que descarté sólo dibujar los bordes del scroll, porque al ser tan pocos 'repintados', la complejidad del código no me suponía muchas mejoras en fps.

Si aumentamos el borde, por ejemplo a 2 tiles por cada lado, pues necesitaríamos dibujar la mitad, 150 veces. Pero ocuparía más memoria el gráfico 999. Eso ya es cuestión de gustos.

A ver si ahora se me ha entendido. :)

Si quieres ser sabio, aprende a interrogar razonablemente, a escuchar con atención, a responder serenamente y a callar cuando no tengas nada que decir.

Drumpi

Ajá, vale, eso es simple y sencillo.
Lo que pasa es que si avanzas esos 16 pixels lateralmente, por ejemplo, entonces refrescas toda la pantalla ¿verdad? Esa es la parte que no me gusta.
Con lo que te he dicho el algoritmo gana algo de eficiencia, pero sigue sin ser tan óptimo como debiera. Lo cierto es que probablemente sea más rápido que mi método actual, la verdad ^^U Pero ya entramos en temas estadísticos y yo de eso (por suerte o por desgracia) poco se.

A ver si puedo reimplementar mi nuevo motor de scroll tileado y le saco ese 33% de velocidad que estimo mediante el cálculo "me lo he sacado de la manga".
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

Fede

 Hombre, se puede optimizar más.
Utilizando tú método y el de sólo pintar la fila y la columna correspondiente, en vez de pintar toda la pantalla, pintaríamos sólo un puñado de tiles.
A la hora de pintar la fila, además miraríamos si de verdad hace falta repintar el tile y así tenemos un repintado mínimo para muchos casos.
Avanzando en diagonal, y en el peor de los casos, pintaría 39 tiles.
Si tenemos en cuenta que se van a repetir muchos, esto bajaría drásticamente con tu método.

Pero... ¿merece la pera tanto código? Cuando tenga ganas voy a echarle un rato, que me estás picando. :D


Si quieres ser sabio, aprende a interrogar razonablemente, a escuchar con atención, a responder serenamente y a callar cuando no tengas nada que decir.

Drumpi

Hombre, es que no pintas sólo una columna... tendrás que desplazar en algún momento todo lo que tienes pintado en alguna dirección, para dejar sitio a la nueva fila/columna ¿no?
Ya te digo yo que con lo del array ya ahorras muchos cálculos, especialmente en los casos que indicas. Y sí, merece la pena si quieres que funcione en WIZ. Si es para PC ni te molestes, porque te sobra potencia por las orejas, pero como te metas en alguna máquina de rendimiento limitado, cualquier optimización es bienvenida.

No te he querido liar más, pero para aumentar el rendimiento (aun más) se me ocurrió ¿y si el mapa fuera cíclico? es decir, que lo que se salga por la derecha de la pantalla apareciera por la izquierda.
Ejemplo con tu mapa de 20x15 tiles: si movemos el mapa a la izquierda 16 pixels, es decir, 1 tile de ancho, esos primeros 16 pixels se pondrían a la derecha del todo, y podemos usar la primera columna del graph 999 para pintar los tiles de la 21ª columna.

¿Cómo podríamos hacer esto? Pues sencillo: usando Start_scroll con este mapa y los flags de "locking indicator":
http://wiki.bennugd.org/index.php?title=Start_scroll

Y con eso ya tienes lo que llevo tiempo intentando hacer en mi nuevo motor de scroll tileado :D :D :D
Yo no lo he conseguido porque quiero que el motor permita que el propio mapa de tiles también pueda ser cíclico a petición del usuario, y calcular el offset de la cámara, del gráfico y del mapa de tiles es una locura, y el funcionamiento de la operación módulo con números negativos no ayuda (no, no es que esté mal, sino que siempre valen positivos y en el espacio negativo valen 1 menos de lo que valen en el espacio positivo)... Pero esto son todo pájaras mías que aun le estoy dando vueltas.
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

Fede

Estooooo. Daba por hecho que estabamos hablando de un scroll cíclico. Por eso te decía que la complejidad del código no merecía la pena.


El primer intento fue eso: Scroll ciclico donde dibujaba sólo la fila o columna que necesitase. Y me dio tal dolor de cabeza intentar calcular donde queria dibujar teniendo encuenta que el dibujo estaría "scrolleado" que lo dejé.


Me dije que tenía tela y el beneficio era poco.


Lo que pasa es que con tu método ya se gana más, y por eso le voy a dar otra oportunidad a ver si soy capaz.
Si quieres ser sabio, aprende a interrogar razonablemente, a escuchar con atención, a responder serenamente y a callar cuando no tengas nada que decir.

Drumpi

Ah, ok ^^U
Por si te interesa, creo que hace tiempo publiqué el código en el foro... No sé dónde, pero lo publiqué. Funcionar funcionaba... salvo que en determinadas diagonales hacía algo raro y provocaba que hubiera un "desplazamiento extra" en una de las coordenadas, y el scroll se veía como "cortado".
Pero sí, en ese caso el código es extremadamente complejo, es muy fácil tener bugs, hay que tener las ideas muy muy claras y dedicarle horas a buscar bugs... y eso es lo que me toca hacer en las próximas semanas ^^U
Ya te contaré.

Suerte con tu código!!!
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

Fede

Bueno. Estoy haciendo pruebas.

Por lo pronto he encontrado un error y mi Tile-scroll que repintaba la pantalla cada vez que se llegaba al final del tile del final de la pantalla (valga la redundancia) hubiese que pintar o no.

Para solucionar esto he ampliado el marco en un tile, con lo que ahora hay dos tiles alrededor de la pantalla.Y esto ha hacelerado bastante el scroll.

He descubierto que map_xput es sensiblemente más lento que map_put. Con lo que si hay que utilizarlo es lo comido por lo servido, e incluso menos.

Y la verdad, llevo varios días haciendo pruebas y no consigo mejoras que merezcan la pena.

...Pero aún no he terminado. :)

Si quieres ser sabio, aprende a interrogar razonablemente, a escuchar con atención, a responder serenamente y a callar cuando no tengas nada que decir.

Fede

Bien, voy obteniendo resultados.

Ya he conseguido implementar una rutina de scroll tileado mejor que la anterior.

Realiza lo siguiente:

-Tiene un marco de dos tiles alrededor de la pantalla.
-Sólo dibuja cuando hemos entrado en el tile del borde exterior del marco.
Si nos movemos dentro del margen de dos tiles, no repinta, sólo scrollea.
-Esto hace que en un mapa de 300 tiles de ancho, para ir de una punta a la otra, repintaría un total de 150 veces. (Si no tenemos en cuenta los bordes y los márgenes).
-Pero además comprueba que el tile a dibujar no esté ya en pantalla a la hora de pintar. Por lo que no repinta la pantalla entera.

Acompaño un ejemplo con un mapa de mi cosecha de 300x300, donde moviéndome aleatoriamente con los cursores, podéis ver la cantidad de tiles que debería de haber pintado, y las que reamente ha pintado usando la detección de tiles ya pintados.

(Se mueve con cursores, se finaliza con escape, y devuelve el resultado en un say).

Como ejemplo a mi me ha dicho:
De 1.181.100 tiles a dibujar, tan sólo ha dibujado 276.026.
O sea, un 76,6 % menos del total de tiles.

También es verdad que el mapa es muy repetitivo.

La única pega es que no podemos usar como color el 0,0,0 en el tile, pues si se transparenta ya no funciona. Esto es porque uso map_put, que es más rápido que map_xput.

Supongo que la cuestión sería elegir el tipo de algoritmo dependiendo del tipo de tilescroll.

Ahora voy a ver si le meto mano al scroll cíclico. :)
Si quieres ser sabio, aprende a interrogar razonablemente, a escuchar con atención, a responder serenamente y a callar cuando no tengas nada que decir.

Drumpi

Genial. Cuando pueda probarlo te cuento.
¿Cuánta diferencia hay entre usar map_put y map_xput? a lo mejor puede ayudarte draw_box con el color 0, pero entonces ya son dos llamadas y puede ir más lento. ¿Y map_xput con NO_COLOR_KEY no va más rápido?
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

Fede

Bueno chicos....

¡Estoy desesperado!  :-[

De entrada sé que mi cabeza no da para mucho, pero esto me está sobrepasando.

Llevo desde que lo anuncié, intentando realizar un Tile Scroll circular... ¡Y no hay forma!

Vale que mi manera de programar es muy poco ortodoxa: me hago una idea en mi cabeza e intento plasmarla en un algoritmo. Pero es que no hay modo.

Llevo varios día rozando el éxito con los dedos, pero siempre se descuadra.

Y es que pensar en 'recircular' se me escapa.

En fin. Ya he cambiado cuatro veces como enfocar el problema... Vamos a por la quinta.  :P

Si quieres ser sabio, aprende a interrogar razonablemente, a escuchar con atención, a responder serenamente y a callar cuando no tengas nada que decir.