O framework utilizado possui a seguinte estrutura, onde o director é responsável por rodar as cenas (scenes), que são responsáveis por criar camadas (layers), que, por sua vez, cria e gerencia sprites.
Basicamente, as Layers (camadas) gerenciam os enventos realizados pelo usuário, como apertar uma tecla específica, e define uma resposta à essa ação do usuário, sendo que essa resposta pode envolver outras partes da arquitetura.
Padrões voltados para ocorrências em assíncrono, a fim de sanar a necessidade de receber inputs não esperados pelo usuario ou estados inesperados da aplicação:
import signal
def signal_handler(signal_received, frame):
if signal_received is signal.SIGINT:
# erase the ^C on Terminal
print "\r "
exit(0)
if __name__ == "__main__":
resource.path.append('data')
resource.reindex()
font.add_directory('data/fonts')
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGSEGV, signal_handler)
Uma dificuldade que o time teve, foi a de conseguir um objeto keyboard que conseguisse manter os estados das teclas. A biblioteca pyglet fornecia uma solução, mas apenas a primeira instância de cada aplicação recebia as atualizações. A solução proposta então foi a de implementar esta instancia como singleton, a fim de garantir que sempre que a primeira instancia pudesse ler lida a qualquer momento da aplicação.
class EventHandle(object):
"""docstring for EventHandle"""
keyboard = None
def __new__(cls):
if not hasattr(cls, 'instance'):
cls.instance = super(EventHandle, cls).__new__(cls)
return cls.instance
…
keyboard = key.KeyStateHandler()
director.window.push_handlers(keyboard)
EventHandle().keyboard = keyboard
A evolução anterior do Pool de objetos se fez a necessidade de ter um objeto que mantivesse o controle de produções e fornecimento dos objetos da pool, assim como destrui-los corretamente,caso necessário. Desta forma, foi implementada uma Factory responsavel pelo fornecimento dos objetos. Como a Factory poderia ser acionada de varias cenas do jogo, ela foi transformada em um singleton para garantir esta acessibilidade, evitando o desperdicio de recursos ao se criar objetos.
class FireFactory(object):
"""docstring for FireFactory"""
ammo = {'hero': [], 'enemies': []}
def __new__(cls):
if not hasattr(cls, 'instance'):
cls.instance = super(FireFactory, cls).__new__(cls)
return cls.instance
@classmethod
def create_bullets(cls, bullet_type, qnt=50):
if bullet_type in hero:
for x in xrange(0, qnt):
cls.ammo['hero'].append(SpaceShipBullet())
elif bullet_type in rohenian:
for x in xrange(0, qnt):
cls.ammo['enemies'].append(RoheniansBullet())
Uma das Factories presentes no código apresentou a requisição de se saber se ela se encontrava ou não vazia. Desta forma, uma flag foi acrescentada nela para manter esta informação. Como esta informação pode ser obtida de forma assincrona, acabou se apresentando o padrão Dirty Flag no contexto.
class EnemyFactory(object):
"""docstring for EnemyFactory"""
enemy_list = {"Rohenian": [], "Aerolite": []}
def __new__(cls):
if not hasattr(cls, 'instance'):
cls.instance = super(EnemyFactory, cls).__new__(cls)
cls.empty = True
return cls.instance
@classmethod
def populate_enemy(cls, enemy_type, qnt=1):
if enemy_type in rohenian:
for x in xrange(0, qnt):
cls.enemy_list["Rohenian"].append(
Rohenian())
cls.empty = False
return
if enemy_type in aerolite:
for x in xrange(0, qnt):
cls.enemy_list["Aerolite"].append(
Aerolite())
cls.empty = False
return
assert 0, "Bad enemy creation: " + enemy_type