Este fin de semana pasado se celebró en la Universidad de Alicante la primera Game Jam. Una Game Jam es un evento que reune a desarrolladores amateur y no tan amateur con el objetivo de realizar un juego en 48 horas. Participamos formando un grupo de 2 programadores y una diseñadora, en total 3 personas. Comenzando el viernes a las 18:30, dimos rienda suelta a nuestras respectivas habilidades, y aunque el resultado no fue del todo satisfactorio, la experiencia ha servido para aprender y desarrollar más habilidad en el lenguaje de programación escogido: LUA. Sin más dilaciones, voy a hacer un resumen de lo que llevamos a cabo en esas 48 horas y luego un balance de lo que he aprendido participando en el evento y cosas que se mejorarán para la próxima edición. Test.
Tecnologías escogidas: LÖVE 2D y LUA
La tecnología escogida para desarrollar el juego fue el framework LÖVE 2D (https://love2d.org/), que usa un lenguaje de scripting llamado LUA (http://www.lua.org/); además hicimos uso de una librería en LUA llamada Zoetrope (https://bitbucket.org/klembot/zoetrope), que añade una capa de abstracción entre las funciones básicas de las que dispone el intérprete de LUA de LÖVE y el desarrollo del juego en sí. Este framework añade a LUA la posibilidad de programar con construcciones del lenguaje pseudo orientadas a objetos. Esto quiere decir que ciertas formas de diseñar propias de los lenguajes orientados a objetos se aplican, pero otras muchas no. Esto facilitó mucho la tarea a la hora de abstraer los elementos que formaron parte del juego, así como ayudar a desarrollar de manera más rápida y genérica las funcionalidades del juego. Zoetrope añade, además, múltiples herramientas y un muy útil sistema orientado a eventos, que implementa de manera más o menos adecuada el Observer pattern (http://en.wikipedia.org/wiki/Observer_pattern).
He de decir que era la primera vez que programaba en LUA en serio, y la experiencia ha sido muy grata. Es un lenguaje realmente potente, muy flexible y con una sintaxis fácil de aprender; por eso este lenguaje es el escogido en multitud de grandes proyectos de grandes empresas de la industria: World of Warcraft, Civilization V, Company Of Heroes, Crysis, y un largo etcétera. Además tiene una API de C bastante bien organizada, y el código fuente es intuitivo y fácil de seguir. El código fuente completo está disponible en http://www.lua.org/source/5.2/. Un muy buen recurso si estás interesado en el desarrollo de lenguajes de programación y compiladores.
Desarrollo del juego
Todo el código fuente escrito durante el fin de semana está disponible en el repositorio público de bitbucket:
https://bitbucket.org/albertofem/gamejam-ua
A pesar de que fue fácil hacerse con el lenguaje, y que más o menos ya dominaba el cómo se estructura un juego y qué tienes que tener en cuenta a la hora de desarrollarlo, surgieron varios problemas inesperados:
Rendimiento
Uno de los primeros fallos que pudimos notar fue el rendimiento de LÖVE 2D. El programar casi por completo la lógica del juego directamente en un lenguaje de scripting, además en un programa que no tiene hilos (no porque no LÖVE / LUA no los soporten, sino porque no dió tiempo a realizar un programar de estas características), causó que surgieran los típicos glitches relacionados con la velocidad a la que procesan todos los elementos en pantalla: colisiones que no llegaban a tiempo, pérdida de framerate, etc. Esto se arregló usando varios hacks, como por ejemplo corrigiendo aquellas colisiones que se notificaban tarde:
|
1 2 3 4 |
onCollide = function(self, other, horizOverlap, vertOverlap) self.y = self.y-vertOverlap -- Corregir desfase producido en X -- Resto de acciones a realizar end |
Diseño orientado a objetos
Como he comentado más arriba, Zoetrope contaba con ciertas facilidades para usar LUA como pseudo lenguaje orientado a objetos. Esto permitió desde el principio llevar a cabo un desarrollo separando cada elemento independiente del resto del sistema. Así pues, la mayor parte del código se desarrolló atendiendo al caso general, de modo que fuera reusable de manera global dentro del juego. Tenemos por ejemplo, la clase Enemy:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
Enemy = ActorAnimation:extend { actions = {}, invincible = false, hit_points = 100, gravity = GravityAwareActor:new(), onNew = function(self) self:addAction(self.gravity) if self.onCustomNew then self:onCustomNew() end end, onCollide = function(self, other, horizOverlap, vertOverlap) for index, action in pairs(self.actions) do if action.onCollide then action:onCollide(self, other, horizOverlap, vertOverlap) end end if self.onCustomCollide then self:onCustomCollide(other, horizOverlap, vertOverlap) end end, onUpdate = function(self) for index, action in pairs(self.actions) do if action.onUpdate then action:onUpdate(self) end end if self.onCustomUpdate then self:onCustomUpdate() end end, addAction = function(self, action) table.insert(self.actions, action) end } |
Esta permite definir distintos tipos de acciones genéricas comunes a todos los enemigos (en este caso que estén afectados por la gravedad), aparte de añadir cualquer otra acción específica de cada enemigo, dejando que el propio framework se encarge de actualizar, colisionar, etc. cada acción. Otras acciones que no dieron tiempo a implementar podrían ser: HitableEnemy, PathFindingEnemy, etc. Por ejemplo, dentro de la clase Player, que es similar, se implementaron acciones como JumpingActor, MovingActor, AttackingActor, etc.
Sin embargo, esto suposo un problema en el desarrollo a largo plazo, ya que ocupó demasiado tiempo del desarrollo, y al final no fue aprovechado todo el potencial que ofrecía. No obstante, estoy contento con el trabajo realizado, si bien en las últimas horas se fue un poco de madre y el código no fue escrito lo más elegantemente posible, pero bueno. Voy a dejar un tag de la versión original en Git, y en las próximas semanas iré dejando el código lo más decente posible, por si pudiera servir de ayuda en el futuro.
Problema de diseño
Otro problema, probablemente el más responsable de que el trabajo no saliera del todo satisfactorio, fue debido a la elección del tipo de juego a programar. Queríamos hacer un juego cargado de demasiados elementos, demasiados casos y comportamientos a tener en cuenta, y al final no pudimos llevar a cabo la mayoría de ellos. Esto hizo que el juego, cuya diversión estaba enfocada a contar con estos elementos, cuantos más mejor, en pantalla, resultara a la hora de la verdad no del todo divertido, eso sin contar los bugs a los que estaba expuesto por falta de tiempo en el desarrollo. Tampoco se tuvo tiempo para perfilar otros elementos importantes: control, música, puntuaciones, que suponga un reto, etc. Muchos participantes de la GameJam enfocaron sus juegos más al estilo minijuego, es decir, acciones repetitivas pero con mecánicas bien definidas y con buena respuesta. Es en este enfoque en donde se ve claramente la diferencia elemental entre lo entretenido que resulta un juego u otro.
Conclusiones y balance
Estoy muy contento con haber participado en esta GameJam. Me lo pasé realmente bien, y aprendí mucho sobre desarrollo de videojuegos, en especial LUA, eso por no hablar de la cantidad de gente genial y el buen rollo que se respiraba. Asistiré sin dudarlo a la próxima GameJam, esta vez intentando corregir la mayoría de errores cometidos, y porqué no, con más experiencia y conocimientos en este mundillo. Según los organizadores, la fecha aproximada será en Febrero de 2013, por lo qu antes tenemos, por ejemplo, la Ludum Dare en apenas 35 días, que puede servir para practicar y coger experiencia (http://www.ludumdare.com/compo/).
Os dejo con un vídeo en timelapse de todo el desarrollo (en programación). ¡Nos vemos en la próxima GameJam!