Hoy os presento un nuevo capítulo sobre DIV2 Games Studio, un entorno de programación de videojuegos para MS-DOS lanzado en 1999 (y su precuela un año antes) que fue bastante popular en España, en buena parte porque se vendía en los quioscos a un precio muy asequible. Pero el de hoy no es un capítulo cualquiera: Es, probablemente, el proyecto más ambicioso que he traído al blog hasta la fecha.
Como sabéis, DIV2 traía de serie una colección de "modos" de pantalla. Por un lado teníamos el modo 7, que abatía un plano y dibujaba sprites escalados por la distancia (truco empleado en juegos como Super Mario Kart); por otro teníamos el modo 8, que permitía moverse por mapas de sectores planos (`.WLD`) que simulaban un entorno tridimensional, al estilo de los FPS pre-3D (como el primer Doom). Y ya está, eso era lo más parecido a las 3D que podíamos hacer hasta la fecha en DIV Games Studio: Nada de rampas, pendientes, personajes poligonales, etc.
Siempre me quedé con la espinita de hasta dónde se podría empujar el motor… y de ahí ha salido esto: una DLL que añade a DIV2 un nuevo modo que he bautizado como Modo 9, capaz de cargar y recorrer escenarios y objetos tridimensionales de verdad, modelados, por ejemplo, en Blender y exportados como ficheros OBJ. Y podéis descargarla libremente desde mi GitHub e incluso ojear su código fuente (https://github.com/LeHamsterRuso/Mode9/tree/main/BIN).
¿Qué es exactamente el Modo 9?
Podéis ver el Modo 9 como el hermano mayor del modo 8. Donde el modo 8 te daba sectores planos y en un formato propietario (propiedad de Hammer Technologies), el modo 9 te deja cargar cualquier tipo de malla 3D: paredes, suelos, techos y rampas con cualquier inclinación. La cámara es un proceso normal de DIV2 (lo mueves con `advance()`, lo giras cambiando su `angle`, igual que en el modo 7 y 8), así que puedes ponerla en primera persona, en tercera persona siguiendo al personaje, en vista cenital o donde se te ocurra.
Y sobre ese escenario 3D puedes colocar tus personajes de tres maneras:
Como billboards planos (sprites 2D que se escalan con la distancia y siempre miran a la cámara, igual que el modo 7). Perfectos para antorchas, árboles o carteles.
Como billboards con perspectiva (objetos con una serie de sprites donde el gráfico cambia según el ángulo). Perfecto para personajes (es por ejemplo el tipo de billboards que vemos en juegos como Daggerfall, Doom o el Super Mario Kart de SNES).
Como objetos 3D de verdad, mallas OBJ que puedes rodear y ver desde todos los ángulos, con soporte incluso para animación por vértices (vertex morphing).
Por debajo lleva todo lo que cabe esperar de un motor de la época: gráficos que recuerdan al 3D de la época de la PSX, niebla por distancia, cielo panorámico de 360º, z-buffer, colisiones con deslizamiento, subida automática de escalones y rampas, downscale opcional (baja la resolución nativa del 3D) para ganar velocidad e incluso split-screen a cuatro cámaras. Todo en una DLL compilada con Watcom C++ 10.6, como mandan los cánones.
Cosas a tener en cuenta:
Cuanto menos polígonos tenga tu escenario y objeto 3D, mejor. Intenta no usar modelos de más de 400 polígonos... la librería lo permite, de hecho permite modelos de hasta 8k caras, pero ten en cuenta que un modelo de varios miles de vértices puede hacer que tu juego vaya terriblemente lento.
Los modelos deben de exportarse con caras trianguladas, forward axis -Z y up axis Y.
Los materiales (texturas) no deben de exceder los 1024x1024 (y a ser posible que tenga un tamaño de potencia de 2, como 128x128, 256x256, 512x512, etc). Además debes de convertirlos a formato MAP a 256 colores (el formato típico de DIV y DIV2).
El cielo panorámico no debe de exceder los 4096 x 2048 píxeles.
¿Cómo se usa? El esqueleto mínimo
Una cosa que no shace falta es declarar un typo de proceso c_m9. Por desgracia este tipo de estructura no se pueden inyectar directamente desde las DLL de DIV2 y toca inicializarla en cada proyecto que use esta librería. Con este ejempo, podemos arrancar un mundo 3D cabe en unas pocas líneas (recuerda copiar la DLL en la carpeta DLL que hay dentro de DIV2):
angle=-90000;// mirando hacia el porton del castillo
m9.camera=id;// Este proceso es la cámara
m9.height=160;// altura del ojo (uds DIV2)
m9.angle=0;// pitch (0 = horizonte)
START_MODE9(id,OFFSETm9.z,0);
LOOP
if(key(_w))advance(20);end
if(key(_s))advance(-20);end
if(key(_a))angle+=4000;end
if(key(_d))angle-=4000;end
if(key(_esc))break;end
FRAME;
END
END
Como veis, la sintaxis recuerda mucho al modo 8 y en este ejemplo cargamos como escenario un fichero 3D en formato OBJ, le aplicamos un material por defecto, le aplicamos una textura de cielo panorámico y nos desplazamos por el escenario con los típicos controles de WASD. A partir de ese punto, mover la cámara es exactamente igual que mover cualquier proceso de DIV2. Pero lo mejor es verlo crecer paso a paso, que es justo lo que hacen los siguientes ejemplos.
Los ejemplos, uno a uno
He preparado seis programas (ficheros PRG) que van añadiendo una sola cosa nueva cada vez, de modo que podáis parar en el que queráis sin miedo a romper nada. Todos comparten el mismo escenario: un castillo con su patio, una escalera, un lago alrededor de la isla y un cielo panorámico.
MODE9_01.PRG · Un mundo vacío para pasear
Este fichero es ll punto de partida: Cargamos el castillo con `LOAD_WLD9()`, le asignamos sus tres texturas por material (`MODE9_MATERIAL` para la escalera y el agua, ya que `CASTLE.OBJ` trae tres `usemtl`: castillo, escalera y lago) y arrancamos el render.
La cámara incluye lo que se espera de un FPS clásico: movimiento con WASD, desplazamiento lateral, ratón para girar y mirar arriba/abajo, salto con gravedad y agacharse. Aquí ya se aprecian las colisiones con deslizamiento y el `MODE9_STEP(70)` subiendo la escalera del patio sin que tengamos que programar nada más.
Cosas a tener en cuenta: La librería permite desactivar y activar las colisiones, de forma que puedas traspasar paredes o incluso golpearte con el techo. No obstante, la gravedad (el hecho de caer hacia abajo) debe de implementarse en el propio PRG.
Como apreciaréis, he inundado el código con comentarios explicando qué hace cada nueva función de este modo. Por cierto, si pulsáis multiples veces espacio podréis "volar". Es un bug del PRG que he mantenido porque en cierta forma sirve para explorar y ver el escenario desde el cielo.
SCREEN_X=320;// Ancho de la resolucion de la pantalla
SCREEN_Y=200;// Alto de la resolucion de la pantalla
GAME_FPS=60;// FPS objetivo
ADV_UNIT=30;// velocidad de paso (uds DIV2)
c_m9=200;// c_type "logico" del modo 9 (convencion, ver cabecera)
GLOBAL
STRUCTm9// STRUCT de camara del modo 9 (mismos campos que m8). &m9.z = base del struct.
z;
camera;
height;
angle;
END
scr_x;// resolucion real (la lee la DLL): raton/camara a
scr_y;// cualquier resolucion, no solo 320x200
BEGIN
// Es probable que los assets tarden en cargar en hardware real.
// Si te salta la expcion 142, puedes decomentar esta linea para ignorarla.
//ignore_error(142);
set_mode(SCREEN_X*1000+SCREEN_Y);
set_fps(GAME_FPS,0);
scr_x=MODE9_SCRW();// MODE9_SCRW() y MODE9_SCRH() se pueden llamar en cualquier momento, no solo al inicio, por si quieres cambiar la resolucion con set_mode durante el juego.
scr_y=MODE9_SCRH();
load_fpg("FPG/MODE9.FPG");// cargar el FPG del juego
// --- Cargar el escenario 3D (UNA sola llamada: malla + textura + escala) --
// CASTLE.OBJ tiene 3 materiales por 'usemtl' (en orden): 0=Castle, 1=Stairs,
// 2=Water. MODE9_MATERIAL(idx, map) asigna una textura a cada uno. El 0 ya lo
// puso LOAD_WLD9 (CASTLE.MAP); aqui ponemos la escalera y el mar. Si un .MAP
// no existe aun, ese material cae al 0 (CASTLE.MAP) sin fallar.
MODE9_MATERIAL(1,"MAP/STAIRS.MAP");// peldanos de la escalera del patio
MODE9_MATERIAL(2,"MAP/WATER.MAP");// agua que rodea la isla
// MODE9_CULL(1) activa el backface culling (no dibujar las caras que se ven por detras; mas rapido).
// Por defecto está activado. Si deseseas desactivarlo haz MODE9_CULL(0);
//MODE9_CULL(0);
// MODE9_FOV cambia el campo de vision horizontal (FOV) en grados.
// El valor por defecto es ~71, que es el FOV "canonico" de los juegos 3D de la época
// Un valor menor (60) hace zoom, y un valor mayor (90 o 110) da un efecto de gran angular.
// Cambiar el FOV puede afectar la sensacion de velocidad y la distorsion de las texturas, asi que ajustalo a tu gusto.
//MODE9_FOV(90);
// MODE9_PERSPECTIVE(1) Corrige gran parte de los defectos gráficos de las texturas, pero impacta mucho en el rendimiento.
// Por defecto esta desactivado (0), lo que da un efecto de distorsion de las texturas.
//MODE9_PERSPECTIVE(1);
// Far clip ("flopping"): no dibujar geometria mas alla de N uds DIV2.
// Por defecto está a 0 (sin limite).
// Si activas niebla con SET_FOG9, se auto-activa el FARCLIP con el valor de 'far' de la niebla (lo que queda tras la niebla es del color de fondo, no se dibuja).
//MODE9_FARCLIP(1000);
// MODE9_COLLISION activa y desactiva las colisiones: con 1, advance/xadvance NO atraviesan las paredes (la distancia
// minima a la pared es el campo 'radius' del proceso camara). Con 0 puedes atravesar paredes.
//MODE9_COLLISION(0);
// MODE9_STEP(INT X) se usa para subir escaleras y rampas.
// El valor es la altura max (uds DIV2) que la camara sube sola. Los muros
// mas bajos NO bloquean (se suben como un peldaño) y la camara se apoya
// en el suelo que tiene debajo (rampas inclinadas, escalones). 0 = desactivado.
// La DLL no aplica GRAVEDAD, la gravedad la tienes que gestionar en el PRG (ver el bloque de salto/gravedad de 'camara');
// Ojo, para que funcione las colisiones deben de estar activadas (no va con MODE9_COLLISION(0)).
MODE9_STEP(70);
// --- Ambiente y niebla (color en rango 0..100, convencion DIV2) ----------
// SET_ENV_COLOR9 fija el color de FONDO y tambien el color de la NIEBLA.
// 0..100 por canal: 100=blanco, 0=negro. SET_FOG9(near,far) funde hacia ese
// color crecientemente con la DISTANCIA: transparente hasta 'near' y opaco en
// 'far' (gradiente suave, como el modo 8). OJO: a diferencia del modo 8
// (0..100), aqui near/far son DISTANCIAS en uds DIV2; lo normal es far ~ 2*near.
//SET_ENV_COLOR9(60, 72, 88); // cielo amarillento, impacta en el color de la niebla
// Tras el FRAME la DLL ya pudo SUBIR z (escalon/rampa/apoyo en el suelo).
// Si z quedo por encima de lo que dejo la gravedad y veniamos cayendo,
// hemos tocado suelo: marcamos grounded y anulamos la velocidad de caida.
if(z>z_fell-1andfall_v>0)
grounded=1;
fall_v=0;
else
grounded=0;
end
END
END
Y aquí os dejo el ejemplo en movimiento:
MODE9_02.PRG · Lo poblamos con billboards
Idéntico al anterior, pero ahora metemos sprites al estilo modo 7. Y es de lo más cómodo: basta con darle a un proceso `ctype = c_m9` y sus coordenadas de mundo en `x, y, z`, y el Modo 9 lo dibuja solo cada frame, escalado por la distancia, sin que llamemos a nada. Coloco varios árboles y un cartel (billboards planos de una cara) y un NPC con gráficos angulares (`xgraph`): una tabla de 8 dibujos repartidos en 360º, de forma que la DLL elige el frame correcto según desde qué ángulo lo mira la cámara, igual que el `xgraph` del modo 7. El z-buffer se encarga de ocultarlos cuando una pared se interpone.
En verde el código añadido y con "(...)" las líneas omitidas por ser idénticas al ejemplo anterior:
npc_gr[]=8,8,1,2,3,4,5,6,7;// N=8 graficos en 360º
BEGIN
ctype=c_m9;// <-- el modo 9 lo pinta como billboard automaticamente
//graph = 8; // grafico de MODE9.FPG, billboard plano (1 cara)
xgraph=&npc_gr[0];// grafico de MODE9.FPG, billboard con multiples caras, en funcion del angulo de vision
size=100;// escala base (%); el modo 9 la modula por distancia
angle=0;
x=wx;y=wy;z=wz;// posicion en el MUNDO (el proceso la conserva)
LOOP
FRAME;
END
END
PROCESSM9TREE(wx,wy,wz)
BEGIN
ctype=c_m9;// <-- el modo 9 lo pinta como billboard automaticamente
graph=9;// grafico de MODE9.FPG, billboard plano (1 cara)
size=100;// escala base (%); el modo 9 la modula por distancia
angle=0;
x=wx;y=wy;z=wz;// posicion en el MUNDO (el proceso la conserva)
LOOP
FRAME;
END
END
PROCESSM9SIGN(wx,wy,wz)
BEGIN
ctype=c_m9;// <-- el modo 9 lo pinta como billboard automaticamente
graph=10;// grafico de MODE9.FPG, billboard plano (1 cara)
size=100;// escala base (%); el modo 9 la modula por distancia
angle=0;
x=wx;y=wy;z=wz;// posicion en el MUNDO (el proceso la conserva)
LOOP
FRAME;
END
END
Y aquí lo tenéis en movimiento:
Por cierto, el NPC que véis es un render de un personaje que modelé en Blender en octubre del año pasado (aquí lo podéis ver en verdadero 3D: https://sketchfab.com/3d-models/nazuna-from-backstabbed-in-a-backwater-dungeon-855b836348714abaa321d4638f2358db). Y bueno, sobra decir que los árboles y la señal también los modelé yo mismo, en este caso para un proyecto abandonado que tenía el nombre en código "La Aventura de Greta: Eboke Debe Dimitir" y del que había ya preparado un boceto para su pantalla de "pulsa start":
MODE9_03.PRG · Metemos un objeto 3D de verdad en nuestro escenario 3D de verdad
Cuando un billboard plano no basta —queremos rodear al bicho y verlo por todos lados— y los billboards con sprites por ángulos se nos queda corto, toca meter un modelo 3D. Aquí cargo una gata humanoide con `LOAD_OBJ9("OBJ/CAT.OBJ", "MAP/CAT.MAP", 50)`, que devuelve un handle, y lo dibujo cada frame con `DRAW_OBJ9()` en la entrada del castillo.
Un detalle importante: los modelos se cargan en segundo plano (mientras jugamos), así que `LOAD_OBJ9` devuelve el handle al instante, pero el gato simplemente "aparece" cuando termina de cargar. A más polígonos, materiales o animaciones, más tardará en cargar. No obstante, he implementado una función de `MODE9_OBJ9_READY() que nos permite consultar si el objeto ligado al manejador ha acabado de cargar, lo cual permite, por ejemplo, la creación de pantallas de carga.
Al igual que antes, en verde dejo el código añadido y con "(...)" las líneas omitidas por ser idénticas al ejemplo anterior:
PROGRAMMODE9_DEMO;
IMPORT"MODE9.DLL";
CONST
(...)
GLOBAL
(...)
cat_h;// handle del modelo 3D del gato (LOAD_OBJ9)
Sobre la gata humanoide, se trata de una revisión lowpoly (menos de 400 polígonos) de un modelo que hice en Blender en 2021 basándome en el rediseño de Ankha que le hizo Zero Zone (aquí podéis consultar la versión original que hice entonces: https://sketchfab.com/3d-models/ankha-zone-version-37819f223f26467e8b3247e83b507805). Si véis que el modelo lowpoly no se parece mucho al que hice hace 5 años, es lógico... Pensad que he comprimido más de 130.000 triángulos en apenas unos 400.
MODE9_04.PRG · Y ahora, que el objeto esté animado
El mismo personaje, pero vivo. No es que esté bailando o corriendo, pero le he puesto una pequeña animación de "idle" (espera/estar quieto). Para ello, en lugar de `LOAD_OBJ9` uso `LOAD_OBJ9_ANIM("OBJ/CAT", ...)`, que carga una secuencia de poses numeradas (`CAT0.OBJ` … `CAT9.OBJ`), y lo pinto con `DRAW_OBJ9_ANIM()` pasándole un parámetro extra, `fpos`, que indica en qué punto de la animación está.
Mi Modo 9 interpola las poses intermedias por nosotros, así que con 10 fotogramas el ciclo de "respiración" del gato queda suave y para ello ligo el `fpos` al `timer` para que la animación corra a la misma
velocidad independientemente de los FPS a los que vaya el juego. En otras palabras, los vértices no se teletransportan del punto A al punto B, si no que hacen un recorrido. Esto lo hago porque en hardware modesto es poco realista hacer animaciones grandes de 180 posiciones, por poner un ejemplo... Al igual que en las animaciones de Blender, si capturas la posición de los vértices en el momento 0 y otra vez en el momento 50, con esta librería se recalculan las posiciones entre 1 y 49, para que la animación luzca fluída, sin que le tengas que especificar en cada frame cual es la posición exacta de los vértices.
Otra cosa a aclarar es que, como los OBJ no tienen soporte nativo para animaciones, los OBJ de CAT1, CAT2, CAT3, CAT4 son idénticos (mismos vértices, UV, materiales, etc), pero con posiciones cambiadas. Esto, si tienes maña con Blender, no es complicado de implementar e incluso te ayuda a exportar las animaciones para funcionar de esta forma.
Tened en cuenta también de que si pensáis usar animaciones 3D, resultaría recomendable que hagáis uso de MODE9_OBJ9_READY() para implementar una pantalla de carga, puesto que, a pesar de que los objetos se cargan en segundo plano, pueden causar ralentizaciones (pensad que una animación 3D de 10 frames es como cargar en memoria 10 objetos 3D, uno tras otro).
Al igual que antes, en verde dejo el código modificado y con "(...)" las líneas omitidas por ser idénticas al ejemplo anterior:
PROGRAMMODE9_DEMO;
IMPORT"MODE9.DLL";
CONST
(...)
c_m9=200;// c_type "logico" del modo 9 (convencion, ver cabecera)
CAT_FRAMES=10;// fotogramas del idle del gato (CAT0.OBJ..CAT9.OBJ)
CAT_LOOP=300;// duracion del bucle en centisegundos (300 = 3 s, ver timer)
GLOBAL
(...)
BEGIN
(...)
// --- NPC 3D animado (objeto OBJ texturado en el mundo) -------------------
// LOAD_OBJ9_ANIM carga una animacion por vertex morphing: los fotogramas son
// OBJ/CAT0.OBJ..OBJ/CAT9.OBJ (misma topologia, solo cambian las posiciones) con una
// unica textura CAT.MAP compartida. Ocupa UN SOLO slot de los 8 de la DLL,
// independientemente del numero de fotogramas. La carga se separa de NPC3D y
// se llama con FRAME entre medias para refrescar antes de que arranquen los
// procesos (y porque parsear 10 OBJ tarda varios segundos en disco real).
MODE9_05.PRG · De un mapa a otro: el castillo por dentro
Aquí es donde se pone serio. Este ejemplo es ya casi un pequeño juego: tiene pantalla de inicio ("Pulsa una tecla"), una pantall de salida con menú Sí/No, y sobre todo, un cambio de escenario al cruzar una puerta. La clave es que `LOAD_WLD9()` recarga el mundo en caliente, sin parar el Modo 9 ni perder la cámara: detecto que el jugador está sobre la puerta (distancia al cuadrado por debajo de un radio), hago un *fade* a negro para disimular, descargo los props del patio con `signal(..., s_kill)`, cargo el interior (`CASTIN.OBJ`), apago el cielo con `SKY9_OFF()`, bajo la luz ambiente con `SET_ENV_COLOR9()` y recoloco al jugador.
Dentro hay un NPC (básciamente he mudado a Nazuna aquí) sobre un estrado, un trono, lámparas colgando y antorchas en las paredes, y un bocadillo de diálogo que sigue al personaje en pantalla usando `MODE9_PROJECT()` para proyectar la posición de su cabeza a coordenadas 2D. Y también hay un guiño a cierto consejo sobre lo peligroso que es andar solo.
Como siempre, en verde dejo el código modificado y con "(...)" las líneas omitidas por ser idénticas al ejemplo anterior:
PROGRAMMODE9_05_DEMO;
IMPORT"MODE9.DLL";
CONST
SCREEN_X=320;// Ancho de la resolucion de la pantalla
SCREEN_Y=200;// Alto de la resolucion de la pantalla
GAME_FPS=60;// FPS objetivo
ADV_UNIT=30;// velocidad de paso (uds DIV2)
c_m9=200;// c_type "logico" del modo 9 (convencion, ver cabecera)
MAP_OUT=0;// exterior (patio)
MAP_IN=1;// interior (sala)
SWAP_CD=40;// frames de cooldown tras un cambio de mapa
// Puerta del torreon (exterior): OBJ z=-4 -> mundo y=-400.
DOOR_X=0;DOOR_Y=-400;DOOR_R2=20000;// radio de disparo ~141
// Puerta de salida (interior): OBJ z=-6 -> mundo y=-600.
EXIT_X=0;EXIT_Y=-600;EXIT_R2=22500;// radio de disparo ~150
// NPC dentro de la sala (mismo sitio que NPC(0,500,50)): globo de dialogo al acercarse.
NPC_X=0;NPC_Y=500;BUBBLE_R2=160000;// radio del globo ~400
NPC_HEAD=220;// punto (mundo) a la altura de la CABEZA: ancla del bocadillo y destino del rabito
NPC_TOP=320;// punto (mundo) SOBRE la cabeza: ahi va el nombre
BUB_HW=78;// semiancho del bocadillo lateral (1 linea de frase)
BUB_HH=12;// semialto del bocadillo
BUB_GAP=12;// separacion entre el NPC y el bocadillo
GLOBAL
STRUCTm9// STRUCT de camara del modo 9 (mismos campos que m8). &m9.z = base del struct.
z;
camera;
height;
angle;
END
scr_x;// resolucion real (la lee la DLL): raton/camara a
scr_y;// cualquier resolucion, no solo 320x200
map_state;// MAP_OUT / MAP_IN (se muestra como 1 digito arriba-izda)
BEGIN
(...)
LOAD_FPG("FPG/MODE9.FPG");
MODE9_LOADFPG("FPG/MODE9.FPG");
MODE9_STEP(70);
map_state=MAP_OUT;
camara();// muestra la pantalla de inicio, carga el 3D y juega
MODE9_MU.PRG · Bonus: pantalla partida a dos jugadores
Para rematar, una demo de split-screen. Este ejemplo lo comentaré simplemente por arriba: Se definen dos regiones (mitad superior y mitad inferior de la pantalla) con `MODE9_REGION`, y se arrancan dos cámaras con sus respectivos struct `m9` mediante `START_MODE9(c1, &m9[0].z, 1)` y `START_MODE9(c2, &m9[1].z, 2)`.
Cada jugador maneja su cámara (uno con WASD, otro con los cursores) y —detalle bonito— como cada cámara lleva también `ctype = c_m9`, cada jugador ve al otro como un billboard angular (Nazuna para variar) dentro del mundo. Para acabar sólo mencionar que mi Modo 9 admite hasta cuatro vistas simultáneas.
Soy consciente de que una librería así no se aprende solo leyendo código, de modo que he generado una ayuda en HTML completa, navegable, con la lista de todas las funciones agrupadas por temas, una guía de "primeros pasos" por etapas (las mismas que los ejemplos), una sección sobre cómo preparar los modelos OBJ y otra con las limitaciones del motor (número máximo de vértices, triángulos, materiales, modelos, billboards, etc.). La tenéis en dos idiomas: `HELP_MODE9_EN` en inglés y `HELP_MODE9_ES` en castellano. Símplemente descargaros el respositorio github del proyecto y abrid el `index.html` de la carpeta que prefiráis.
¿Y ahora qué?
Me hace especial ilusión este capítulo porque siento que el Modo 9 abre la puerta a un tipo de juego que mucha gente daba por imposible en DIV2. Tenéis la DLL ya compilada en la carpeta `BIN`, lista para usar; los ejemplos y los assets en `EXAMPLES`; y el código fuente en `SRC`.
Si esta entrada te ha interesado, te dejo el enlace a los capítulos anteriores de la serie sobre DIV2 Games Studio: