Desenvolvimento de Jogos: Mais difícil do que se pensa.

Publicado dia 22 de Maio de 2019 por Tiago Silveira

     Em tempos passados, a engenharia de jogos era principalmente sobre otimização de baixo nível-código de escrita que rodaria rapidamente no computador de destino, alavancando pequenos truques inteligentes sempre que possível. Agora, o principal desafio técnico é simplesmente fazer com que o código funcione para produzir um resultado final que tenha alguma semelhança com a funcionalidade desejada. Para programação, usamos um ambiente de desenvolvimento de compilador como o Unity ou a Unreal Engine, que, basicamente, são softwares que permitem o desenvolvimento completo de um jogo. A maioria dos jogos agora é escrita principalmente em C#. O Unity é o compilador mais popular que temos em PCs (embora a Unreal Engine, seja uma alternativa competitiva). Normalmente, um desenvolvedor de um projeto grande, leva entre um a quatro anos para conseguir finalizar o projeto. Por exemplo, a maioria dos estúdios se beneficiaria da capacidade de construir grandes malhas 3D contínuas, com vários artistas trabalhando na mesma malha de uma só vez - ou métodos de edição de malhas triangulares para garantir que as rachaduras e buracos não apareçam. Assim, precisamos aumentar esses pacotes de conteúdo com nossos próprios plugins e ferramentas de pós - processamento, que em geral serão mal integradas e carentes de recursos, e podem apresentar problemas de robustez. Às vezes, para construir a geometria do mundo, apenas escrevemos nossos próprios editores específicos de domínio do zero (Warcraft e UnrealEd são exemplos disso).
     Historicamente, a situação em relação às ferramentas de gerenciamento de ativos também foi ruim. Ultimamente, algumas empresas aumentaram para fornecer controle de ativos especificamente para projetos de jogos. No lado da programação, nossos ciclos de compilação / edição / depuração geralmente são longos demais. Muitos jogos demoram meia hora ou mais para compilar ao iniciar do zero, ou quando um arquivo principal de cabeçalho C é alterado. Em geral, o C parece encorajar tempos de construção longos. Outra maneira de atacar o problema de compilação é usar uma ferramenta de terceiros para distribuir compiladores em várias máquinas (um desses produtos é Incredibuild). O tempo de inicialização normalmente pode ser de três minutos para uma compilação de depuração com arquivos de dados grandes para os quais a otimização de tempo de carregamento não foi feita.
     Durante o desenvolvimento, muitas vezes temos que construir o jogo para todos os tipos de compilação (Debug, Release) para todas as plataformas de destino (PC, Playstation 4, Xbox One, Mobile) antes de confirmar nossas alterações no controle de origem. O programador pode estar facilmente à espera de horas, por isso, há uma forte motivação para verificar alterações no código com a menor frequência possível. Mesmo se este for o caso, o programador ainda pode enfrentar grandes dificuldades na integração do módulo de terceiros com o resto do jogo. Muitas vezes, a API (application program interface) é difícil de lidar, pois incorpora alguns modelos conceituais que são inadequados para a maneira como seu jogo precisa funcionar. Geralmente, são necessárias camadas de cola espessa entre o código do jogo principal e a API de terceiros.
     Os produtos disponíveis cobrem estas áreas: áudio, baixo nível (os produtos foram muito bem sucedidos), rendering, low-level (muito bem sucedido), renderização, gerenciamento de cena (sucesso misto), detecção de colisão e física (apenas um pouco bem - sucedida, mas é muito difícil escrever esses sistemas por conta própria, então há uma vitória significativa para ferramentas de terceiros aqui), rede, animação esquelética e alvos de metamorfose (muito bem sucedidos), armazenamento persistente de objetos (sucesso misto), e linguagens de script (sucesso misto). Como os jogos são complicados e exigem conhecimento técnico aprofundado, pode ser difícil usar esses componentes de terceiros: muitas vezes o programador deve ter muita experiência no domínio do problema para entender como interagir com o produto com êxito. Mesmo se este for o caso, o programador ainda pode enfrentar grandes dificuldades na integração do módulo de terceiros com o resto do jogo. E como os jogos exigem muito da CPU (assim como da GPU), muitas vezes acontece que o componente de terceiros apresenta um gargalo significativo no desempenho de alguns cenários de entrada - e o programador deve corrigir essas situações ou contorná-las.
     Um programador não será competente em um jogo moderno sem uma compreensão decente da álgebra linear básica, assim como a geometria em 2D e 3D. Frequentemente, usamos representações 4D para operações básicas (coordenadas homogêneas 4D para transformações lineares gerais e quatérnios para representar rotações), de modo que a capacidade de raciocinar sobre dimensões superiores é extremamente útil. Para muitas tarefas de renderização, a matemática de processamento de sinal é muito importante (tanto o processamento de sinal linear, como o estudo mais obscuro de harmônicos esféricos.) Um bom programador de motores deve ter familiaridade com um grande número de algoritmos. Fornecedores de hardware gráfico, como AMD e NVIDIA, criam ferramentas de perfil específicas para gráficos, assim como os fabricantes de alguns consoles de jogos. Os jogos, de forma geral, sempre evoluíram para aumentar a complexidade técnica para dar aos jogadores coisas que eles nunca experimentaram antes. Assim, os desenvolvedores de jogos carregam muito risco técnico (você não pode programar com precisão o desconhecido ou prever como ele irá interagir com o resto do sistema), bem como o risco do design do jogo (como será esse recurso nunca implementado para o usuário final? Será que valerá todo esse trabalho que estamos tomando para implementá-lo?" Os algoritmos mais necessários executam tarefas como particionamento espacial, clustering (conjunto de técnicas de prospecção de dados que visa fazer agrupamentos automáticos de dados segundo o seu grau de semelhança), e interseção e recorte de primitivos geométricos. Durante anos, foram feitas pesquisas acadêmicas para encontrar e modificar algoritmos apropriados. No entanto, um mecanismo de jogo deve atender aos requisitos simples em tempo real, e a maioria dos trabalhos acadêmicos nas áreas relevantes é voltada para a computação em lote. (A maioria das pesquisas anteriores em gráficos foi aplicada à renderização cinematográfica offline). Esses algoritmos não se adaptam como os jogos estão começando a ser levados a sério pela comunidade acadêmica. Isso está começando a mudar, mas a maioria das pesquisas acadêmicas ainda é direcionada em direções que não nos fazem muito bem.
     Por um tempo nos concentramos principalmente em gráficos, que é uma simulação de como a luz se comporta no mundo do jogo. Mas agora estamos entrando em um momento em que as partes da simulação que governam física e inteligência artificial podem ser mais importantes para a qualidade de experiência do usuário final do que os gráficos. Como a IA generalizada é um problema não resolvido, ninguém sabe como será no futuro. Trabalhar em física nos ensinou sobre alguns problemas que podem ser generalizados como pertencentes a todo tipo de sistemas complexos simulados que evoluem no tempo. A simulação de um sistema complexo geralmente envolve a integração de quantidades ao longo do tempo usando métodos numéricos. (Se/então as declarações criarem descontinuidades, a menos que façamos um esforço explícito de outra forma ; portanto, devemos ter cuidado com as instruções if/then ao trabalhar com simulação de baixo nível!) Para ajudar a manter as coisas integráveis, eventos mundiais significativos, incluindo decisões de IA precisa ocorrer em um nível superior ao integrador básico: isto é, eles não podem simplesmente chutar sem aviso e mudar o estado do mundo. Uma vez que tenhamos feito tudo isso, precisamos nos preocupar com a rigidez - o fato de que apenas ajustando as constantes, você pode fazer com que a simulação se torne instável. Para contornar esse problema, precisamos ser bons em selecionar interações insignificantes para diminuir o tamanho do problema. Estamos sempre tentando empurrar a CPU o máximo que podemos, então a criação de perfil é muito importante. Os jogos exibem um comportamento fortemente modal baseado em condições dinâmicas ( em um momento, o envio de triângulos ao hardware gráfico pode ser um gargalo no desempenho ; no momento seguinte, detectar colisões entre entidades do jogo pode ser o problema.
     Com isso, concluímos que, embora jogar seja divertido, é preciso ter um conhecimento técnico em diversas áreas, que vão além da programação. É preciso uma análise de requisitos, cálculo, física, design, som, inúmeros testes, entre outras coisas. Mas acima de tudo, é preciso estar sempre antenado no que está em alta no quesito “Desenvolvimento de Games”, pois a cada dia que passa, uma nova tecnologia aparece. E como numa corrida, ganha quem chegar primeiro.

Fontes e referências

Jonathan Blow - "Game Development: Harder Than you think"