El siguiente esquema ilustra el funcionamiento del bucle principal del FXI [instange_go_all] en la versión 0.83:
Mientras haya activo algún proceso:
Examina todos los procesos activos:
Crea una lista (ilist)con todos aquellos que tengan STATUS igual a 2 y FRAME_PERCENT menor a 100
Inicializa las variables TYPE_SCAN y ID_SCAN (contadores para GET_ID y COLLISION)
Si la lista está vacía:
Destruye todos los procesos que tengan STATUS igual a 1
Espera hasta que sea momento de empezar el siguiente frame [gr_wait_frame]
Dibuja el siguiente frame en pantalla [gr_draw_frame]:
Recoge los eventos de teclado y ratón de SDL,
actualizando las variables globales correspondientes [do_events]
Si está activado el flag de saltar el frame actual, no hace nada más
Si está activado el flag de cambio de paleta, la actualiza [gr_refresh_palette]
Bloquea el acceso a pantalla [gr_lock_screen]. Esta función crea un bitmap "falso" cuyo puntero a datos en realidad apunta al buffer de video (modo doble_buffer o en ventana), o bien crea un bitmap en memoria para la pantalla (modo 2xscale o pantalla completa sin doble_buffer).
Dibuja el frame completo [gr_draw_screen] en el bitmap (scrbitmap) preparado por gr_lock_screen:
En el caso de que no haya un scroll a pantalla completa [gr_scroll_is_fullscreen], borra la pantalla: poniendola a negro si no hay fondo, o copiando el gráfico de fondo, que puede ser de 8 ó 16 bits.
Crea una lista de objetos a dibujar, con:
Todos los procesos donde CTYPE vale 0, su estado es 2 ó 4, y tienen un número distinto de 0 en GRAPH ó XGRAPH.
Todos los objetos registrados por DLL [gr_new_object]. Esto incluye los gráficos tipo primitiva creados como objetos [gr_drawing_new]
El dibujo de textos, como objeto único [draw_texts]
El dibujo del ratón, como objeto único [draw_mouse]
El dibujo del FLI actual, como objeto único [draw_fli]
Cualquier plano de scroll, como objeto único [draw_scroll]
Cualquier plano de modo 7, como objeto único [draw_mode7]
Ordena la lista en función de coordenada Z con un qsort
Llama a la función que dibuja cada objeto, por orden
Si hay un fade activo, actualiza su posición y opcionalmente colorea scrbitmap [gr_fade_step]
Desbloquea el acceso a pantalla [gr_unlock_screen].
En modo 2xscale, esta función mueve el contenido de scrbitmap a la pantalla real (screen) empleando la función AdMame2x y seguidamente hace un SDL_UpdateRect() de toda la pantalla.
En modo doble buffer o a ventana, se limita a desbloquear el bitmap "falso" y hacer un SDL_Flip.
En modo de pantalla completa sin doble buffer, copia el contenido de scrbitmap a screen y hace un SDL_UpdateRect. Además, en 16 bits soporta el "filtrado" -f, que mezcla cada color con el de su derecha al hacer la copia.
Resta 100 al FRAME_PERCENT de todos los procesos que tengan STATUS igual a 2
Avanza los contadores de tiempo [gr_advance_timers]
Sale del bucle principal si existe una condición de salida (ej. se ejecutó EXIT)
Vuelve al comienzo del bucle principal
Ordena la lista de procesos activos (ilist) por la variable PRIORITY
Ejecuta uno a uno los procesos, por el orden dado por la lista [instance_go]
La lista de procesos se recorre de principio a fin varias veces por
frame: una para obtener la lista de procesos a ejecutar, otra para
destuir los procesos afectados, y otra para restar 100 al FRAME_PERCENT
de los procesos. Además, la lista de procesos activos se crea y
se ordena como mínimo una vez por frame, incluso aunque no hayan
cambios respecto a la ejecución anterior.
El nuevo bucle principal se nutre de los siguientes conceptos:
Existen dos bucles de ejecución de procesos:
Un bucle primario que se ejecuta al principio de cada frame y
considera todos los procesos que existen al principio del FRAME (incluso
aquellos que no son susceptibles de ejecutarse), ejecutando los que lo
precisen. Este bucle también reduce el valor de FRAME_PERCENT.
Un bucle secundario que se ejecuta inmediatamente después y puede ejecutarse varias veces, y que sólo considera aquellos procesos que tras la ejecución de un bucle anterior este frame aún tienen tiempo de frame por agotar, así como aquellos procesos que se crean pero no agotan todo su tiempo de frame durante la primera ejecución.
Existen cinco listas globales que se mantienen como listas doblemente enlazadas. Las cinco listas son:
Lista primaria de ejecución: para el bucle primario. Contiene todos los procesos existentes ordenados por PRIORITY, incluso aquellos que estén dormidos, para que el bucle primario pueda observar que han despertado. Sin embargo, al crear un proceso nuevo [instance_new] este no se añade a la lista, ya que perturbaría la ejecución de este frame (el bucle primario sólo debe considerar procesos que existían al iniciar el frame).
Lista secundaria de ejecución: un proceso que tras ejecutarse [instance_go] no acaba con un FRAME_PERCENT de 100 o más debe volver a ejecutarse en un bucle secundario posterior, así que se añade a esta lista, manteniendo un orden por PRIORITY. Esto también incluye a los procesos nuevos, creados durante este frame.
Lista de procesos nuevos: un proceso nuevo [instance_new] se añade a esta lista desordenada en lugar de a la primaria. El bucle principal se encargará de actualizar la lista primaria moviendo el contenido de esta, antes de iniciar un nuevo frame.
Lista de procesos a matar: tanto el bucle principal como el secundario añadirán a esta lista desordenada cualquier proceso tal que, antes o después de ejecutarlo, acaba con STATUS = 1. El bucle principal hará efectiva la destrucción de los procesos que figuren en esta lista justo después de dibujar el frame actual, antes de empezar el siguiente.
Lista de objetos. No es una lista de procesos propiamente dicha, ya que puede contener objetos, textos, primitivas gráficas y objetos de DLL. Pero los procesos deben añadirse a esta lista si son susceptibles de ser dibujados. Tanto el bucle principal como el secundario deben comprobar los datos del proceso para añadirlo a esta lista, borrarlo de ella, o actualizar su posición en la misma (ante una variación de coordenada Z).
Actualmente se está dibujando cada frame entero, incluso cuando el gráfico de pantalla scrbitmap es falso y se podría mantener entre frames. Debería permitirse modificar la modalidad de funcionamiento en función de la necesidad del juego, como en DIV, soportando las variables DUMP_TYPE y RESTORE_TYPE con las siguientes combinaciones:
DUMP_TYPE = COMPLETE_DUMP ; RESTORE_TYPE = COMPLETE_RESTORE
Funcionamiento actual. Cada nuevo frame, se borra toda la
pantalla (o se copia el fondo sobre scrbitmap) y se pasan a dibujar
todos los objetos y procesos por orden de coordenada Z. Compatible con
doble buffer (dibujando directamente sobre el buffer de video).
DUMP_TYPE = COMPLETE_DUMP ; RESTORE_TYPE = NO_RESTORE
No se copia el fondo ni se borra la pantalla, y se dibujan todos
los procesos y objetos encima de "lo que haya". Esto sólo es
útil con scrolls a pantalla completa. Compatible con doble buffer.
DUMP_TYPE = PARTIAL_DUMP ; RESTORE_TYPE = PARTIAL_RESTORE
Se hace una búsqueda de rectángulos "sucios" a
redibujar... Básicamente, se trata de ennumerar los
rectángulos que contengan a cualquier proceso que haya cambiado
una de las variables, GRAPH, FILE, X, Y, SIZE, ANGLE, SIZE_X ó
SIZE_Y desde el último frame que fueron dibujados. Las DLL
deberán registrar una función que proporcione estos
rectángulos cada frame. Seguidamente, se dibujan selectivamente
sólo estas zonas (primero se borran o se copia un trozo de fondo
sobre ellas; luego se dibujan con clipping los procesos y objetos
precisos).
El mayor inconveniente es que los rectángulos no deberían
superponerse, y puede haber un número elevado de los mismos.
Para que sea compatible con el doble buffer habría que guardar
las posiciones de los procesos tal como se dibujaron por separado por
cada uno de los buffers, para poder comprobar si hay variaciones.
Habría que consultar la SDL a ver si el doble buffer nos deja el
backbuffer con los datos correctos tal como se dibujaron, o bien su
contenido es indeterminado.
DUMP_TYPE = PARTIAL_DUMP ; RESTORE_TYPE = NO_RESTORE
Igual que el paso anterior, pero no se borran los rectángulos ni
se copia sobre ellos el fondo. Sólo resulta útil en
juegos con scrolls que no sean a pantalla completa. Compatible con
doble buffer sin tener que hacer nada especial. Un juego que tenga
procesos normales y use este modo no funcionará correctamente.