Varios días atrás tuve que hacer un juego simple, un memotest para ser exacto, para correr en unos «kioskos» para un cliente. Ya que tenía pendiente aprender a usar RubyGame, lo hicimos con este framework para ver que onda, ya que hasta ahora veníamos usando pyGame.
El juego salió super rápido, sin mayores problemas, pero la lógica de juego no me gustaba porque teníamos que andar trackeando el estado actual a mano, muchos ifs y comprobaciones que hacían del loop de juego un choclo de código.
Es por eso que me puse a ver un poco como aprovechar el tener bloques de código para encapsular la lógica del juego un poco más prolijo. Antes de comenzar encontré la gema Statemachine pero a primera vista no la entendí mirando los ejemplos 🙂 y luego de jugar un rato no me terminó de convencer ya que parece mucho más de lo que yo necesitaba.
El resultado de un par de horas de tirar «magia» fue poder definir la lógica de la siguiente manera (el ejemplo está simplificado, omitiendo los efectos y parte de la lógica) :
class Logic include StateMachine # Esperando interacción del usuario state :user_input do @events.each { |event| case event when MouseDownEvent selected event.pos when QuitEvent end_game end } end # Oculta las piezas seleccionadas cuando no hubo match state :clear do @selected.each {|f| f.hide } @selected = [] end # Cambio de estado transition :user_input, :clear do @selected.size == 2 end # Cambio de estado transition :clear, :user_input do true end end
Cada declaración de state
tiene el código que se debe ejecutar cuando estamos en dicho estado, mientras que las transition
son usadas automáticamente para saber a qué estado nos debemos mover. La primer transition
que retorne true
, se toma el estado destino y se asigna como el actual.
Por el lado del game loop, lo único que se debe hacer es llamar a un método que se encarga de ejecutar el estado actual y luego verificar si alguna transición retorna «true» y se cambia al nuevo estado.
class Game include Rubygame include Logic def event_loop loop do current_state return if game_ended? draw @clock.tick @screen.update end end end
En Game
hay otros métodos auxiliares como game_ended
, draw
y selected
, que no vienen mucho al caso en este momento.
El próximo paso ahora es limpiar un poco esto, ver si no hay una forma mejor de hacerla y publicar el esqueleto completo (la idea a futuro es tener un generator) para poder tener un mini framework para hacer juegos simples.
Si buscan un framework interesante les recomiendo Shattered Ruby (git repo), aunque al momento de escribir este post el sitio principal no responde.
One Comment
Des
Muy bueno, la verdad quedó re prolijo.