Cómo se hizo Hire Hare
– How Hire Hare was made
10850 hits
Carátula generosamente proporcionada por DracuOfTheFuture
– Cover art generously provided by DracuOfTheFuture
Este artículo trata sobre el desarrollo de "Hire Hare", mi proyecto para el concurso de videojuegos CPC RetroDev 2016. En el momento de escribir estas líneas no se han decidido los premios, pero no por ello es mal momento para explicar los entresijos del proyecto y de su desarrollo.
–– This article discusses the development of "Hire Hare", my project for the videogame contest CPC RetroDev 2016. In the moment of writing these lines, the awards haven't been chosen yet, but that doesn't make it the wrong time for explaining the project's innards and its development.
Las versiones para CPC de "Sabre Wulf" y "Knight Lore"
– The CPC ports of "Sabre Wulf" and "Knight Lore"
Ante todo y sobre todo, el nombre mismo era algo muy viejo: "Hire Hare" ("liebre de alquiler" o "liebre mercenaria") es un chiste fácil a costa del legendario pero inédito "Mire Mare" ("yegua del pantano", pero "mare" también es "nightmare", pesadilla) de Ultimate, el juego que debía haber sido la quinta y última parte de la pentalogía de Sabreman: "Sabre Wulf" (1984), "Underwurlde" (1984), "Knight Lore" (1984) y "Pentagram" (1986). Ya hablaba yo de hacer un juego llamado así hace diez años; pero como suele ocurrir, un simple título no da realmente pie a crear nada: solamente quería que fuese un juego tridimensional isométrico, y eso que cuando finalmente los hermanos Stamper hablaron sobre el asunto revelaron que "Mire Mare" no iba a ser tridimensional como "Knight Lore" sino bidimensional como "Sabre Wulf".
–– First of all, the name itself was a very old idea: "Hire Hare" is an easy pun at the expense of the legendary yet unreleased "Mire Mare" from Ultimate, the game that should have been the fifth and last part of the Sabreman pentalogy: "Sabre Wulf" (1984), "Underwurlde" (1984), "Knight Lore" (1984) and "Pentagram" (1986). I was already pondering making a game with this name ten years ago; but as it usually happens, a simple name isn't really the starting point of anything: I just wanted it to be an isometric tridimensional game, and only later I learned that when the Stamper brothers spoke about the topic they revealed that "Mire Mare" wasn't going to be tridimensional like "Knight Lore" but bidimensional like "Sabre Wulf".
"Frogalot", segundo premio de la categoría PRO y áccesit al avance técnico en CPC RetroDev 2015
– "Frogalot", second prize in the PRO category and best technical advancement award
La cuarta edición del concurso CPC RetroDev me animó a retomar el proyecto y a convertirlo en una realidad. Un año antes había presentado "Frogalot", nacido como un experimento técnico: ¿podía conseguirse en el CPC un "Nebulus" fiel a la versión original para C64 que no redujese el tamaño de los gráficos y que mantuviese o incluso mejorase el rendimiento? Aunque al final se notó mucho que me faltó tiempo para hacer de "Frogalot" un juego realmente jugable (tuve que hacerlo todo en poco más de un mes, y al final solamente tuvo ocho fases, de las que tres de ellas eran simplemente un mismo patrón de plataformas repetido una docena de veces), obtuvo buenos resultados: segundo premio en la categoría PRO y el áccesit al avance técnico.
–– The fourth edition of the CPC RetroDev contest encouraged me into opening the project again and making it true. One year earlier I had showcased "Frogalot", born as a technical experiment: was it feasible a CPC version of "Nebulus" that were faithful to the original C64 version, that didn't shrink the size of the graphics and that could keep or even improve its performance? Despite ultimately being too obvious that I didn't have enough time to make "Frogalot" a truly playable game (I had to do everything in little more than a month, and in the end it just had eight levels, three out of them being just the same platform pattern repeated a dozen times) it received good outcomes: second prize in the PRO category and the best technical advancement award.
El primer boceto de la protagonista, la liebre Hecatia, dibujado el 17 de Septiembre por la noche. Nótese el parecido obvio con Sorceress, personaje del videojuego "Dragon's Crown", aquí dibujado por DeeperOcean
– The first rough sketch of the main character, Hecatia the hare, drawn on September 17th at night. Note the obvious likeness with Sorceress, character from the videogame "Dragon's Crown", depicted here by DeeperOcean
Así fue como me puse manos a la obra. Eran días difíciles: estoy a punto de completar la carrera de Ingeniería Informática en la UNED pero he tenido que hacer exámenes y trabajar en el proyecto de fin de grado hasta el último instante (¡y de hecho, ahora mismo aún tengo que hacer uno en Diciembre!) con el resultado desastroso de que para cuando por fin tuve las manos libres ya estaba a mediados de Septiembre, lo que me daba cinco semanas para hacer el juego y entregarlo antes de la medianoche del 26 de Octubre. Al menos el tema estaba relativamente claro desde el principio, especialmente dada la fecha de entrega, muy cercana al famoso Halloween de los anglosajones: magia y fantasía.
–– That was how everythign begun. Those were difficult days: I'm very close to completing the degree of Computer Engineering at the UNED but I had to take exams and work on the graduation project until the very last minute (and in fact, I have yet to take one more exam in December!) with the disastrous outcome that when my hands were free at last it was already mid September, so I only had five weeks to make the game and deliver it before October 26th midnight. At least the theme was relatively clear since the beginning, specially given the delivery date, very near to the Anglo-Saxons' Halloween: magic and fantasy.
El primer sprite de todos, muy tosco, y el segundo, mucho más satisfactorio, también dibujados durante aquella misma noche. La criatura aún no tenía nombre
– The very first sprite ever, very rough, and the second one, far more fulfilling, also drawn during that same night. The creature wasn't named yet
Todo empezaba dando a la protagonista una forma y una apariencia; pero con un solo personaje no se define un juego entero. Lo siguiente fue paradójicamente la elección de un estilo de texto, una fuente tipográfica para el juego. Siempre me ha gustado que el texto sea multicolor, pero parecía buen momento para probar algo más sofisticado, y entonces me acordé de "Stormlord" y su fuente proporcional dibujada por Hugh Binns.
–– The first step was to give the main character a shape and a look; but a character alone doesn't define a whole game. The next step was paradoxally the choice of a text style, a typographical font for the game. I always liked texts to be multicolour, but it seemed the right moment for trying something more sophisticated, and then I remembered "Stormlord" and its proportional font drawn by Hugh Binns.
El primer menú del juego, con una versión temprana de la fuente, y unos colores relativamente mal elegidos. Compárese con la fuente original de "Stormlord"
– The game's first menu, with an early version of the font, and a relatively badly chosen palette. Compare it against the original font from "Stormlord"
Me tocó dibujar todos los caracteres por mí mismo porque no podía utilizar la fuente original tal cual, en parte porque no era ético, en parte porque ésta presentaba problemas de geometría que eran incompatibles con las necesidades de mi videojuego, como por ejemplo poder escribir en líneas sucesivas sin que los extremos verticales de los caracteres de distintas líneas se tocasen. También necesitaba sombras y una paleta que pudiese manipular fácilmente en tiempo real, así que me tocó dibujarlo todo desde cero.
–– I had to draw all the characters on my own because I couldn't use the original font as it was, partly because it wouldn't be ethical, partly because it featured geometrical traits that were incompatible with my videogame's needs, such as the ability to write in immediate lines without the characters' vertical extremes touching. I also needed shadows and a palette that could be easily handled in runtime, so I had to draw everything from scratch.
Los rectángulos correspondientes a dos sprites inmediatamente consecutivos de una misma animación
– The rectangles belonging to two immediately following sprites in a single animation
Pero lo más importante era crear el propio motor tridimensional, al que llamé "Petiso3D" simplemente para abreviar. Una prioridad muy clara era superar el rendimiento de "Justin", que escribí hace once años primero para Amstrad CPC y luego para Sinclair Spectrum. El gran problema de "Justin" era que hacía lo mismo que "Knight Lore": usaba un búfer del mismo tamaño que el área de juego. Esto me facilitaba el trabajo (redibujar un espacio solamente requería redibujar los objetos que apareciesen en su interior) pero era poco eficaz: consumía mucha memoria (12k) y era redundante (los sprites se redibujaban enteros). Además las áreas mismas, los "rectángulos sucios" ("dirty boxes") se combinaban entre sí de una forma poco inteligente, pues mientras que era aceptable fundir dos rectángulos sucios fuertemente solapados, no lo era cuando lo único que compartían era un rincón diminuto.
–– But the most important step was to create the tridimensional engine itself, that I named "Petiso3D" simply as a shorthand. A very clear priority was to improve on the performance of "Justin", that I wrote eleven years ago for the Amstrad CPC first and the Sinclair Spectrum next. The big problem of "Justin" was the same from "Knight Lore": it used a buffer as big as the playing area. This made my work easier (redrawing a space only required redrawing the objects therein) but it wasn't efficient: it ate much memory (12k) and was redundant (sprites were redrawn whole). Besides, the areas themselves, the "dirty rectangles" or "dirty boxes" were merged together in a not very clever way, because while it was acceptable to fuse two strongly overlapping dirty rectangles, it wasn't when they only shared a tiny corner.
Un ejemplo animado de un algoritmo de ordenamiento de profundidad en acción. Fuente: Reddit, r/gamedev
– An animated example of a z-sorting algorithm in action. Source: Reddit, r/gamedev
También necesitaba un algoritmo de ordenamiento de profundidad ("Z-sorting") que diese buenos resultados, dado que el "bubble sort" clásico no solamente es lento por sí mismo, sino que además reacciona muy mal a las relaciones de orden intransitivas, y la profundidad es una de ellas: un objeto puede estar a la vez delante de otro en una dimensión y detrás de él en otra, lo que lleva a la rutina de ordenamiento a intercambiar las posiciones de dichos objetos repetida e inútilmente.
–– I also needed a Z-sorting algorithm that could provide good performance, because the classical "bubble sort" isn't just slow on its own, but it also behaves very poorly to intransitive relationships, and depth is one of them: an item can be at once in front of another one on a dimension and behind it on another one, and this leads the sorting routine to swap these objects' locations repeatedly and uselessly.
Comparación entre dos casos: en el primero es más conveniente fundir el rectángulo cian con el amarillo, y en el segundo es mejor dejarlos separados
– Comparison between two different cases: merging the cyan and yellow rectangles together is an improvement in the first case, but keeping them apart is better in the second one
El primer problema se vio resuelto gracias a las matemáticas. Al fundir dos rectángulos en uno solo nos vemos en la posibilidad de dibujar solamente una vez el espacio que ambos compartían, pero a su vez tenemos la obligación de dibujar espacios adicionales que antes los rectángulos no abarcaban. En consecuencia fundir rectángulos solamente es eficiente cuando el espacio compartido es mayor que los espacios adicionales. Calcular todas estas áreas es pesado, pero la tarea se simplifica grandemente si utilizamos las áreas de los dos rectángulos originales y la de la fusión: si la suma de las dos primeras es igual o mayor que la última, la fusión es la mejor opción; si la suma es menor, es preferible dejar los rectángulos originales sin fundir. Todo esto también sirvió para reducir el búfer de redibujado a un tamaño mucho más aceptable: 1536 bytes, la octava parte de los 12k usados por "Justin".
–– The first problem was solved thanks to mathematics. When we merge two rectangles into a single one we benefit from the ability to draw only once the space shared by both, but we also have the duty to draw additional spaces that weren't spanned by the previous rectangles. Thus rectangle merging is only performant when the shared space is bigger than the additional spaces. Calculating all these areas is heavy, but the task becomes greatly simplified if we rely on the areas of the two original rectangles and the fusion: if the sum of the former two is equal or bigger than the later, fusion is the better choice; if the sum is lower, we'd rather leave the original rectangles apart. All this work also helped shrinking the scratch buffer to a much more acceptable size: 1536 bytes, 1/8 the 12k used by "Justin".
Comparación visual de 'bubble sort' contra 'insertion sort'. Fuente: Wikipedia
– Visual comparison of 'bubble sort' against 'insertion sort'. Source: Wikipedia
El segundo problema encontró una solución sencilla y eficaz al emplear el método "insertion sort", que a pesar de mostrar un orden de complejidad idéntico al de "bubble sort" tiene dos ventajas fundamentales: por un lado hace menos escrituras, lo que le hace más rápido, y por el otro las operaciones de traslado de un objeto desordenado a su lugar correcto se hacen de un tirón en vez de paso a paso, lo que significa que las repeticiones redundantes y erróneas provocadas por las comparaciones intransitivas de la profundidad de los objetos desaparecen. Gracias a ello el "Z-sorting" pasó de consumir el 40% del tiempo ¡a solamente el 2%! Una mejora adicional fue la conservación de la lista ordenada (una lista ordenada de punteros, para ser más exactos) a cada paso: si los objetos no se movían, sus posiciones relativas en el ordenamiento tampoco cambiaban; y si alguno se movía, los cambios en el ordenamiento eran generalmente pequeños, lo que simplifica aún más la labor del método de ordenación.
–– The second problem found a simple and efficient solution in the "insertion sort" method; despite showing an identical order of complexity to "bubble sort" it displays two fundamental advantages: on one hand it does fewer writes, and is thus faster, and on the other hand the operations that shift an unsorted object to its correct location are done in a single go rather than in small steps, and this leads to the removal of the redundant and wrong repetitions caused by the intransitive comparisons of the objects' depths. Thanks to this, the "Z-sorting" went from spending 40% CPU time to ... only 2%! A further improvement was to keep the sorted list (a sorted pointer list, more exactly) through every step: if objects didn't move, their relative locations within the list didn't change either; and if any object moved, the changes in the ordering were generally small, thus simplifying even further the sorting method's work.
A dos semanas justas de la fecha de entrega, "Petiso3D" por fin estaba terminado y era eficiente
– Exactly two weeks before the deadline, "Petiso3D" was at last complete and efficient
Además había que escribir rutinas fuertemente optimizadas para dibujar los decorados (500 azulejos de 16x8 píxeles cada uno) mediante cadenas de LDI, y los objetos (sprites de tamaños arbitrarios que podían ser dibujados al derecho o al revés, enteros o cortados por los bordes...) mediante variaciones de la conocida secuencia óptima LD A,(BC): LD L,A: LD A,(DE): AND (HL): OR L: LD (DE),A... Todo esto llevó su tiempo y muchas pruebas, pero tras tres semanas de duro trabajo por fin "Petiso3D" era capaz de hacer todo lo que le ordenaba, y con una eficiencia notable: mover un sprite cuadrado genérico de 32x32 píxeles por la pantalla consumía algo menos de 20 milisegundos: es decir, podía moverse a los 50 frames por segundo usados por el monitor de tipo PAL del Amstrad CPC. Lo malo era que solamente me quedaban dos semanas para crear el juego propiamente dicho...
–– I also had to write strongly optimised routines to render the backgrounds (500 tiles of 16x8 pixels each) with strings of LDI and the objects (sprites of arbitrary sizes that might be drawn normally or in reverse, whole or clipped by their edges...) through variations of the known optimal sequence LD A,(BC): LD L,A: LD A,(DE): AND (HL): OR L: LD (DE),A... All this work took its time and many tests, but after three weeks of hard work "Petiso3D" was at last able to do everything I commanded, and with a noteworthy performance: moving a generic square sprite of 32x32 pixels through the screen spent a little less than 20 milliseconds: this means that it could move at the 50 frames per second used by the PAL-type screen from the Amstrad CPC. The downside was that only two weeks remained for the creation of the game proper...
Bocetos de la batalla final y un decorado forestal
– Sketches of the final battle and a forest scenery
Algunas de las ideas centrales del juego ya estaban relativamente claras desde bastante temprano. Por ejemplo, quería que el final del juego fuese una batalla contra un enemigo que atacase al jugador a distancia. Otras ideas se perdieron por el camino, como por ejemplo los espacios amplios a cielo abierto con árboles y arbustos. Y otras nacieron en sesiones de brainstorming con colegas como DracuOfTheFuture, el autor de la carátula, tales como la apariencia de los Perros Soldados, basados en los Gnolls de D&D.
–– Several central ideas of the game were already clearly defined from early on. For example, I wanted the end of the game to be a battle against an enemy that could attack the player from distance. Other ideas were lost on the way, such as the open outdoors locations with trees and bushes. And others were born from brainstorming sessions held with colleagues such as DracuOfTheFuture, the box art's author, who devised the look of the Dog Soldiers, based on the D&D Gnolls.
Ensayos y ejemplos de estancias de formas irregulares. Compárese con "Crafton & Xunk" e "Inside Outing"
– Tests and samples of irregularly shaped locations. Compare them against "Crafton & Xunk" and "Inside Outing"
También quería poder dar formas arbitrarias a las estancias: paredes abiertas, pasillos estrechos... Al final no las aproveché mucho pero Petiso3D es capaz de administrarlas debidamente. Hay libertad total para definir los fondos de las habitaciones, algo que siempre me gustó de juegos como "Crafton & Xunk" e "Inside Outing" al que lo único que le faltaba era tener suelos tan bellamente decorados como las paredes.
–– I also wanted to be able to set arbitrary shapes for the locations: open walls, narrow corridors... I ultimately didn't use them too often but Petiso3D is able to manage them properly. There's full freedom to define the room backgrounds, a thing I had always liked from games such as "Crafton & Xunk" and "Inside Outing" that only lacked floors as beautifully ornate as their walls.
La casa de un solo cuarto de Hecatia, y sus sprites aislados.
– Hecatia's one-room house, and its isolated sprites.
Así es como me habría gustado que fuese el principio del juego: que todo comenzase en la casita de Hecatia. De nuevo, tampoco pudo ser, porque escaseaban la memoria y el tiempo. Al menos me sirvió para hacer pruebas, como por ejemplo el cálculo correcto de los rectángulos sucios. en este caso obtenidos tras forzar el borrado de toda la pantalla y el redibujado exclusivo de los sprites.
–– This is how I would have liked the beginning of the game to look like: everything begins at Hecatia's little house. Again, it couldn't be, because memory and time were scarce. At least I could use it for testing purposes, such as the correct calculation of the dirty rectangles, in this case generated after forcing a screen erasure and redrawing only the sprites within.
Ensayos de los marcadores; abajo, el diseño final
– Score panel tests; on bottom, the final design
Incluso los marcadores del juego se vieron simplificados por mor de la necesidad. Los planes originales de adornarlos con bordes enrollados como los pergaminos que aparecen en el menú y en otros momentos del juego debieron sacrificarse en beneficio de la simplicidad y del buen uso del poco espacio disponible.
–– Even the score panels found themselves simplified for the sake of necessity. The original plans of decorating them with rolled borders as those from the parchments that show in the menu and other moments of the game had to be sacrified for the benefit of simplicity and the good management of the scarce available room.
Algunas pantallas del juego tal como fue entregado al concurso CPC RetroDev 2016
– Several screens from the game as it was delivered to the CPC RetroDev 2016 contest
Pero lo peor, lo más doloroso, fue quedarme sin memoria durante el desarrollo del mapa. Para entonces ya quedaban menos de dos días para la entrega, y la solución que le di al tremendo problema fue fulminante: eliminar todo lo que no fuese esencial. Desaparecieron los efectos de sonido, se esfumaron varios sprites tales como las estanterías con libros y el tocón, las habitaciones perdieron muchísimos muebles (en la primera versión todas las lámparas exigían apilar barriles, sillas y taburetes para alcanzarlas; al final solamente las dos últimas siguieron estando fuera del alcance normal del jugador) y los decorados se empobrecieron: por ejemplo, el espacio que separa las dos plataformas de la batalla final está totalmente vacío. Solamente después de la fecha de entrega resolví el problema del espacio de una forma mucho más eficiente: en vez de mantener dos copias del mapa entero, una inicial y una actual, solamente copié los datos de los objetos: así gané más de 2k, ¡espacio suficiente para casi 300 objetos! Pero ya era tarde y no había remedio: el jurado del concurso juzgará una versión recortada, incompleta de lo que "Hire Hare" debía haber sido.
–– But the worst to happen, the most painful, was to run out of memory during the development of the map. The deadline was already less than two days away, and the solution I devised for this tremendous problem was fulminant: annihilate all that wasn't essential. The sound effects disappeared, sprites such as the bookshelves and the tree stump vanished, the locations lost countless pieces of furniture (in the first version all the lamps required piling barrels, chairs and stools up to reach them; in the end, only the two last lamps stayed too high to be reached normally by the player) and the backgrounds got impoverished: for example, the gap between the final battle's two platforms is completely empty. Only after the delivery I was able to solve the memory problem in a much more efficient fashion: instead of keeping two copies of the whole map, an initial one and a current one, I copied only the objects' data: that's how I gained more than 2k, enough room for almost 300 objects! But it was too late and there was no remedy: the contest jury will judge a cut-down, incomplete version of what "Hire Hare" was intended to be.
"Black Lamp" y "Fox Fights Back" para el Commodore 64
– "Black Lamp" and "Fox Fights Back" on the Commodore 64
Finalmente, una vez más me quedé sin tiempo para hacer música en condiciones, así que hube de recurrir de nuevo a imitar canciones con cierta tradición dentro del mundillo. Si el año pasado fue Rob Hubbard y su famosa cuarta canción para "The Human Race" que tantos músicos de la demoscene del C64 copiaron y reprodujeron, ahora fue el turno de Tim Follin y Fred Gray y sus partituras para "Black Lamp" y "Fox Fights Back", cuyas bandas sonoras son a su vez refritos de temas muy conocidos, desde la Marcha Turca de Beethoven hasta "In the hall of the Mountain King" de Edvard Grieg, pasando por Mozart, Brahms y la anónima "Greensleeves". Al menos el juego no se quedó mudo.
–– Finally, once again I ran out of time to make proper music, so I had to resort once more to imitate songs with some tradition within the scene. If one year ago it was Rob Hubbard and his famous fourth song for "The Human Race" that so many C64 demoscene musicians copied and covered, it was now the turn of Tim Follin and Fred Gray and their scores for "Black Lamp" and "Fox Fights Back", whose soundtracks are themselves mashups of very known themes, ranging from Beethoven's Turkish March to "In the hall of the Mountain King" by Edvard Grieg, through Mozart, Brahms and the anonymous "Greensleeves". At least the game didn't end up mute.
César N.G., 5 Nov 2016
P.D.: el día despues de publicarse este artículo, "Hire Hare" ganó el segundo premio y el áccesit "Jon Ritman" al avance técnico, los mismos galardones que "Frogalot" había ganado en 2015.
–– P.S.: on the next day after this article was published, "Hire Hare" won the second prize and the "Jon Ritman" best technical achievement award, the same prizes "Frogalot" had won in 2015.