Monday, February 14, 2011

Dungeon Crawler III

Después de experimentar con el dibujo de las barras y la info del personaje, me puse a hacer una visión extendida del nivel -estilo mapa- que no se si estará en la versión final (aunque está bueno poder ver cómo quedaron la mayoría de los chunks unidos). Ah, también hice magia y ahora hay hasta 16x16 chunks, dando un nivel de 80x80 bloques máximo.

Lo divertido del domingo y hoy a la mañana fue armar la generación de niveles. Primero se definían al azar las dimensiones del nivel. Como recordarán, cada chunk tenía 8 bits, NSEO ABCD, habiendo un 1 en N/S/E/O si existía una salida en esa dirección. La idea, entonces, era generar primero por X y luego por Y. Si no era el primer chunk en X, entonces asignaba su bit de salida Oeste al mismo que tenía el de salida Este de su vecino (X-1). Si no era el primer chunk en Y, hacía lo mismo con el de arriba. Si era el último en X, chequeaba que su salida Este fuera igual a la Oeste del primero en X, idem Y.


Pseudocódigo para los que lo pueden entender (nota: los tabs son nuestros amigos)

for (y = 0; y <= chunkSizeY; y++) {
    for (x = 0; x <= chunkSizeX; x++) {
        chunk[y][x] = random(); //Genera byte al azar
        if (x > 0) chunk[y][x][5] = chunk[y][x - 1][4]
        if (y > 0) chunk[y][x][7] = chunk[y - 1][x][6]
        if (x == chunkSizeX - 1) chunk[y][x][4] = chunk[y][0][5]
        if (y == chunkSizeY - 1) chunk[y][x][6] = chunk[0][x][7]

        //Extras
    }
}

Luego de los chequeos de salidas de arriba, hay que evitar que existan islas -pedazos de nivel inconectables-. La idea que se me ocurrió fue forzar a que haya una salida en 1 que no sea obligatoria, o sea, si por ejemplo estamos con x = 1, y = 0, la salida Oeste sería obligatoriamente la misma que la del Este del vecino, entonces por más que esté en 1 no cuenta y habría que ver las otras tres que son 'libres', digamos.

En el caso en que todas las salidas sean obligatorias, fuerzo a que exista una salida al norte, cosa de que no me quede ese chunk separado del resto. Generado el nivel, busco una posición al azar para el pj y otra para la salida.

¿Ventajas de no tener islas? Todo el nivel es explorable, no me tengo que preocupar porque no se pueda llegar a la salida, o encontrar un item o bicho, o algo. Bajar la escalera (ese es el dibujo) indica que hay que regenerar el nivel, las posiciones, los items, bichos, etc.


Lo siguiente sería agregar los mobs. En principio tengo pensado momias y murciélagos, después veré qué cosas se pueden dibujar con 8x8 pixels. Cada bicho definiría su poder de daño según su raza y la profundidad del dungeon, al igual que su HP. Estoy pensando en que aparecerían cuando el pj va caminando por ahí, al igual que con los items. Y además creo que estaría bueno dejar que se muevan en tiempo real -o sea, no un turno yo, un turno ellos- cosa de hacerlo más emocionante.

Bajar el hp de un bicho a cero quizás lo haría droppear algún item y daría una cierta experiencia que depende de la raza del bicho y la profundidad del dungeon.


En fin, tengo muchas cosas para pensar sobre cómo continuar y hacerlo divertido y justo en su dificultad. Creo que ya está al 50%, dado que así es un juego de exploración -aunque no el más entretenido quizás-.

Se aceptan ideas sobre mobs, items, skills, etc!!

Saturday, February 12, 2011

Dungeon Crawler II

Hace bastante que no comento los avances en el juego y ahora tengo algo de tiempo (un recreo bah :P)

Bueno, en principio empecé el martes a la tarde cuando tuve el programador en mis manos -bwahahaha-, lo segui el jueves con más tiempo y recién un poco. Las cosas que se pueden hacer por ahora es observar el nivel moviéndose con las flechas. El mundo es de hasta 75x75 (por tener 15x15 de los registros que comentaba antes, a los cuales voy a llamar Chunk porque me recuerdan a lo que hace Minecraft)


Un Chunk, entonces, es un byte codificado como "NSEO ABCD", habiendo un bit en N/S/E/O si hay una salida en esa dirección y siendo ABCD la forma que tiene el conjunto de bloques. Hay 16 combinaciones de ABCD, pero sólo ocho de ellas dan forma a conjuntos distintos -no, no me iba a poner a pensar dieciseis diferentes-.

El truco está en que cuando AB != CD, se generan dos conjuntos de 5x5 bloques que luego mediante un AND lógico arman las diferentes combinaciones. Si AB == CD, entonces se genera un único conjunto según el valor de AB.

Finalmente, si por ejemplo no hay salida al "norte", eso se traduce en un bloque de 5x5 vacío con la parte superior bloqueada que tendrá efecto en el conjunto final mediante un OR. Todo esto muy teóricamente, en realidad no necesita tooodos los 5x5 bloques para sacar los datos que usa.


Lo interesante vino cuando tuve que juntar dos o tres chunks para armar el byte que dice dónde hay bloques en las posiciones {(x + i, y), i <- [0 .. 8)} del mundo. Luego de eso tuve problemas con el redireccionamiento indirecto del PIC porque creía que andaba de otra forma :P

Como resultado final, tengo un nivel de tamaño variable, de 1 hasta 15 chunks encolumnados por otros 1 a 15 chunks en fila, dándome los 15x15 chunks, o 75x75 bloques. Además, el mundo es circular y no tiene fin, ya que irse por el este termina llevando al oeste.

Pero basta por un rato de charla técnica y veamos un video:



El video creo que muestra un par de bugs (a ver si los encuentran!) que venían del mal uso del redireccionamiento indirecto. Estuve un rato en la mañana tratando de arreglarlo y es genial cuando sabés que algo está andando 'raro', entendés por qué salen los errores y tenés una sospecha de cuál es el problema, todo para que una búsqueda en google te dé la razón :)


Lo que hice hace un rato es agregar los datos del personaje. A modo de prueba le puse 17/50 de MP (magic points), 40/50 de HP y lvl 39. Dibujar las barras de HP y MP fue un desafío que creo haber solucionado de manera inteligente.

Por ejemplo, para el HP tengo dos barras, cada barra ocupando dos posiciones, digamosle, en el lcd, con cada subbarra teniendo ocho pixels de alto. La idea entonces fue usar el recurso mágico de la búsqueda binaria. Con cuatro subbarras (una potencia de dos), con ocho pixels cada una (otra potencia de dos), tenía entonces 32 pixels para ver de dibujar. La idea consiste en tomar la mitad del máximo de HP (25), ver si es mayor o menor que el actual (40), y a partir de ahí decidir si había que pintar más o menos de 16 pixels, para luego repetir el proceso.

En este caso, sería así:
MHP: 50
HP: 40
pixels = 0

MHP / 2 = 25
40 >= 25
pixels = 16

(MHP / 2) + MHP / 4 = 25 + 12 = 37
40 >= 37
pixels = 16 + 8 = 24

(MHP / 2) + MHP / 4 + MHP / 8 = 25 + 12 + 6 = 43
40 <>pixels = 16 + 8 = 24

(MHP / 2) + MHP / 4 + MHP / 16 = 25 + 12 + 3 = 40
40 >= 40
pixels = 16 + 8 + 2 = 26


Con 26 pixels para dibujar, sabía que tenía las primeras tres subbarras completamente llenas y la 4ta con 26 - 24 = 2 pixels

Lo mismo pasa con la de MP. El problema está en la barra de experiencia (XP), que no usa un byte sino dos (para poder tener 31256/65536 xp en vez de los limitados 78/255). La idea en el fondo sería la misma, aunque un poco más complicada porque tendría que manejar dos registros, pero creo que mañana estaría andando.


Y para terminar, dejo video de cómo quedó todo eso:



Tuesday, February 8, 2011

Old pic projects [Video]

Dejo estos dos videos con el snake/tetris que había hecho allá por el 2008 :P

Snake


Tetris


Algunas pics: tiki

Saturday, February 5, 2011

Blog Rebirth - Dungeon Crawler I

Bueno, bueno... Hace bastante que no vengo por acá pero parece que hay mucha gente interesada en que reviva al blog (ok, no exactamente mucha), así que voy a hablar un rato del nuevo proyecto. Mientras hablo, ustedes pueden leer.

Un poco de background: resulta que en 6to año de mi secundario teníamos una materia llamada Microcontroladores -creo-, donde por un lado vimos la teoría de las arquitecturas CISC/RISC, y de ahí nos metimos de lleno en entender cómo andaba un microcontrolador específico, el PIC16F84 (con algunas extensiones del 16F877). Hecha la teoría y presentado el set de instrucciones de cada uno, luego de practicar resolviendo problemas y bla bla bla, había que hacer un tp final.

Yo estaba con Ariel en grupo, entonces, cuando no jugábamos al smash en clase -teníamos la única pc que parecía poder correrlo!-, nos poníamos a pensar qué se podía hacer. Fuimos de un auto a radiocontrol con parlantes y micrófonos que transmitían/recibían datos desde el control remoto hasta una especie de auto patrulla que exploraba una habitacion para saber cómo era.

El proyecto que les ganó a todos esos fue el de hacer un juego. En un principio pensamos en usar una matriz de leds, pero eso iba a hacer que el resultado final fuera muuuy grande, por lo que surgió la idea de usar un lcd. Buscando, encontré uno de 128x64 pixels, bastante bien para lo que queríamos hacer. Problemas más, problemas menos, terminé diseñando y armando la placa y programando un snake (Ariel ayudó a soldar y con la parte económica :P). Creo que fue el mejor tp que jamás hice -apenas se lo entregué, el profesor salio corriendo a mostrarle al jefe de taller lo que había hecho, jajaja-. Aparte me dieron ganas de seguir con el tema, así que hasta las primeras semanas de ese diciembre, optimicé y le agregué cosas al snake. También me compré otro pic e hice un tetris. Estaba en planes de hacer un asteroids, pero lo dejé y desde ese día no toco nada de assembler.


Hoy me leí el manual de microchip del PIC16F877A, recordando algunas cosas y descubriendo otras que hubiesen facilitado la programación y que ahora pienso usar. El martes con algo de suerte tendría el programador para volver a las andanzas con este nuevo proyecto que usaría esa placa del tp, con ese lcd y que creo que va a estar bueno para adaptar a un diseño mas compacto estilo tamagotchi :P

La idea es hacer un Dungeon Crawler, para los que no estén familiarizados en estos tecnicismos (por ejemplo, los bots y las personas que caen al blog cuando buscan cosas del Ragnarok Online), se trata de un tipo de juego donde la idea es explorar y sobrevivir. En mi Kyocera S14, mi primer celular, uno de estos venía por defecto. Básicamente recorrías un nivel generado al azar, estilo laberíntico, donde podías encontrar bichos o cofres con items. Creo que había comida y cosas como espadas y armaduras. La idea era ir bajando los niveles y sobrevivir lo máximo posible.

Mi idea del juego sería algo parecido, pero en assembler (sep, sin usar ni C ni BASIC) y enfrentándome a las limitaciones que tienen 8KB de memoria de programa (8192 caracteres, para hacer como los del discovery que tiran cifras y las comparan con otras cosas) y 384 bytes de registros de memoria RAM (en una pc hay en general algo así como 2GB, que serían mas de mil millones de veces los registros que tiene el PIC). Ahora que están asustados y aturdidos por tanta información, voy a darles el golpe final comentando cómo sería mi idea a la hora de encarar el juego.


Siendo mi pantalla de 128x64 pixels, pensaba reducirla a una de 64x64 para el tamaño tamagotchi, por lo que en primera instancia estaría dibujando la mitad de la pantalla, cosa de ver cómo queda. El juego tendría bloques de 8x8 pixels, mostrando en la pantalla un total de 8x8 bloques. Mi idea ahora es que el laberinto sea de tamaños estilo 40x40 bloques, por lo que haría falta scrollear la pantalla. Aparte, mi intención es que el mundo sea wraparound, toroidal, que se repita, es decir, que cuando pases el bloque 39 vuelvas al bloque 0 -zero based, folks!-.

La primera idea que se me ocurrió fue la de tener 5x40 registros (200), donde por cada registro habría un total de ocho bits (que es lo que tiene un byte) indicando con un 1 si hay un bloque y con un 0 si no lo hay. El primer problema visible es tener 200 de 368 registros de memoria ocupados en el tablero, limitándome en las posibles otras cosas que quisiera hacer. El segundo problema, no tan visible, está en que al generar un nivel al azar, sería complicado armar un laberinto 'creible' -o mejor dicho, que no se note tanto que es random-, donde puedas ir desde el punto de partida hasta la salida al siguiente nivel. Hay otros más, pero voy a detenerme y empezar a hablar de mi (posible?) solución final.

Habría un total de 8x8 registros (64), indicando cómo serían los 40x40 bloques del laberinto. Las ventajas son obvias, menos memoria ocupada (64 vs 200), menos llamadas al procedimiento 'random' y la posibilidad de hacer niveles de tamaño variable. O sea, podría haber algunos con 100x50 bloques, que vendrían a ocupar los 200 registros como 20x10, o bien otros con 4x4 registros que representen un nivel de 20x20 bloques, por ejemplo. Como ya estarán mareados con los números, procedo a encender una lamparita e iluminar este nuevo procedimiento.

Cada uno de esos 8x8 registros representaría un conjunto de 5x5 bloques. Siguen teniendo 8 bits, pero la diferencia es que cuatro serán indicadores de si el conjunto tiene salida al norte, sur, este u oeste, mientras que los 4 restantes podrían representar otras cosas, como 16 variedades distintas que conectan las salidas entre sí. De esta forma también se puede saber qué conjuntos se pueden conectar con otros, y la parte de generación de niveles al azar sería mucho más simple, ya que habría que armar conjuntos que se conecten con los que ya fueron generados. It's magic!


Pero bueno, por hoy ya es suficiente. Tengo que ver el tema de los bichos, los items, el hp, si hay skills, experiencia, level up, etc, etc.