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) { 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]: Exibe a descrição de um comando, ou se invocado sem parâmetros, exibe uma lista com todos os comandos definidos. Comando clear: 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 exit Command Description: Terminates the program > |
Considerações Finais
Cabem aqui algumas considerações:
1 - É conveniente impedir que o game processe input do teclado enquanto o terminal estiver ativo. Fazemos isso simplesmente impedindo que qualquer outro processamento aconteça enquanto o terminal estiver ativo
protected override void Update(GameTime gameTime) { // Não processa input se o terminal estiver ativo if (_terminal.State == TerminalState.Active) return; // processa input HandleInput(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 ou
remova o terminal, adicione o componente e adicione o terminal novamente.
//Remove o terminal Components.Remove(ConsoleComponent.GetConsole()); //Adiciona o novo componente Components.Add(outroComponente); //Adiciona novamente o terminal Components.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 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.
Baixe aqui o demo.