10 de enero de 2011

Em Cee Kids
2a. Parte


 Para leer la primera parte de este Reportaje, pincha aquí

:::::::::::::::::::::::::::::::::::::::

ASPECTOS TÉCNICOS DE MC KIDS




CONJUNTANDO LOS "TILES"

Cada tipo de "Tile" (cuadrito que va armando una figura o parte del escenario, compuesto de 8x8 pixeles) tiene asociado a sí 7 bytes:

Los primeros 4 bytes definen cuales figuras/personajes estarán en la memoria de lo que se mostrará en pantalla, esto por cada esquina del "Tile". El quinto define el color (0...255). El sexto especifica qué tipo de "Tile" será (0...255), algunos valores para este sexto byte son por ejemplo: 0 = cielo, 1 = sólido, 2 = inclinación izquierda, $30 = arco faltante, $31 = carta, $17 = puente cayendo.... El séptimo y último byte define en qué se convertirá el "Tile" en caso de ser necesario (por ejemplo, cuando el personaje toma un arco, ese espacio que ocupaba cambia a un "Tile" de color blanco del cielo, porque el personaje ya se "comio" la figurita).


Esta captura pertenece al blog de Gregg, a fin de mostrar cómo
la "M" (arco) está sobrepuesta por sobre el blanco/azul del cielo, y no a la inversa




Asímismo, cada "Tile" tiene 5 rutinas de colisión (explicadas en el apartado "Cajas de Colisión" que leerás más adelante), 2 tablas de contorno, y varias "directrices/instrucciones" incluyendo:


AT_BEHIND: Si algún objeto toca (colisiona) este "Tile", la figura de tal objeto debe ser mostrada detrás del "Tile". Este valor se usa generalmente para "Tiles" como las nubes, lava, y cercas/enrejados.


AT_EYES: Si el personaje colisiona con este "Tile", el personaje seguirá siendo visible. Por ejemplo, cuando al ir caminando y de fondo hay nubes, el personaje tendrá "prioridad" de visibilidad por sobre los "Tiles" de las nubes.


AT_ABOVE: Si el "Tile" de una colina es posicionado sobre un "Tile" con este valor (AT_ABOVE), este quedará sobrepuesto (aunque aquí se genera un inconveniente, detallado en el mismo apartado de "Cajas de Colisión").


Cada set de "Tiles" puede contener hasta 64 figuras de un máximo de 64 "Tiles". Cada set de "Tiles" no puede contener mas de 4 sets de colores (si no se acuerdan, el sistema NES solo puede mostrar 4 paletas de color para las figuras).


Cada nivel en el juego contiene como máximo 4 sets de "Tiles", pero todos los niveles cuentan con "Tiles" globales, que son los que dan forma a objetos de uso común (línea de meta, arcos dorados, bloques...).


Tener un "Set de Tiles" sirve para organizar el trabajo, por ejemplo, el "Set de Tiles" de un bosque contiene muchos "Tiles" verdes (árboles, arbustos, hojas, etcétera), el "Set de Tiles" de la lava contiene rocas, puentes ardiendo, etcétera.




Fuego, puente cayendo, escenario rojo... es preferible tener
los "Tiles" de un escenario en una misma librería




Hay alrededor de 44 "Sets de Tiles" en MC Kids de donde escoger.



"A la grande le puse Cuca"

:::::::::::::::::::::::::::::::::::::::

Para que este punto (TILES) quede mejor explicado, me he servido tomar una serie de imágenes de otro sitio, así como la explicación al respecto. Se trata del juego Metroid (al igual que con el presente artículo, todo está INTERPRETADO de la fuente al calce citada):



DEFINICIÓN DE TILE

Como algunos deben saberlo, la pantalla del Sistema NES está representada por una tabla de "Tiles" con valores indexados, de un tamaño de 32x30 bytes. Cada byte contiene un valor, que hace referencia a un "Tile" en el patrón de la tabla; cada "Tile" por sí mismo es de 8x8 pixeles. Una forma gráfica de representar los "Tiles" es esta:



Cada cuadro representa un byte, y cada byte contiene un rango en la tabla de valores de 00 a FF. Este valor determina qué "Tile" será desplegado en una área particular de la pantalla.

Si miras con detenimiento la captura anterior del juego Metroid (reticulada), verás que los "Tiles" dan forma a algo: rocas, columnas, etcétera. Aquí unos ejemplos:





Como puedes ver, cada "Patrón" está formado por 4 "Tiles", teniendo en este ejemplo 3 "Patrones" descansando en una malla de 2x2 "Tiles" (no pierdas de vista que cada "Tile" tiene 8x8 pixeles, ¿lo notas?).


:::::::::::::::::::::::::::::::::::::::


FORMAS Y ANIMACIONES

Una "Forma" (definida, para que se entienda el vocablo) consiste de Sprites/figuras de 8x8 que están agrupados. Hay 364 "Formas", y 213 "animaciones" usando estas "Formas" en MC Kids.


Con casi 8k para representar estas formas, se utilizó para MC Kids un formato que permitía compactar la mayoría de las formas. Hay 2 formatos, el primer formato solo permite posicionar Sprites dentro de un espacio de 8x8. El segundo de ellos permite que los Sprites puedan ser colocados donde quiera (en relación con otros Sprites).


El segundo formato cuenta además con un código que permite cambiar el "Set de colores".


Hablando de formas (y antes de hablar de "Objetos"), hay que decir que estas generalmente miran a la derecha, y como lo normal es que también puedan mirar a la izquierda, entonces el eje de giro (lo que llamaremos "Hot Spot") se debe ubicar en la parte central inferior:




Posicionando esta "Forma" en la coordenada 0,0 tenemos lo siguiente:
Es aquí donde entra en juego la "Animación". Una "Animación" especifica el orden y duración de un conjunto de "Formas", se agrega una repetición de ciclo, y listo.


Cuando una "Animación" comienza a repetirse, el sistema nos dice la rutina del objeto, el cuál puede entonces modificar su comportamiento (más detalles en la sección de "Objetos").


La duración de una "Animación" esta representada por 6 bits, así que el tiempo máximo que una figura puede estar visible en pantalla es de 63 ticks (63/60 segundos). Los otros 2 bits especifican hacia dónde está girada (en la dirección X o Y). Esta información, sobre el girado X o Y, es independiente de aquella que tiene que ver con el girado establecido de la Forma.


No confundamos esta información de hacia dónde gira la "Forma", con la ya establecida rutina de giro de un objeto controlado de una forma.



Esto me ha dejado como lección, que si yo rehiciese el Sistema, pondría información adicional concerniente a las posiciones, con la finalidad de que una "Animación" pudiese mover un objeto.


OBJETOS

Los "Objetos" son puestos en el sistema de objetos por varias razones. La más común, por el hecho de que se interactúa con ellos: peces, cangrejos, plataformas móviles, son algunos ejemplos.


El sistema permite "n" objetos activos al mismo tiempo; para M.C. Kids, "n" es 16. Un gran número, pues con más de 7 objetos moviéndose simultáneamente, el sistema comienza a "ralentizarse". Nosotros descubrimos esta limitante durante el desarrollo del juego, y reorientamos la mayor parte de los niveles para que el jugador tuviera más de 6 monstruos juntos. Por hacer un comparativo, Super Mario 2 y 3 están cuidadosamente hechos para que el programa casi nunca ponga más de 5 objetos a la vez.


Así pues, por cada nivel del juego hay una tabla de "Objetos", la cual describre dónde apareceran dichos objetos.


Cada entrada en la tabla de "Objetos" tiene tres campos de información: la posición en X, Y, y el tipo de "Objeto". Cuando el jugador se mueve a la derecha, la posición de la pantalla es comparada con la tabla de "Objetos", y si alguno de ellos debe mantenerse en la pantalla, este es introducido en la lista de "Objetos" activa que, dicho sea de paso, se encuentra ordenada. De ese modo, cuando un "Objeto" desaparece de la pantalla es el momento en que se puede mostrar un nuevo objeto (el "Objeto" que ya no está en pantalla es descargado de la lista de "Objetos" activa, aunque solo desplazado en la tabla de "Objetos", según el orden de aparición).


Para los objetos que están a izquierda-derecha se usa la tabla de "Objetos" X, y para los que están arriba-abajo la tabla de "Objetos" Y.


Difícilmente en un juego verás más de 16
objetos interactuando con el jugador, sin que la acción se alente



Pero también hay razones por las cuales un "Objeto" es puesto en un juego. Por ejemplo, cuando uno de los M.C. Kids pisa un bloque, otro "Objeto" lo reemplaza, pues esa transición hará una "Animación" del bloque. Igual pasa cuando tomas una vida, o cuando el bote salpica agua (hay que agregar otro "Objeto" de las gotas del agua), y así con otros "Objetos".


Por cada "Objeto" hay directrices (bits) como estas:


OB_HASBIT: ¿Está el objeto en la tabla de objetos activos?... si sí lo está, esto evitará que se multiplique arbitrariamente en pantalla.


OB_EARLY: ¿El objeto puede ser quitado prontamente?... esto a fin de evitar que el juego se alente, por ejemplo en un puente cuyos bloques van cayendo: si hay más de 5 que caen casi simultáneamente, el programa hará la ralentización, así que con esta instrucción no caerá un quinto bloque hasta que el primero que comenzó a caer haya desaparecido.


OB_XPHYS: ¿El objeto tiene física horizontal (X)?


OB_YPHYS: ¿El objeto tiene física vertical (Y)?


OB_XCHK: ¿Debe el objeto verificar si hay colisión a izquierda o derecha? (en el apartado de "Terreno" y "Cajas de Colisión" se explica más a fondo).




OB_YCHK: ¿Debe el objeto verificar si hay colisión arriba o abajo? (en el apartado de "Terreno" y "Cajas de Colisión" se explica más a fondo).

OB_ONCE: ¿Debe el objeto desaparecer y no volver a aparecer ? Cada objeto en el juego tiene su directriz, eso prevendrá que se aparezca indiscriminadamente. Esto puede verse en el ejemplo del puente: los bloques del puente que caen uno decidiría si al avanzar esa parte, y luego regresar a él, los bloques se "pusieron" como por magia, o sí esa parte del escenario sigue con un gran abismo porque los bloques cayeron y no se volvieron a poner.

OB_REMOVE: ¿Debe el objeto ser removido cuando está a 4 "Tiles" de distancia fuera de la pantalla?... si este bit esta puesto, entonces será cuestión del objeto desaparecerse por sí mismo.

Ahora bien, por cada "Objeto" en el juego, hay un protocolo que le dirá que hacer:

1. Buscar y ejecutar la rutina específica del "Objeto". Por ejemplo, la rutina de una plataforma es "ir y venir", esa sería la rutina específica (primaria) del objeto "Plataforma". Supongamos que el personaje se acerca a esta plataforma, la rutina de tal personaje lee la dirección al control que le estamos aplicando, y actúa acorde a la rutina del "Objeto" (la plataforma, en este caso).

Esta plataforma está quieta, hasta que
el personaje la pisa y ambos caen



2. Si las propiedades físicas horizontales (X) del "Objeto" están "activadas", estas son computadas usando la programación que se le dió a la aceleración, velocidad, y posición.

Hay dos tipos de routinas para los movimientos horizontales (X). Una rutina es usada cuando el "Objeto" esté en el aire, la otra cuando el "Objeto" está en la tierra. La del aire utiliza física normal, pero en el caso del "Objeto" que está en tierra, se requiere hacer que dicho objeto siga el contorno del piso, así que por cada pixel que se mueva a izquierda/derecha, la posición vertical (Y) se tiene que ajustar a la altura correcta.

3. Comparación de "Objetos" y "Objetos" con colisión. El programa hace el comparativo entre estos dos tipos de objetos, y si hay una "Colisión" (más adelante se explica esta propiedad), la rutina de "Colisión" se pone en marcha. Por ejemplo, si tenemos un bloque transparente y el personaje choca con él cuando las variables son las correctas (que esté cargando un bloque sólido, en este caso), el "Objeto" colisionará, dada esta instrucción. Otro ejemplo sencillo es cuando un bloque toca un enemigo, y este es dañado por así corresponder a la programación.



El recuadro transparente colisiona con el bloque que carga el niño,
quedando empotrado como consecuencia



Aunque en el apartado de "Cajas de Colisión" se adentra en este tema, es pertinente mencionar que cada "Objeto" tiene asignada una prioridad de colisión: es más importante para el programa saber cuando el personaje golpea a un monstruo con un bloque, que saber cuando el personaje toma dicho bloque (no hay colisiones simultáneas), sumado al hecho de que cada "Objeto" solo puede colisionar con otro "Objeto" por frame.

Todos los objetos cuentan con una matriz de colisión, así que solo pueden colisionar con ciertos "Objetos"; el único "Objeto" que puede colisionar con todo es el personaje. No tener que estar probando cada posible colisión, ahorrará muuuuuuucho tiempo.

4. Si el "Objeto" tiene activada la modalidad "Colisión con Tile horizontal", entonces se lleva a cabo una revisión de si dicho "Objeto" debe colisionar con algún "Tile" de la librería de "Tiles horizontales" (X), dependiendo si dicho "Objeto" se mueve a la derecha o izquierda. Existe una rutina programada para cada tipo de "Tile", la cual determina si ha sucedido una colisión (o si debe tomarse otra acción, como juntar un arco, o cruzar la línea de meta). Si por ejemplo estamos avanzando a la derecha y tocamos (colisionamos) con un bloque que hará que vayamos a la izquierda, la rutina de "ir hacia la izquierda" se pone en marcha.


¡Estas santas Colisiones me traen de cabeza Mack!




5. Si las propiedades físicas verticales (Y) del "Objeto" están "activadas", estas son computadas.


6.  Si el "Objeto" tiene activada la modalidad "Colisión con Tile vertical", entonces se ejecuta una colisón vertical (Y). Esto para cuando el "Objeto" está en el aire, porque si está en tierra, entonces será la colisión horizontal (X) la que se ejecutará.

En resumen, cada "Objeto" tiene 8 rutinas: ObjectInit, ObjectDoit, ObjectClid, ObjectLeft, ObjectRight, ObjectUp, ObjectDown, ObjectGround. Y cada "Tile" puede tener hasta 5 de ellas: TileLeft, TileRight, TileUp, TileDown, TileGround; y muchos de ellos apuntan hacia la misma rutina (olidLeft, SolidRight, SolidUp, SolidDown, and SolidGround, or SkyLeft, SkyRight, SkyUp, SkyDown, SkyGround).


¿Más datos técnicos?... adicionalmente, cada "Tile" tiene 2 tablas de contorno asociadas, cada una de ellas de 16 bytes, una para caminar hacia la izquierda, y la otra para caminar hacia la derecha. Estas tablas dictan las rutinas sobre cómo ajustar las posiciones verticales (Y) por cada pixel que se avanza horizontalmente (X).


Si tuviera que hacerlo de nuevo, preferiría deshacerme de muchas de estas directrices y solo dar las necesarias dependiendo los "Objetos". Para dar un ejemplo, en lugar de generar una directriz para el eje X, le daría solo a los "Objetos" necesarios de este eje, la física requerida (en relación al eje X).


CUADROS DE COLISIÓN

Ahora sí, vamos con la parte divertida. No, no es jugar todavía, primero quiero mostrarles los pixeles de los M.C. Kids que sirvieron como cajas de colisión.






El punto H, como les había dicho, es considerado el punto central de nuestro personaje. Usualmente, este punto será la parte superior de un "Tile" (el "Tile" puede ser el piso, un bloque, etcétera):



Esto quiere decir que el personaje está parado en algo sólido, pues si lo moviéramos un pixel más arriba entonces tendríamos que insertarlo en un "Tile" del cielo.


Volviendo a la imágen que contiene los puntos de colisión, los puntos 1 y 2 indican si el personaje está parado sobre algo: si ambos puntos no están sobre un "Objeto", entonces debe estar cayendo. Los puntos A y B, o C y D, están indicados dependiendo la dirección hacia la que el personaje mira. Los puntos B y D están programados para interactuar con cada tipo de "Objeto", mientras que los puntos A y C solo sirven para dimensionar al chiquillo (personaje).


El punto "T", finalmente, sirve para indicar hasta qué punto la cabeza del niño puede tocar el techo.


La mayoría de las cajas de colisión son ajustables en varias formas, por ejemplo, los puntos 1, A, y B, siempre tienen la misma altura, pero pueden ser movidos a izquierda o derecha.




TERRENOS

Una de las cosas más difíciles de programar es la interacción entre "Objetos" y el "Terreno". Aquí un ejemplo:



Aquí tenemos 10 "Tiles", numerados del 0 al 9 (de arriba a abajo, y de izquierda a derecha). También tenemos una letra que representa el tipo de "Tyle", donde la N = Normal, H = Hill (colina), y S = Sky (cielo).


Parece sencillo, pero veamos las contrariedades cuando uno trata de implementar esta lógica:



1. Cuando el personaje camina del "Tile" 9 al 6 (obsérvese que el piso está en la parte superior del "Tile" 9), en lugar de comenzar a subir esa colina por el "Tile" 6, el personaje aparece en el "Tile" 8 (donde en teoría no debería poder caminar, por ser el subsuelo). Una solución era hacer otro tipo de "Tile", y nombrarlo "B" (Begining of a Hill), colocándolo en lugar del "Tile" 6. Esta solución, sin embargo, obliga al programador a ser más atento en su obra.



2. Cuando el personaje camina del "Tile" 3 al "Tile" 2, este aparece en el "Tile" 0 (obsérvese que es en el "Tile" 2 donde está el piso, el Tile 0 es cielo). Este error (bug) se quedó en el juego (compruébalo la próxima vez que juegues M.C. Kids). Y bien, este error pudo corregirse del mismo modo que con el error pasado: haciendo un nuevo tipo de "Tile" con la letra "E" por ejemplo (End of Hill) en lugar del "Tile" 9.


3. Frecuentemente hay "Tiles" N (Normal) en el cielo (como las plataformas), y lo ideal sería que cuando los puntos de colisión 1 y 2 del personaje (la imágen que indica estos puntos la encuentras en el apartado de "Cajas de Colisión") no toquen ese "Tile", el personaje debería caer, pero ¿qué pasaría si el "Tile" que sigue es una colina (H)?... 

 Qué quebradera de coco poner plataformas en el aire


El mayor problema es la "inclinación de la colina": el personaje no "caerá" de la plataforma hasta que los dos puntos de colisión (1 y 2) estén fuera de ese "Tile", pero para subir-bajar una colina la instrucción nos dice que es el punto de colisión "H" el que determina el avance arriba-abajo.


Bien, el problema se resolvió (una vez más), creando otro tipo de "Tile" llamado A (Adyacente a una colina), cuya instrucción fue que el personaje cayera cuando el punto de colisión "H" no tocará el "Tile" (en lugar del punto de colisión 1, como es el caso).


 Mario dice que el se quita de problemas de "Tiles" y "Colisiones"
rozándose la colita


En Super Mario Bros. 3 el problema se resolvió de este modo (aunque aquí la instrucción fue general: si en el nivel había "colinas", entonces todos los "Tiles N" tendrían comportamiento de "Tile A"), pero la diferencia es que en este tipo de niveles (donde hay colinas) Mario no puede saltar teniendo solo el punto de colisión 1 sobre el "Tile" (NOTA TYGRUS: con lo incrédulo que soy, voy ahora mismo a comprobarlo).


Hay que pensar que los personajes en los videojuegos son ágiles, y que a ese respecto casi todos los sistemas de Video Juego deben correr a 60 frames por segundo (fps), y si el poner "colinas" en tu diseño significa buscar 4 o 5 posiciones x "Tile" x pixel de colisión x por frame, entonces te quedarás fuera de tiempo (yo le creo).


2 y 2 son 4, 4 y 2 son 6, 6 y 2 son 8 y 8 16, brinca la tablita, yo ya la brinqué...
¡mejor no metamos colinas en nuestro juego!


Considera esto al momento de hacer tu juego: es mucho más fácil hacer un juego de plataformas sin colinas, más aún si estás corto de tiempo.

En el juego M.C. Kids tenemos más de 100 "Tiles" (Solido, Colina Izquierda, Salto, Muerte, Resbaladizo Solido, etcétera), y por cada tipo de "Tile" tenemos información tal como velocidad máxima (cuando se sube una colina, para darle física a la sensación), fricción (en las zonas resbaladizas), así como la velocidad a la que el personaje se gira, entre otros.

Para concluir, si tuviera que hacer todo esto nuevamente, optaría por hacer más tipos de "Tiles" para ahorrarme problemas como los que tuvimos en la creación de M.C. Kids, eso sin contar con que optaría por un procesador como el 68000, que es más eficiente.



NOTA FINAL

Si necesitas un juego de plataformas, deberías venir a nosotros, es decir, a nuestro motor de programación, el cual puede hacer casi cualquier tipo de juego de plataformas de 8-bits, donde tu deseo e imaginación serán el verdadero desafio.

Ahora que has leído este artículo, ¿verdad que no es tan difícil?...




:::::::::::::::::::::::::::::::::::::::::

Pues ahí la tienen, la comparecencia de Mister Gregg Iz-Tavares sobre la programación de MC Kids, que con esta segunda parte queda concluída (con el sesgo que pueda haber luego de la interpretación que de él he hecho).


Espero que las palabras de Gregg sean de regocijo para quienes deseen programar algún juego de estas características, y/o que sean un deleite para quienes solo nos recreamos con este tipo de artículos y anécdotas. ¡Hasta la próxima!


Tygrus

2 comentarios:

Seba dijo...

Hacia años que no leía un post tan interesante como este! Muchas gracias Tygrus!! Me dieron ganas de escribir acerca de los juegos que e desarrollado!! =D es gracioso, porque hace mas o menos un mes que estoy desarrollando un juego de plataformas en c++, y muchas de las cosas que decía ahí las viví de cerca! (habrá que comprobar lo del mario 3!!)

TYGRUS dijo...

Qué bueno que te gustó Seba (años después te respondo, ¡sorry!), hay que tener bastante paciencia para que un proyecto vaya tomando forma y movimiento. Suerte.

Links to the Past