O objetivo deste tutorial é ensinar programação de jogos 2D e linguagem C através de exemplos. O autor costumava programar jogos em meados da década de 1980 e foi designer de jogos na MicroProse por um ano nos anos 90. Embora grande parte disso não seja relevante para a programação dos grandes jogos 3D de hoje, para pequenos jogos casuais será uma introdução útil.
Implementando Snake
Jogos como cobra, onde objetos estão se movendo sobre um campo 2D, podem representar os objetos do jogo em uma grade 2D ou como uma matriz de objetos de dimensão única. "Objeto" aqui significa qualquer objeto do jogo, não um objeto como usado na programação orientada a objetos.
Controles do jogo
As teclas são movidas com W = para cima, A = para a esquerda, S = para baixo, D = para a direita. Pressione Esc para sair do jogo, f para alternar a taxa de quadros (isso não é sincronizado com a tela, portanto, pode ser rápido), a tecla Tab para alternar as informações de depuração ep para pausá-la. Quando pausada, a legenda muda e a cobra pisca,
Na cobra, os principais objetos do jogo são
- A cobra
- Armadilhas e frutas
Para fins de jogo, uma variedade de entradas segurará todos os objetos do jogo (ou parte da cobra). Isso também pode ajudar ao renderizar os objetos no buffer de tela. Eu projetei os gráficos para o jogo da seguinte maneira:
- Corpo de cobra horizontal - 0
- Corpo de cobra vertical - 1
- Cabeça em 4 x rotações de 90 graus 2-5
- Cauda em 4 x rotações de 90 graus 6-9
- Curvas para mudança de rota. 10-13
- Apple - 14
- Morango - 15
- Banana - 16
- Armadilha - 17
- Visualizar o arquivo de gráficos de cobra snake.gif
Portanto, faz sentido usar esses valores em um tipo de grade definido como bloco [WIDTH * HEIGHT]. Como existem apenas 256 locais na grade, escolhi armazená-lo em uma matriz de dimensão única. Cada coordenada na grade 16 x16 é um número inteiro de 0 a 255. Usamos ints para que você possa aumentar a grade. Tudo é definido por #define com WIDTH e HEIGHT, ambos 16. Como os gráficos de cobra têm 48 x 48 pixels (GRWIDTH e GRHEIGHT # define), a janela é inicialmente definida como 17 x GRWIDTH e 17 x GRHEIGHT para ser apenas um pouco maior que a grade.
Isso tem benefícios na velocidade do jogo, pois o uso de dois índices é sempre mais lento que um, mas isso significa que, em vez de adicionar ou subtrair 1 das coordenadas Y da cobra para se mover verticalmente, você subtrai WIDTH. Adicione 1 para mover para a direita. Por mais furtivo que seja, também definimos uma macro l (x, y) que converte as coordenadas x e y em tempo de compilação.
O que é uma macro?
#define l (X, Y) (Y * LARGURA) + X
A primeira linha é o índice 0-15, o 2º 16-31, etc. Se a cobra estiver na primeira coluna e se movendo para a esquerda, a verificação para acertar a parede, antes de se mover para a esquerda, deve verificar se a coordenada% WIDTH == 0 e a coordenada da parede direita% WIDTH == WIDTH-1. O% é o operador do módulo C (como a aritmética do relógio) e retorna o restante após a divisão. 31 div 16 deixa um restante de 15.
Gerenciando a Cobra
Existem três blocos (matrizes int) usados no jogo.
- cobra [], um buffer de anel
- shape [] - Mantém índices gráficos Snake
- dir [] - Mantém a direção de todos os segmentos da cobra, incluindo cabeça e cauda.
No início do jogo, a cobra tem dois segmentos de comprimento, com cabeça e cauda. Ambos podem apontar em 4 direções. Para norte, a cabeça é o índice 3, a cauda é 7, para a cabeça leste é 4, a cauda é 8, para a cabeça sul é 5 e a cauda é 9, e para o oeste, a cabeça é 6 e a cauda é 10. Enquanto a cobra tem dois segmentos, a cabeça e a cauda estão sempre a 180 graus de distância, mas depois que a cobra cresce, elas podem ficar 90 ou 270 graus.
O jogo começa com a cabeça voltada para o norte no local 120 e a cauda voltada para o sul no 136, aproximadamente central. Com um pequeno custo de cerca de 1.600 bytes de armazenamento, podemos obter uma melhoria discernível da velocidade no jogo mantendo os locais da cobra no buffer do anel da serpente [] mencionado acima.
O que é um buffer de anel?
Um buffer de anel é um bloco de memória usado para armazenar uma fila de tamanho fixo e deve ser grande o suficiente para armazenar todos os dados. Neste caso, é apenas para a cobra. Os dados são empurrados na frente da fila e retirados na parte de trás. Se a frente da fila atingir o final do bloco, ela será contornada. Enquanto o bloco for grande o suficiente, a frente da fila nunca alcançará as costas.
Toda localização da cobra (isto é, a única coordenada int) da cauda até a cabeça (isto é, para trás) é armazenada no buffer do anel. Isso oferece benefícios de velocidade porque, não importa quanto tempo a cobra fique, apenas a cabeça, a cauda e o primeiro segmento após a cabeça (se existir) precisam ser alterados à medida que se move.
Armazená-lo para trás também é benéfico, porque quando a cobra recebe comida, ela cresce quando é movida em seguida. Isso é feito movendo o local do cabeçote um no buffer do anel e alterando o local do cabeçote antigo para se tornar um segmento. A cobra é composta por uma cabeça, segmentos 0-n) e depois uma cauda.
Quando a cobra come comida, a variável atefood é definida como 1 e marcada na função DoSnakeMove ()
Movendo a cobra
Utilizamos duas variáveis de índice, headindex e tailindex, para apontar para os locais de cabeça e cauda no buffer do anel. Eles começam em 1 (índice de cabeçalho) e 0. Portanto, o local 1 no buffer do anel mantém o local (0-255) da cobra no tabuleiro. O local 0 mantém o local da cauda. Quando a cobra move um local para frente, o índice de cauda e o índice de cabeça são incrementados em um, passando para 0 quando atingem 256. Então agora o local que estava na cabeça é onde está a cauda.
Mesmo com uma cobra muito longa que é sinuosa e complicada, digamos 200 segmentos. somente o índice principal, o segmento próximo ao índice principal e final muda sempre que se move.
Observe por causa do caminho SDL funciona, temos que desenhar a cobra inteira a cada quadro. Cada elemento é desenhado no buffer do quadro e, em seguida, invertido para que seja exibido. Porém, isso tem uma vantagem: poderíamos desenhar a cobra movendo suavemente alguns pixels, não uma posição inteira na grade.