quinta-feira, 24 de fevereiro de 2011

Managers em Jogos

Acho que todo mundo que já programou algum jogo, por mais simples que seja, notou que ele pode ficar com o código bem confuso muito rápido. Aquele seu método Update principal fica com 200, 300, 600 linhas de código onde você processa entrada, atualiza física, mensagens, o jogados, inimigos, sons... Ai você percebe que tem que organizar a coisa ou vai enlouquecer, e perde mais tempo juntando pequenas coisinhas do que juntando teu jogo como um todo.

Ai surgem os Managers, ou gerenciadores: classes que são responsáveis por tudo o que envolva seu nome. Err... Nada melhor que um exemplo:

class InputManager
{
   void Update()
   {
      //Atualiza os estados de teclado e mouse.
   }

   bool IsKeyPressed(Key)
   {
      //Testa se uma determinada tecla foi pressionada.
   }

   event MouseClicked; //Dispara um evento sempre que o mouse for clicado.
}

O exemplo anterior trata de um gerenciador de entradas, basicamente mouse e teclado, ou controles, ou qualquer outra coisa, e expõe dois métodos e um evento. Isso é só um exemplo, um gerenciador de verdade exporá bem mais métodos e eventos que isso.
Note as vantagens desse tipo de abordagem:
1- Fácil expansão: Colocar um joystick ali no meio é tão fácil quanto adicionar um ou dois métodos bem simples.
2- Fácil manutenção: Se algo bugar, você sabe exatamente em qual gerenciador olhar, e até mesmo, em qual método o problema pode estar, muitas vezes, sem nem precisar utilizar o debugger.
3- Leitura tranquila: Seu update principal vai ficar enxuto, com várias chamadas para updates do gerenciador de entrada, de física, de som, de jogador, de inimigos, de tiros, etc. (Se todos os gerenciadores derivarem de uma classe base com um método virtual update fica ainda melhor.)

Mas como tudo na vida, esses gerenciadores devem ser usados com parcimônia e bom senso, ou eles vão entupir teu código e podem até piorar a situação geral. Imagine que você vai construir um jogo de tiro, poderá ficar tentado a pensar: "Cada tiro terá seu próprio gerenciador, assim só preciso chamar update neles sem muita preocupação." Errado!

Sua performance vai para o buraco se pensar dessa forma... Imagine 100 tiros voando numa guerra, cada um deles com seu próprio gerenciador, ocupando um monte de memória e fazendo o computador sofrer com um monte de chamados de métodos.

Nessa situação, assim como também os inimigos, sons e qualquer outra coisa que tenha o potencial de vir em quantidade, a melhor opção é criar um único gerenciador para cada categoria. Assim esse único gerenciador ficará responsável por atualizar posições de todos os seus "filhos", fazê-los pensar, gritar ou qualquer coisa que eles devam fazer.

No caso do tiro, sempre que você quiser que um tiro apareça na tela, ao invés de criá-lo você mesmo, peça ao seu gerenciador de tiro que adicione um naquela posição, naquela direção e deixe que ele cuide do resto. Ele só precisará avançar sua posição e verificar se ele bateu em alguma coisa para destruí-lo (e possivelmente disparar um evento para dizer pra quem interessar que o tiro foi destruído, onde e em que bateu e assim por diante.

Isso ainda pode diminuir o acoplamento de classes; o jogador que antes precisaria de apegar à entrada, armas, sons, tiros e física, agora só precisa da entrada e dos tiros, o restante só precisam se "engachar" aos eventos corretos para tomarem suas devidas ações.

Espero que tenham gostado, qualquer coisa comentem ai e podemos elaborar mais a ideia.

Abraços!

Nenhum comentário:

Postar um comentário