Artigo: Boas Práticas em Programação de Jogos

Se você escreve código há um bom tempo, provavelmente já percebeu que cada pessoa tem o seu jeito peculiar. Em programação de jogos, não é diferente.

Também pode ter acontecido de você e um amigo conversar sobre como seria a melhor forma de fazer isso.

Veremos nesse artigo algumas das boas atitudes que programadores podem tomar ao escrever código.

Nós, redatores e participantes do blog, falamos várias vezes em tutoriais, entrevistas e podcasts sobre a importância do programador ter como hábito se atualizar. Comentamos que é muito válido procurar por mais conhecimento e por desenvolver boas práticas de programação. Isso gerou dúvidas recorrentes na comunidade leitora. Como eu devo escrever meus códigos? Com o que eu devo me preocupar quando eu programo?

leitoresAtendendo a pedidos de vários de nossos fiéis leitores, o artigo de hoje tem como tema boas práticas de programação. Os pontos aqui abordados não foram tirados de livro algum. Foram todos cuidadosamente selecionados com base na minha experiência de aproximadamente 12 anos em programação, não apenas de jogos eletrônicos. São atitudes que os programadores normalmente acabam adquirindo com o tempo, sem necessariamente aderir ou usar o que se lê em livros técnicos.

Iniciando o artigo de hoje, podemos pensar que boas práticas de programação são um conjunto de atitudes tomadas por programadores que pensam em produtividade e eficiência computacional. Isso significa que, se bem aplicadas, essas atitudes resultam em legibilidade do código e velocidade na execução dele com uso de pouca memória. Ambos são importantíssimos no desenvolvimento de jogos. Vamos explorar essas atitudes.

 

Programação de jogos legível

Quando se trabalha em equipe, é indispensável escrever as linhas de código de uma forma fácil de entender. Os programadores de uma equipe de desenvolvimento não têm sempre o mesmo nível de aptidão à tarefa, além de cada um ter a sua forma própria de programar. Essa diversidade faz com que, às vezes, um código de fácil entendimento para uma pessoa seja muito complicado para o resto da equipe entender.

Cat_NoQuem é mais conservador, nesse momento, pensará da seguinte forma: “eu não trabalho em equipe, logo, eu não preciso me preocupar com legibilidade de código”. Se você pensou mais ou menos assim, eu o aconselho fortemente a pelo menos ler as próximas frases. Quando o seu jogo começa a agregar novas funcionalidades, o seu código se torna automaticamente mais carregado. O programador até pode entender o seu próprio código carregado, porém, quando ele fica alguns dias sem programar, ele acaba se perdendo no seu próprio código. Isso quando o programador não se perde mesmo programando todos os dias. Isso é natural, afinal não existe a necessidade do programador decorar todo o código do jogo. Possivelmente, você mesmo já passou por esse problema.

Programar de forma legível é muito importante, sendo você o único membro da equipe ou não. Existem boas práticas que ajudam os programadores a entenderem um código, indiferente de seus gostos pessoais. Eu elenquei essas:

Indentação: essa é a atitude que normalmente vem primeiro na cabeça de todo programador. Acho que posso afirmar com convicção que essa é a base para o bom entendimento de um código. Um código mal indentado faz qualquer programador pirar. Então, se você abriu um novo bloco ou estrutura, nunca se esqueça de aumentar a tabulação na próxima linha. Um código bem indentado é o MÍNIMO que TODO programador deve fazer.

Figura 1 - Indentação
Figura 1 – Indentação

Convenção da linguagem: cada linguagem tem uma forma mais ou menos padrão de se programar. Nomes de constantes em C/C++ e Java, costumam ser em caixa alta (“IMPAR”, por exemplo). Nomes de métodos em C++ e Java, costumam iniciar com letra minúscula e com separador de palavras em maiúscula (“realizaDivisaoDeNumeros”, por exemplo). Nomes de funções e variáveis em C, normalmente têm as palavras separadas pelo caractere “_” (“realiza_divisao”, por exemplo). Programe parecido com a comunidade e o seu código será aceito por ela.

Funcionalidades da linguagem: utilize as funcionalidades da linguagem a seu favor. Se a linguagem é orientada a objetos, implemente o seu jogo utilizando conceitos de orientação a objetos, como herança, por exemplo. Se a linguagem oferece bibliotecas com estruturas de dados pre-implementadas, utilize-as sem medo. Pense que se os mantenedores de uma linguagem de programação incluíram um determinado recurso, então eles querem que os programadores o utilizem. Se você utiliza bem os recursos de uma linguagem de programação, então você já está programando de modo otimizado.

quebra-cabecasModularização: divida os procedimentos do seu programa em módulos. Quanto mais subdividido for o seu programa, mais fácil fica entender a lógica. Não tenha medo de criar mais funções e métodos. Além de aumentar a legibilidade, você evita a reprogramação de uma mesma tarefa e facilita a localização e correção de erros.

Comentários: comentar um código não significa que você perderá produtividade. Normalmente, esse é o primeiro pensamento de um programador que começa a se descobrir. Imagine o quanto um comentário agiliza o entendimento de um método já implementado e em funcionamento. Agora imagine que você precisa entender para quê serve um método implementado por outra pessoa … você prefere entender por um comentário no código ou por uma engenharia reversa? Comentários nunca são demais. Utilizá-los antes de cada método, descrevendo os parâmetros, retorno e qual a sua função, é uma atitude sábia.

Figura 2 - Tirinha sobre comentários (Vida de Programador)
Figura 2 – Tirinha sobre comentários (Vida de Programador)

 

Programando códigos eficientes

Eu já toquei várias vezes nesse assunto em tutoriais do blog. Acho que por si só, todo bom programador percebe a importância de escrever códigos com execução eficiente. Citar dois problemas já é suficiente para alertá-los sobre a importância desse tema: LAG (latência no jogo) e sobrecarga de memória.

Quando você pensa em eficiência de execução de um código, normalmente você se depara com duas métricas a serem otimizadas: quantidade de instruções executadas para conclusão de tarefas e quantidade de memória utilizada. Naturalmente, todo programador sempre busca otimizar ambas, mas nem sempre isso é possível. As vezes, você precisa optar por uma solução que consome mais memória para obter mais velocidade na execução, por exemplo. Porém, comentarei aqui, práticas que otimizam um sem influenciar no outro ou práticas que otimizam ambos.

Métodos e variáveis estáticos: sempre quando possível, utilize métodos ou variáveis estáticos. Contextualizando, você declarou uma variável comum em uma classe. Assim, quando você cria objetos dessa classe, você aloca uma região na memória diferente para cada variável de cada objeto criado. Quando você declara uma variável estática, você aloca uma única região de memória, independente da quantidade de objetos instanciados. Dessa forma, quando você tem cinco objetos instanciados, por exemplo, quando uma variável da classe não é estática, você tem cinco regiões de memória alocadas para ela. Quando a variável é estática, você tem apenas uma região de memória alocada, mesmo existindo cinco objetos instanciados. Acho que eu não preciso explicar o porque utilizar métodos e variáveis estáticos, né? A Figura 3 ilustra a explicação dada.

Figura 3 - Variável estática
Figura 3 – Variável estática

Saber utilizar estruturas de dados: é necessário conhecer bem sobre as estruturas de dados e saber quais utilizar em cada momento. Se você for utilizar estruturas pré-implementadas por uma biblioteca, conheça como elas funcionam internamente. Listas ligadas, por exemplo, apesar de ser fácil utilizá-las, elas são lentas para manipulação de dados e podem trazer o tão indesejado LAG. Se você utiliza um motor de desenvolvimento de jogos que já tem algumas estruturas implementadas, tenha preferência por utilizar as estruturas do motor. Veja a árvore com bons olhos, pois ela será uma companheira recorrente no desenvolvimento de jogos.

Figura 4 - Estrutura de dados árvore
Figura 4 – Estrutura de dados árvore

Conhecer algoritmos para solucionar problemas diversos: muitos problemas possuem soluções mais rápidas do que aquelas do senso comum. O problema de ordenação de números, por exemplo, possui algoritmos com desempenho computacional muito superior (quicksort e mergesort) aos algoritmos desenvolvidos por quem está começando a aprender programação (bubblesort e insertionsort). Sobre quais “problemas diversos” você precisa aprender? Comece pelos mais básicos como busca em vetor ordenado (melhor solução por busca binária) e vai partindo para os mais difíceis. Não fique parado achando que já sabe tudo.

Conte instruções: um programa com muitas linhas de código não necessariamente tem execução lenta. Da mesma forma, um programa com poucas linhas de código não necessariamente tem execução rápida. O ponto chave para velocidade de execução está na quantidade de instruções que o processador executa. Uma estrutura de repetição com 2 linhas de código pode executar mais instruções do que 7 linhas de código corrido, por exemplo. É interessante você avaliar a quantidade de instruções de uma solução adotada por você e se perguntar: “E se eu fizesse de outro jeito? Será que essa outra forma de resolver não faz a mesma coisa com menos instruções?”.

Desalocação de recursos: na minha opinião, essa atitude é a mais negligenciada, apesar de ser uma das mais cruciais. Instanciou um objeto, libere-o da memória quando não for mais utilizá-lo. Se você não se preocupa com desalocação de memória, com o passar do tempo, o jogo vai consumindo cada vez mais memória até travar. Cada objeto alocado precisa ser desalocado. Se você não pensava nisso, sugiro fortemente começar a pensar. Apesar de existirem alguns motores gráficos que realizam a desalocação de memória automaticamente, como o caso do Cocos2d-x, você precisa ter em mente que essas rotinas estão sendo executadas para cada objeto instanciado.

 

Para finalizar, mostrarei outras duas ótimas práticas que não tem ligação direta com legibilidade nem com código eficiente.

projetoProjete seu código no papel: essa é outra boa prática que muitos programadores negligenciam. É bem normal o programador iniciar abrindo o editor de código e sair compilando antes mesmo de saber certinho o que resolver e como resolver. Isso não é o certo a se fazer. Antes de você iniciar a programação de um módulo do seu software, é de muita valia você fazer um diagrama de classes no papel. Esse simples ato fará você se deparar com muitos problemas que você não enxerga estando com a lógica somente na cabeça. Dedique 20 minutos desenhando um diagrama de classes e projetando a lógica antes de escrever código para evitar 5 horas de retrabalho apagando código errado e programando novamente da forma certa.

Utilize um programa de versionamento: saiba que imprevistos acontecem com todas as pessoas. Queimar um HD, perder um pendrive, apagar sem querer uma pasta importante são apenas exemplos de situações catastróficas que podem acontecer com qualquer um. Agora pense que todo o código do jogo que você dedicou meses pode estar nesse HD queimado. Além disso, você pode agregar uma lógica falha no seu sistema que conduz a sua execução para a ruína. Uma solução é deixar o código exatamente como ele estava há dois dias, por exemplo. A nuvem pode ser uma ótima aliada para evitar esses dois problemas. Seja sábio e utilize um software de versionamento, como, por exemplo, o git.

 

timeApensar das boas práticas mostradas nesse artigo parecerem óbvias, muitos programadores não a incorporam para o bem próprio e do grupo que também tem acesso ao código. Viver sem realizar boas práticas na programação, na verdade, não te faz um péssimo programador. Elas apenas ajudam você e os revisores do código a otimizar tempo e a deixar o aplicativo mais eficiente em sua execução.

Seja um programador responsável e competente. Realize as boas práticas aqui explicadas.

Santiago Viertel

Formado em Bacharelado em Ciência da Computação (UDESC), mestre e doutorando em Análise de Algoritmos (UFPR). Foi programador da Céu Games por 8 anos. Possui a preferência por jogos de estratégia e de tiro em primeira pessoa. Jogando bastante DotA 2, Left 4 Dead 2 e Age of Empires II HD.

One thought on “Artigo: Boas Práticas em Programação de Jogos

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *