domingo, 15 de julho de 2012

Explicando o processo de desenvolvimento de jogos

Este artigo é dedicado à você que sempre quiz saber como são feitos os jogos.

Aqui eu não vou apresentar um tutorial completo sobre programação de jogos, mas sim tentar passar uma idéia de como são desenvolvidos os jogos eletrônicos, em especial os jogos de computador.

Obviamente, jogos de computador são um tipo de software, e o desenvolvimento de software não é como uma ciência exata em que se você seguir todos os passos religiosamente você acaba chegando num resultado exato e esperado. Aqui eu tentarei dar uma explicação bem resumida e superficial, como se fosse o processo todo "visto de cima", você terá uma idéia geral de como isso funciona.

Outro detalhe é que eu não vou entrar em detalhes específicos sobre nenhuma linguagem de programação, porém recomendo usar o C e C++ pois essas são as linguagens tradicionalmente utilizadas na indústria de jogos. Além disso, este artigo está dividido em partes, para facilitar o seu entendimento.

As dicas aqui são baseadas na minha própria experiência com programação de jogos relativamente simples em C/C++ e Java, e elas são genéricas, se aplicando na maioria das vezes tanto para jogos simples e pequenos como para qualquer outro jogo de qualquer tamanho. Importante lembrar que, essas dicas não estão necessariamente em ordem, ou seja, algumas etapas podem ser concluídas parcialmente ou completamente antes ou depois de outras etapas, sem grande impacto no resultado final. Afinal o desenvolvimento de jogos, assim como o desenvolvimento de qualquer software, costuma ser um processo iterativo.

Enfim, vamos começar apresentando as etapas do desenvolvimento de um jogo, segundo a minha própria experiência, e em seguida, os detalhes sobre cada etapa:




- Concepção: É o conjunto das idéias que inicialmente se tem sobre o jogo;
- Especificação: É o detalhamento completo das principais idéias iniciais;
- Escolha do Ambiente/Plataforma de Desenvolvimento: Qual sistema e linguagem;
- Criação dos Recursos: É a criação de gráficos, sons, e outros recursos usados pelo jogo;
- Definição da Arquitetura/Estrutura do Sistema: Detalhamento técnico do jogo;
- Programação: Implementação do sistema do jogo na linguagem e ambiente escolhidos;
- Testes: É testar o jogo e ver se tudo funciona conforme o esperado;
- Revisões: É voltar atrás nas etapas anteriores e revisar o jogo conforme necessário;
- Conclusão: É finalmente terminar o projeto e distribuir o jogo pronto por aí.





Concepção


Essa é sempre a primeira etapa. A concepção do jogo é em outras palavras a idéia inicial que se tem sobre o que futuramente será o jogo, é a essência do jogo. Se você não sabe sobre o que se trata o jogo, como vai desenvolvê-lo? É preciso ter uma visão clara do jogo na sua mente.

Sobre o que ele é? De que gênero é? É um jogo de ação? De estratégia? É um puzzle? Ou um adventure? Qual a perspectiva visual do jogador? O jogo é visto em 3D? De cima? De lado? É isométrico? Ou só tem texto? Para essas e outras perguntas você precisa ter as respostas muito antes de começar a pensar do desenvolvimento do jogo em si.

É bom também já ter uma historinha pronta para explicar o motivo da existência do jogador dentro do jogo. Todo jogo que se preze tem uma historinha por mais furada que seja, só para motivar o jogador. Os objetivos do jogo também precisam estar claros na sua mente. O que o jogador pode fazer no universo do jogo? O que ele precisa fazer para avançar? Etc, etc e etc... Se você pensa em desenvolver um jogo provavelmente você já deve ter concluído essa etapa, pelo menos parte dela.


Especificação


Essa etapa é aquela onde você descreve todas as características do jogo, nos mínimos detalhes, o máximo possível. Na etapa de concepção você teve mais ou menos um monte de idéias, mas agora você precisa ter uma visão muito mais aprofundada das idéias principais. É necessário especificar nos mínimos detalhes todo o universo do jogo, o seu funcionamento e o funcionamento de todas as suas partes componentes. Isso significa abrir o seu editor de texto e começar a escrever tudo com o máximo de detalhamento possível.

Exemplo: você precisa escrever toda a história do jogo, o desenrolar do enredo com início, meio e fim (caso o jogo tenha uma história complexa), nomear personagens, inimigos, objetos, etc. detalhar formas de interação do jogador com o universo, o sistema de física do jogo (quase sempre existe dependendo do gênero, mesmo que a simulação necessária seja mínima), o que acontece durante o jogo, o que acontece quando o jogador executa cada ação permitida, e todos os outros detalhes.

Pode ser que algumas idéias não estejam claras o suficiente, então é necessário repensar. Fica muito complicado você partir para o desenvolvimento do jogo sem ter ele já completamente especificado (detalhado). Essa parte basicamente consiste em escrever documentos de texto em português comum, de uma forma que você entenda como o jogo funciona em todos os aspectos, por todos os ângulos. Partindo daí, quando você já tiver tudo bem detalhado, já é possível ir para a próxima etapa com segurança.


Escolha do Ambiente/Plataforma de Desenvolvimento


Antes de começar a desenvolver o jogo você terá que escolher o ambiente e a plataforma de desenvolvimento, e todas as ferramentas necessárias. Ou seja, para qual sistema o jogo será desenvolvido? Para o Windows? Para Linux? Ou para outro sistema? Em qual linguagem será desenvolvido? Em C? Em C++? Em Java? Ou em outra linguagem? Quais bibliotecas adicionais (se necessárias) serão utilizadas para alcançar os objetivos técnicos do jogo, como por exemplo a parte gráfica, sonora e entrada/saída de dados?

Em Java, o framework da linguagem já fornece uma boa infraestrutura para implementar os sistemas gráficos e de som do seu jogo. Já em C/C++ você pode usar algumas bibliotecas existentes atualmente, como SDL, Allegro, OpenGL, DirectX, entre muitas outras. Essa escolha de bibliotecas vai depender muito do sistema, da linguagem, e do estilo do seu jogo. Por exemplo, o DirectX é só do Windows, já OpenGL é multiplataforma. SDL também é multiplataforma, mas é um tanto baixo-nível. Allegro é mais fácil porém menos popular do que SDL.

Enfim, você precisa ter resolvido todas essas questões referentes à plataforma e ao ambiente no qual o jogo será desenvolvido. Sem ter tudo isso já determinado, é impossível começar a trabalhar na implementação do jogo.

Um outro detalhe que pode também ser verificado nessa etapa é qual será a metodologia de desenvolvimento. A metodologia que eu recomendo pessoalmente é TDD (Test Driven Development), onde você escreve testes usando alguma biblioteca especializada (no C++, exemplos são CppUnit e CppUTest) e depois escreve código que passe em todos os testes, garantindo que o programa funciona corretamente conforme o esperado. Mas esse assunto está fora do escopo deste artigo, por isso não irei me aprofundar (mas quem sabe em um outro artigo futuro).


Criação dos Recursos


Muito provavelmente o seu jogo terá gráficos, sons, vídeos e outros tipos de recursos que serão utilizados pelo sistema. Pois bem, você precisa agora criá-los primeiro para que eles possam ser incluídos no jogo. Nesta etapa você utiliza ferramentas de desenho e editores gráficos para criar a arte gráfica do jogo, ou seja, toda a parte visual do jogo. Além disso, os sons podem ser criados usando certas ferramentas especializadas. Se você não tiver experiência com esse tipo de trabalho o negócio é buscar na internet esses recursos já prontos, o que geralmente não é uma boa idéia e muito provavelmente não é o que você quer.

Um detalhe importante é lembrar sempre do ambiente e da plataforma de desenvolvimento quando estiver criando recursos, pois o formato dos arquivos gerados devem ser compatíveis com o que as bibliotecas são capazes de manipular. Algumas bibliotecas multimídia só aceitam imagens no formato BMP, outras já aceitam JPG, PNG, GIF, entre outros formatos. Se o seu jogo é em 3D, um tipo de recurso usado são os arquivos de modelo dos objetos 3D. Um formato de som comumente aceito é o WAV, mas algumas bibliotecas são capazes de trabalhar com MP3, MIDI, entre outros.

Além de recursos multimídia como gráficos e sons, o seu jogo pode precisar de diversos outros tipos de recurso, como arquivos de texto, arquivos de configurações, scripts, e quaisquer outros tipos de arquivos de dados (seja em formato binário, ou não) que serão usados como fontes de dados pelo sistema do jogo. Exemplos de arquivos de dados que o seu jogo pode precisar são arquivos contendo o layout e a estrutura de fases, mapas, níveis, etc.

Essa etapa pode ser repetida diversas vezes durante o desenvolvimento. Eu mesmo já passei por isso, algumas vezes durante a programação resolvia criar novos recursos para um jogo e aos poucos ia incluindo eles no sistema.

Depois de ter à disposição todos os recursos do jogo (ou pelo menos uma boa parte deles), você já pode partir para a parte da definição da arquitetura do sistema do jogo, e depois para a implementação dos códigos-fonte do sistema do jogo na linguagem, ambiente e plataforma definidos na etapa anterior.


Definição da Arquitetura/Estrutura do Sistema


Essa etapa é muito importante pois vai influenciar profundamente na hora de programar. Aqui você vai precisar ter bons conhecimentos na área de Análise de Sistemas. Terá de pensar em como o sistema do jogo será estruturado, ou seja, você terá de fazer um "mapa" completo da arquitetura do jogo. Isso vai depender da linguagem de programação utilizada.

Por exemplo, em C, você define a arquitetura do sistema em termos de variáveis, estruturas de dados e funções, pois em C, sendo uma linguagem estruturada, o foco do programador é em criar estruturas de dados que representam as entidades do jogo (sejam elas visuais ou não) e funções que atuam sobre estes dados. Já em C++ e Java, você define a arquitetura do sistema em termos de hierarquias de classes, objetos e relacionamentos entre esses objetos, pois C++ e Java são linguagens orientadas a objeto. Para deixar mais claro o que eu quero dizer, pense no seguinte exemplo:

Um jogo de nave espacial, onde temos o jogador controlando uma nave e diversos tipos de inimigos controlados pelo sistema. Em C, provavelmente teríamos estruturas de dados contendo toda a informação necessária para representar o estado do jogador (isto é, da sua nave e outros conceitos pertinentes ao jogador), outras estruturas para representar os inimigos e todas as outras entidades do jogo, e teríamos funções para executar o jogo em si, acessando e manipulando as estruturas já definidas, como por exemplo uma função para atirar no inimigo, uma função para controlar a nave, uma função para desenhar a tela, uma função para mover um inimigo, etc. Já em C++ e Java teríamos provavelmente classes que encapsulam o estado e comportamento da nave, diversas outras que encapsulam o estado e comportamento dos inimigos, e outras que encapsulam e orquestram as ações e manipulam o estado do jogo e suas partes.


Programação


Essa é a parte crucial do desenvolvimento do jogo. Sem a programação, obviamente o jogo só vai existir no papel. Aqui você precisará ter bons conhecimentos na linguagem de programação escolhida (C, C++, Java, ou outra), no paradigma da linguagem escolhida (estruturado, orientado a objetos, ou outro), e nas disciplinas de Lógica de Programação e Algoritmos. Você terá que pegar toda a arquitetura do jogo que foi definida na etapa anterior e transformá-la em códigos-fonte escritos na linguagem de programação definida. Se for uma linguagem estruturada, terá que escrever as estruturas de dados, as funções, e tudo mais, e se for uma linguagem orientada a objetos, terá que escrever as classes com seus atributos e métodos, e estruturar todo o sistema sempre de acordo com tudo o que foi definido nas etapas anteriores.

Essa é a etapa mais técnica do processo, e talvez a mais demorada, porque basicamente o que ocorre aqui é a tradução de todas as idéias do jogo para o computador. Nessa etapa você pega todos os recursos que foram criados e "insere" eles de alguma forma dentro do programa, e isso se consegue em geral usando rotinas que carregam os arquivos para alguma estrutura interna (quando se usa uma linguagem estruturada) ou usando alguma classe que seja responsável pelo carregamento e gerenciamento de recursos (quando se usa uma linguagem orientada a objetos).

Além disso, nessa etapa você tipicamente precisará de um "loop" para "orquestrar" o fluxo de execução do jogo. A forma de funcionamento deste "loop de execução", também conhecido como "loop de eventos" (do inglês "event loop") deve ter sido especificada na etapa anterior. Este conceito é um pouco complicado tanto de explicar como de entender para quem não tem familiaridade com o conceito de eventos no contexto da programação, mas a seguir tentarei explicar da forma mais simples possível.

Explicando o Loop de Eventos

Durante a execução do jogo, sempre ocorrem diversas coisas que precisam executar mais ou menos em uma determinada ordem. Estas "coisas" que precisam acontecer estão encapsuladas em funções ou métodos de classes que serão invocados conforme necessário. Entre as coisas que podem acontecer durante a execução de um jogo são:

- Desenhar as coisas (objetos, textos, desenhos, etc) na tela;
- Observar eventos (por exemplo, verificar se o jogador pressionou alguma tecla);
- Processar eventos (ou seja, fazer alguma coisa dependendo de qual evento ocorreu);
- Tratar colisões (ver se um objeto está colidindo com outro e fazer algo a respeito);
- Mover objetos (se necessário);
- Repetir tudo isso.

Estes passos descritos acima compõem um loop de execução tipicamente encontrado nos mais diversos tipos de jogos eletrônicos, com suas próprias adaptações e peculiaridades que vão depender do tipo (estilo) do jogo e da complexidade da sua arquitetura. Explicando o exemplo acima em maiores detalhes:

Desenhar as coisas

Todo jogo precisa desenhar as coisas na tela, então esse passo existe em praticamente qualquer jogo. Primeiro você verifica quais objetos precisam ser desenhados num determinado momento em determinadas condições. Então, você faz com que o programa desenhe todos os objetos que precisam aparecer na tela naquele momento e naquelas condições, além de desenhar os outros gráficos e textos se existirem e caso seja necessário. O momento e as condições para desenhar cada coisa na tela são definidos por variáveis, estruturas e/ou objetos que contenham estas informações.

Observar eventos

Significa mandar o sistema verificar se existe algum evento aguardando ser processado numa fila de eventos. Um tipo de evento muito comum é o pressionamento de alguma tecla ou botão pelo jogador. Se o sistema verifica que alguma tecla foi pressionada, ele deve processar este evento dependendo de qual tecla foi pressionada.

Processar eventos

Isso quer dizer, se ocorreu algum evento, verificar de qual tipo ele é, e dependendo do tipo do evento, fazer alguma coisa. Por exemplo, se ocorreu um evento de pressionamento de tecla, o sistema verifica qual foi a tecla pressionada e reage, por exemplo movendo o personagem para alguma direção.

Tratar colisões

Aqui o sistema verifica se dois ou mais objetos estão colidindo entre si. Normalmente, detecta-se uma colisão entre dois objetos no momento em que eles estão ocupando o mesmo espaço ou compartilhando uma determinada área no espaço de coordenadas do jogo. Isso é muito variável e depende do tipo de jogo. Em alguns tipos de jogo nem sequer existe a detecção e tratamento de colisões por não haver necessidade disso. Por exemplo, um jogo de puzzle talvez apenas precise efetuar alguns cálculos para poder atualizar o estado do jogo, e não precisa tratar colisões entre os objetos no espaço.

Mover objetos

Dependendo do tipo de jogo, é necessário mover os objetos pelo espaço do jogo ao longo do tempo. Isso é feito nesta etapa do loop de execução. Aqui os objetos que possuem coordenadas devem ter suas posições atualizadas conforme necessário, da forma adequada, num determinado momento e em determinada condições. Em seguida o sistema repete o loop, que redesenha a tela e todos os objetos em suas novas posições, resultando na ilusão de movimento e de ação no jogo.

Resumindo o Loop de Eventos

O "loop de execução" é também conhecido como "loop de eventos" pois praticamente todos estes passos podem ser tratados como eventos de tipos diversos, e o loop pode acabar consistindo de apenas um único passo: tratar eventos. Então teríamos no sistema eventos de pressionamento de tecla, eventos de colisão, eventos de redesenhar a tela, eventos de mover objetos, etc. que seriam "disparados" pelos objetos do sistema e armazenados em uma fila, e o loop de execução apenas trataria cada um dos eventos na lista na ordem em que foram disparados (incluídos). Depois que um evento da fila é tratado, ele é retirado da fila e o próximo evento que estiver lá é tratado, e assim sucessivamente. Conforme o jogo segue sua execução e diversas coisas acontecem, mais e mais eventos serão gerados pelos objetos componentes e incluídos na fila para serem tratados, e o tratamento de eventos acaba funcionando como o cerne do sistema, o grande maestro que orquestra todo o fluxo de execução do jogo.


Testes


Durante a programação, você precisa testar o sistema para ver se tudo funciona corretamente conforme o esperado, e esta etapa é muito repetitiva e extremamente comum. A cada funcionalidade implementada é importante fazer diversas verificações para certificar-se de que o sistema se comporta da forma adequada e que não existem erros. Para isso, pode-se usar certas ferramentas para testes (o que é o mais recomendado) ou simplesmente jogar o que já estiver pronto e ver se tudo está ok.


Revisões


A etapa de programação não é a última e não termina assim da noite pro dia. Será necessário muitas vezes voltar atrás nas etapas anteriores e revisar o sistema e o jogo. Por isso temos esta etapa repetitiva, que é revisar a arquitetura, estrutura, recursos, e outros detalhes do jogo conforme for necessário.

Em alguns casos durante a programação, podem ocorrer problemas oriundos de alguma das etapas anteriores, que por algum motivo não foram previstos. Por exemplo, talvez você tenha especificado certos detalhes do jogo que por fim acabaram ficando extremamente difíceis ou complexos demais para implementar, ou que conflitam com outros detalhes relacionados. Nesses casos é necessário alterar as especificações para contornar os problemas. Outro motivo que pode causar a revisão do jogo e que acontece freqüentemente é você ter idéias novas durante a programação e ter que alterar especificação, arquitetura, estrutura e/ou recursos para acomodar estas novas idéias. E isso é perfeitamente normal.


Conclusão


Bom, depois de todo esse trabalho, que pode durar semanas, meses, ou anos de programação, testes e revisões, o jogo fica prontinho e o projeto é encerrado, sendo possível então a distribuição do jogo por aí. Mesmo depois de lançado, um jogo pode acabar posteriormente recebendo inúmeras revisões, motivadas por bugs que por algum motivo não foram encontrados durante as etapas de testes e acabaram por fim sendo encontrados pelos jogadores. Então os códigos-fonte do jogo serão reabertos e avaliados, os bugs resolvidos e uma revisão será lançada contendo as correções feitas... e basicamente é isso!

O desenvolvimento de jogos como citado no início do artigo, não é uma ciência exata, então cabe a você tomar as decisões certas e o rumo mais adequado durante a criação do seu jogo. Este artigo não pretende ser um passo-à-passo para desenvolver jogos, mas sim um conjunto de dicas úteis e que funcionaram diversas vezes durante a minha própria experiência com o desenvolvimento deste tipo particular de software.

Espero ter esclarecido as suas dúvidas ou sanado a sua curiosidade sobre como funciona o processo de desenvolvimento de jogos!

Tem dúvidas, sugestões e/ou correções a este artigo? Por favor deixe seu comentário!