Terminal de comandos embutido no jogo
Autor: Márcio Freitas | Publicado: 2010-10-21T16:34:00-07:00




No jogo que estamos fazendo, precisei continuamente reinicar o jogo para forçar o reload do cenário, ou inserir DrawString()’s no método Draw() para acompanhar o estado de algmas variáveis. Percebi que já havia tido necessidades semelhantes neste e em outros projetos. Decidi então que era hora de incluir um terminal que me permitisse automatizar ações comuns durante o desenvolvimento.
Muitos jogos utilizam um terminal para permitir que os jogadores ativem cheats ou façam configurações rápidas no contexto do jogo. Não sei dizer qual foi o primeiro game a incorporar um terminal de comandos mas acredito que apareceram na época dos vovô Quake2(alguém sabe ?).
Seja para Forçar Spawn de um NPC, Mostrar/Ocultar geometria de colisão ,Carregar/Recarregar um level, Exibir estatísticas (como uso de memória e Frame Rate) ou qualquer coisa você precise, um terminal é uma maneira confortável de interagir com seu jogo com o máximo de controle.



Utilizando o TerminalComponent

O terminal foi implementado segundo o padrão de projeto Singleton,
A primeira coisa a fazer é inicializar o componente:
   protected override void Initialize(){ConsoleComponent.InitializeConsole(this, 25);…}  

Agora basta, obter uma instância e adicionar a lista de componentes :
   ConsoleComponent _terminal;…
protected override void Initialize(){ConsoleComponent.InitializeConsole(this, 25);_terminal = ConsoleComponent.GetConsole()Components.Add(_terminal);
…}
  

Por padrão a tecla F2 é utilizada para exibir/ocultar o terminal. Caso seu jogo já utilize esta tecla (pouco provável :D), você pode redefini-la através da propriedade ShowHideKey. Por exemplo, se quisermos utilizar a letra “T” bastaria o seguinte:
   protected override void Initialize(){ConsoleComponent.InitializeConsole(this, 25);_terminal = ConsoleComponent.GetConsole()Components.Add(_terminal);_terminal.ShowHideKey = Keys.T;
…}
  


Pronto! Nosso game já tem um terminal! Mas pra que seja realmente útil, devemos definir alguns comandos, pois são eles – os comandos – a razão de ser do terminal.

Adicionando comandos personalizados

Vamos definir um comando chamado exit cuja função é finalizar o programa.
A primeira coisa a fazer é criar um método que será executado quando o comando for chamado. Este método deve obedecer a assinatura do TerminalCommandDelegate, ou seja, retornar void e receber um string[] como parâmetro.


   private void ExitCommand(string[] args){&nbsp;&nbsp; Exit();}
  

Simples assim. Falta agora definir um comando no terminal que execute este método. Definir um comando é simplesmente passar para o método AddCommand o nome, uma descrição e um TerminalCommandDelegate (Delegate responsável pela execução do comando).Para o nosso comando exit, ficaria assim:
   protected override void Initialize(){ConsoleComponent.InitializeConsole(this, 25);_terminal = ConsoleComponent.GetConsole()Components.Add(_terminal);_terminal.ShowHideKey = Keys.T;_terminal.AddCommand("exit", "Terminates   the program", ExitCommand);
…}  

Podemos definir também comandos mais complexos que recebam parâmetros, simplesmente verificando o array de strings passado para o método do comando.Neste array, o primeiro elemento – índice zero – é sempre o nome do comando e os demais elementos, são os parâmetros passados para o comando.

Comandos pré-definidos
Além dos comandos que você definir para o terminal, existe sempre 2 comandos pré-definidos:
   Comando help   [comando]:&nbsp;Exibe a descrição   de um comando, ou&nbsp; se invocado sem   parâmetros, exibe uma lista com todos os comandos definidos. 
Comando clear:&nbsp;Limpa o terminal   removendo todas as linhas de texto.
  

Chamar o comando help passando o nosso comando exit como parâmetro, receberíamos como resposta, a descrição que criamos para o comando.
   help exitCommand Description: Terminates the program&gt;&nbsp;
  

Considerações Finais
Cabem aqui algumas considerações:
1 - É conveniente impedir que o game processe input do teclado enquanto o terminal estiver ativo.&nbsp; Fazemos isso simplesmente impedindo que&nbsp; qualquer outro processamento aconteça enquanto o terminal estiver ativo
   protected override void Update(GameTime   gameTime){&nbsp;&nbsp; // Não processa input   se o terminal estiver ativo&nbsp; if (_terminal.State   == TerminalState.Active)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;
// processa inputHandleInput(gameTime);…}
  

2 – O console deve sempre ser o último componente a ser desenhado , por isso, se durante a execução do jogo precisar adicionar um componente, adicione o componete com Insert() em alguma posição que não seja a última ouremova o terminal, adicione o componente e adicione o terminal novamente. 
   //Remove o terminalComponents.Remove(ConsoleComponent.GetConsole());//Adiciona o novo componenteComponents.Add(outroComponente);//Adiciona novamente o terminalComponents.Add(ConsoleComponent.GetConsole());
  

3 - Como o terminal é um Singleton podemos obter uma referencia em qualquer parte do programa e utilizá-lo como saída padrão de erros, mensagens e exceções.
   #if DEBUG&nbsp;ConsoleComponent.GetConsole().WriteLine(“Recarregando   cenário”, Color.White);#endif
  

Conclusão
É isso! O terminal será tão útil quanto os comandos que você definir para ele.. Deixe um comentário aqui ou mande um e-mail se este componente foi útil ao seu projeto ok ?

Baixe aqui o demo.