Este é um pequeno jogo da cobrinha, que fiz em 2009.
O jogo funcionará da seguinte maneira:
- O jogador começará com um certo tamanho da cobra e usará as setas do teclado para mudar a direção dela.
- O objetivo do jogo é comer maçãs e alcançar o maior tamanho que puder.
- O jogo não tem um fim, o jogador vai até onde conseguir ir.
Baixe as imagens abaixo, elas serão usadas no jogo:
Obs.: quando executar o jogo, as imagens devem estar na mesma pasta do executável.
Arquivo
snake.c:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <SDL/SDL.h>
/* direções da cobra */
#define CIMA 0
#define DIREITA 1
#define BAIXO 2
#define ESQUERDA 3
#define TAMANHOIMAGEM 32
struct Pedaco
{
int coorX;
int coorY;
int direcao; // direção
} pedaco[256], maca; // pedaco[0] = ponta do rabo E pedaco[tamanho - 1] = cabeça
/* Imagens e tela principal */
SDL_Surface *tela, *img_maca, *img_snakes, *img_cabeca;
int seta_cima = 0, seta_baixo = 0, seta_esquerda = 0, seta_direita = 0;
int colisao = 0; // identifica colisao da cabeça com outras partes do corpo
// tamanho = tamanho atual da cobra
int tamanho = 5, tamanho_anterior = 5;
int velX = 0, velY = 0; // para mover a cobra
int mapa_largura = 25, mapa_altura = 20; // dimensões do mapa
char hud[256]; // informações passadas ao usuario
/* Funcao que controla o fps */
void controla_fps ( int tempo_inicial )
{
int fps = 1000/7; // converte 7 FPS para milissegundos
int tempo_agora = SDL_GetTicks() - tempo_inicial;
if(tempo_agora < fps)
SDL_Delay(fps - tempo_agora);
}
int carrega_imagens ( )
{
img_maca = SDL_LoadBMP("apple.bmp");
if (img_maca == NULL)
{
printf("Não carregou apple.bmp\n");
return 0;
}
img_snakes = SDL_LoadBMP("piece.bmp");
if (img_snakes == NULL)
{
printf("Não carregou piece.bmp\n");
return 0;
}
img_cabeca = SDL_LoadBMP("head.bmp");
if (img_cabeca == NULL)
{
printf("Não carregou head.bmp\n");
return 0;
}
return 1;
}
void posiciona_maca ( )
{
int i;
int repetir;
do
{
// escolhe aleatoriamente as coordenadas
maca.coorX = rand() % mapa_largura;
maca.coorY = rand() % mapa_altura;
repetir = 0;
for (i = 0; i < tamanho; i++)
{
// verifica em todas as peças se as coordenadas delas
// são iguais as novas coordenadas da maçã.
if ((pedaco[i].coorX == maca.coorX) && (pedaco[i].coorY == maca.coorY))
{
// Se forem iguais então pare o loop e
// repita o procedimento para escolher outra coordenada para a maçã.
repetir = 1;
break;
}
}
// enquanto for para repetir continue escolhendo outra coordenada para a maçã.
} while (repetir);
}
void inicia_jogo ( )
{
tamanho_anterior = tamanho; // para o hud
//Reinicie o jogo
tamanho = 5;
colisao = 0;
velX = 0;
velY = 0;
// reinicializando as peças
// inicializando a parte A - a cabeça
pedaco[4].coorX = 5;
pedaco[4].coorY = 3;
pedaco[4].direcao = DIREITA;
// inicializando a parte B
pedaco[3].coorX = 4;
pedaco[3].coorY = 3;
pedaco[3].direcao = DIREITA;
// inicializando a parte C
pedaco[2].coorX = 3;
pedaco[2].coorY = 3;
pedaco[2].direcao = DIREITA;
// inicializando a parte D
pedaco[1].coorX = 2;
pedaco[1].coorY = 3;
pedaco[1].direcao = DIREITA;
// inicializando a parte E - o rabo
pedaco[0].coorX = 1;
pedaco[0].coorY = 3;
pedaco[0].direcao = DIREITA;
// inicializando as coordenadas da maçã.
posiciona_maca();
}
void controla_snake ( SDL_Event evento )
{
if (evento.type == SDL_KEYDOWN)
{
switch (evento.key.keysym.sym)
{
case SDLK_RIGHT:
seta_direita = 1;
break;
case SDLK_LEFT:
seta_esquerda = 1;
break;
case SDLK_UP:
seta_cima = 1;
break;
case SDLK_DOWN:
seta_baixo = 1;
break;
case SDLK_p:
velX = 0;
velY = 0;
break;
default:
break;
}
}
else if (evento.type == SDL_KEYUP)
{
switch (evento.key.keysym.sym)
{
case SDLK_RIGHT:
seta_direita = 0;
break;
case SDLK_LEFT:
seta_esquerda = 0;
break;
case SDLK_UP:
seta_cima = 0;
break;
case SDLK_DOWN:
seta_baixo = 0;
break;
default:
break;
}
}
}
void move_snake ( )
{
if (seta_direita && pedaco[tamanho - 1].direcao != ESQUERDA)
{
velX = 1; // move horizontalmente a cabeça para direita
velY = 0; // e para de mover verticalmente
pedaco[tamanho - 1].direcao = DIREITA;
}
else if (seta_esquerda && pedaco[tamanho - 1].direcao != DIREITA)
{
velX = -1; // move horizontalmente a cabeça para esquerda
velY = 0; // e para de mover verticalmente
pedaco[tamanho - 1].direcao = ESQUERDA;
}
else if (seta_cima && pedaco[tamanho - 1].direcao != BAIXO)
{
velX = 0; // para de mover horizontalmente
velY = -1; // e move verticalmente a cabeça
pedaco[tamanho - 1].direcao = CIMA;
}
else if (seta_baixo && pedaco[tamanho - 1].direcao != CIMA)
{
velX = 0; // para de mover horizontalmente
velY = 1; // e move verticalmente a cabeça
pedaco[tamanho - 1].direcao = BAIXO;
}
// depois ajustando as posições das outras peças (partes)
// primeiro move as partes do corpo
if (velX || velY) // se estiver movendo
{
int i;
for (i = 0; i < tamanho - 1; i++)
{
// faça a peça de trás (pedaco[i]) igual a peça da frente (pedaco[i + 1])
pedaco[i].coorX = pedaco[i + 1].coorX;
pedaco[i].coorY = pedaco[i + 1].coorY;
pedaco[i].direcao = pedaco[i + 1].direcao;
}
}
// agora move a cabeça
pedaco[tamanho - 1].coorX += velX;
pedaco[tamanho - 1].coorY += velY;
// Verifica os limites do movimento da cobra
// Para o eixo X
// se estiver além da largura da tela/mapa
if (pedaco[tamanho - 1].coorX >= mapa_largura)
{
// volte para posição coorX = 0
pedaco[tamanho - 1].coorX = 0;
}
else if (pedaco[tamanho - 1].coorX < 0) // se estiver além de 0
{
// volte para a posição da largura do mapa
pedaco[tamanho - 1].coorX = mapa_largura - 1;
}
// Para o eixo Y
if (pedaco[tamanho - 1].coorY >= mapa_altura)
{
pedaco[tamanho - 1].coorY = 0;
}
else if (pedaco[tamanho - 1].coorY < 0)
{
pedaco[tamanho - 1].coorY = mapa_altura - 1;
}
}
void desenha_snake ( )
{
int i;
SDL_Rect destino;
// blitando as img_macas das peças
for (i = 0; i < tamanho - 1; i++)
{
destino.y = pedaco[i].coorY * TAMANHOIMAGEM;
destino.x = pedaco[i].coorX * TAMANHOIMAGEM;
SDL_BlitSurface(img_snakes, NULL, tela, &destino);
}
// blitando a cabeça
destino.y = pedaco[tamanho - 1].coorY * TAMANHOIMAGEM;
destino.x = pedaco[tamanho - 1].coorX * TAMANHOIMAGEM;
SDL_BlitSurface(img_cabeca, NULL, tela, &destino);
}
int main (int argc, char **args)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("ERROR: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
SDL_Event evento;
// controle do FPS
Uint32 tempo_inicial;
tela = SDL_SetVideoMode(mapa_largura * TAMANHOIMAGEM, mapa_altura * TAMANHOIMAGEM, 16, SDL_SWSURFACE);
if (tela == NULL)
{
printf("ERROR: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
if (carrega_imagens() == 0)
{
printf("ERROR: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
// inicia o jogo
inicia_jogo();
srand(time(NULL));
int i;
int fim = 0; // variável de controle do loop principal
while (!fim)
{
tempo_inicial = SDL_GetTicks();
sprintf(hud, "SNAKE by Sam L. - TAMANHO: ATUAL = %d | ANTERIOR = %d", tamanho, tamanho_anterior);
SDL_WM_SetCaption(hud, NULL);
while (SDL_PollEvent(&evento))
{
if (evento.type == SDL_QUIT)
{
fim = 1;
break;
}
controla_snake(evento);
}
// move a cobra
move_snake();
// colisão com a maçã
if ((pedaco[tamanho - 1].coorX == maca.coorX) &&
(pedaco[tamanho - 1].coorY == maca.coorY))
{
tamanho++;
pedaco[tamanho - 1].coorX = maca.coorX;
pedaco[tamanho - 1].coorY = maca.coorY;
pedaco[tamanho - 1].direcao = pedaco[tamanho - 2].direcao;
// reinicializando a posição da maçã
// escolhe uma posição diferente das peças da serpente
posiciona_maca();
}
/* Colisão só será atualizada no próximo loop, pois o usuário deve ver as peças sobrepostas */
if (colisao)
{
// reinicia o jogo e seta a variavel colisao para 0
inicia_jogo();
}
/* Colisão entre cabeça e outras partes da cobra */
for (i = 0; i < tamanho - 2 && colisao == 0; i++)
{
if ((pedaco[tamanho - 1].coorX == pedaco[i].coorX) &&
(pedaco[tamanho - 1].coorY == pedaco[i].coorY))
colisao = 1;
}
// Blitagem
// Pintando o tela de branco
SDL_FillRect(tela, NULL, SDL_MapRGB(tela->format, 255, 255, 255));
SDL_Rect destino;
destino.x = maca.coorX * TAMANHOIMAGEM;
destino.y = maca.coorY * TAMANHOIMAGEM;
SDL_BlitSurface(img_maca, NULL, tela, &destino);
desenha_snake();
// atualizando a tela
SDL_UpdateRect(tela, 0,0,0,0);
controla_fps(tempo_inicial);
}
SDL_Quit(); // fecha o SDL
return 0;
}
Para compilar, rode:
gcc -o snake snake.c -lSDL
Controle de FPS
void controla_fps ( int tempo_inicial )
{
int fps = 1000/7; // converte 7 FPS para milissegundos
int tempo_agora = SDL_GetTicks() - tempo_inicial;
if(tempo_agora < fps)
SDL_Delay(fps - tempo_agora);
}
Para o controle do FPS, usei a mesma função do jogo do labirinto, mudou apenas o valor do FPS, que agora é 7 FPS (1000/7 é o valor em milissegundos).
Carregando as imagens
int carrega_imagens ( )
{
img_maca = SDL_LoadBMP("apple.bmp");
if (img_maca == NULL)
{
printf("Não carregou apple.bmp\n");
return 0;
}
img_snakes = SDL_LoadBMP("piece.bmp");
if (img_snakes == NULL)
{
printf("Não carregou piece.bmp\n");
return 0;
}
img_cabeca = SDL_LoadBMP("head.bmp");
if (img_cabeca == NULL)
{
printf("Não carregou head.bmp\n");
return 0;
}
return 1;
}
A função
carrega_imagens(), faz o mesmo da outra do jogo do labirinto, retorna 0 caso alguma imagem não tenha sido carregada e retorna 1, quando todas as imagens foram carregadas corretamente.
Posicionando a maçã
void posiciona_maca ( )
{
int i;
int repetir;
do
{
// escolhe aleatoriamente as coordenadas
maca.coorX = rand() % mapa_largura;
maca.coorY = rand() % mapa_altura;
repetir = 0;
for (i = 0; i < tamanho; i++)
{
// verifica em todas as peças se as coordenadas delas
// são iguais as novas coordenadas da maçã.
if ((pedaco[i].coorX == maca.coorX) && (pedaco[i].coorY == maca.coorY))
{
// Se forem iguais então pare o loop e
// repita o procedimento para escolher outra coordenada para a maçã.
repetir = 1;
break;
}
}
// enquanto for para repetir continue escolhendo outra coordenada para a maçã.
} while (repetir);
}
Essa função posiciona a maçã na tela. Ela escolhe uma posição diferente das posições das partes da cobra.
Iniciando o jogo
void inicia_jogo ( )
{
tamanho_anterior = tamanho; // para o hud
//Reinicie o jogo
tamanho = 5;
colisao = 0;
velX = 0;
velY = 0;
// inicializando os pedaços
// inicializando a parte A - a cabeça
pedaco[4].coorX = 5;
pedaco[4].coorY = 3;
pedaco[4].direcao = DIREITA;
// inicializando a parte B
pedaco[3].coorX = 4;
pedaco[3].coorY = 3;
pedaco[3].direcao = DIREITA;
// inicializando a parte C
pedaco[2].coorX = 3;
pedaco[2].coorY = 3;
pedaco[2].direcao = DIREITA;
// inicializando a parte D
pedaco[1].coorX = 2;
pedaco[1].coorY = 3;
pedaco[1].direcao = DIREITA;
// inicializando a parte E - o rabo
pedaco[0].coorX = 1;
pedaco[0].coorY = 3;
pedaco[0].direcao = DIREITA;
// inicializando as coordenadas da maçã.
posiciona_maca();
}
Função para iniciar o jogo. Ela define as posições iniciais dos pedaços da cobra, aqui são somente 5 pedaços, e reposiciona a maçã na tela com
posiciona_maca(). Sempre será chamada quando ocorrer colisão da cabeça com os outros pedaços da cobra.
Controlando a cobra
void controla_snake ( SDL_Event evento )
{
if (evento.type == SDL_KEYDOWN)
{
switch (evento.key.keysym.sym)
{
case SDLK_RIGHT:
seta_direita = 1;
break;
case SDLK_LEFT:
seta_esquerda = 1;
break;
case SDLK_UP:
seta_cima = 1;
break;
case SDLK_DOWN:
seta_baixo = 1;
break;
case SDLK_p:
velX = 0;
velY = 0;
break;
default:
break;
}
}
else if (evento.type == SDL_KEYUP)
{
switch (evento.key.keysym.sym)
{
case SDLK_RIGHT:
seta_direita = 0;
break;
case SDLK_LEFT:
seta_esquerda = 0;
break;
case SDLK_UP:
seta_cima = 0;
break;
case SDLK_DOWN:
seta_baixo = 0;
break;
default:
break;
}
}
}
Para controlar a cobra, usei a função
controla_snake(). Ela fica responsável por ajustar o valor da variáveis das setas. Quando uma seta do teclado é pressionada, então, identifica qual foi e define o valor da variável correspondente para 1. Se a tecla for solta, o valor da variável correspondente será 0.
Movendo a cobra
void move_snake ( )
{
if (seta_direita && pedaco[tamanho - 1].direcao != ESQUERDA)
{
velX = 1; // move horizontalmente a cabeça para direita
velY = 0; // e para de mover verticalmente
pedaco[tamanho - 1].direcao = DIREITA;
}
else if (seta_esquerda && pedaco[tamanho - 1].direcao != DIREITA)
{
velX = -1; // move horizontalmente a cabeça para esquerda
velY = 0; // e para de mover verticalmente
pedaco[tamanho - 1].direcao = ESQUERDA;
}
else if (seta_cima && pedaco[tamanho - 1].direcao != BAIXO)
{
velX = 0; // para de mover horizontalmente
velY = -1; // e move verticalmente a cabeça
pedaco[tamanho - 1].direcao = CIMA;
}
else if (seta_baixo && pedaco[tamanho - 1].direcao != CIMA)
{
velX = 0; // para de mover horizontalmente
velY = 1; // e move verticalmente a cabeça
pedaco[tamanho - 1].direcao = BAIXO;
}
// depois ajustando as posições das outras peças (partes)
// primeiro move as partes do corpo
if (velX || velY) // se estiver movendo
{
int i;
for (i = 0; i < tamanho - 1; i++)
{
// faça a peça de trás (pedaco[i]) igual a peça da frente (pedaco[i + 1])
pedaco[i].coorX = pedaco[i + 1].coorX;
pedaco[i].coorY = pedaco[i + 1].coorY;
pedaco[i].direcao = pedaco[i + 1].direcao;
}
}
// agora move a cabeça
pedaco[tamanho - 1].coorX += velX;
pedaco[tamanho - 1].coorY += velY;
// Verifica os limites do movimento da cobra
// Para o eixo X
// se estiver além da largura da tela/mapa
if (pedaco[tamanho - 1].coorX >= mapa_largura)
{
// volte para posição coorX = 0
pedaco[tamanho - 1].coorX = 0;
}
else if (pedaco[tamanho - 1].coorX < 0) // se estiver além de 0
{
// volte para a posição da largura do mapa
pedaco[tamanho - 1].coorX = mapa_largura - 1;
}
// Para o eixo Y
if (pedaco[tamanho - 1].coorY >= mapa_altura)
{
pedaco[tamanho - 1].coorY = 0;
}
else if (pedaco[tamanho - 1].coorY < 0)
{
pedaco[tamanho - 1].coorY = mapa_altura - 1;
}
}
Para mover, a cobra usei a função
move_snake(). Ela move cada parte da cobra para as novas posições. Se o valor de velX, ou velY, é diferente de zero, significa que deve-se atualizar as partes anteriores a cabeça.
A cabeça é a única peça que realmente move, as outras partes apenas movem para posições antigas da cabeça. É como um trem, onde a locomotiva (a cabeça) que puxa os vagões (as outras partes) sobre os trilhos.
if (seta_direita && pedaco[tamanho - 1].direcao != ESQUERDA)
{
velX = 1; // move horizontalmente a cabeça para direita
velY = 0; // e para de mover verticalmente
pedaco[tamanho - 1].direcao = DIREITA;
}
else if (seta_esquerda && pedaco[tamanho - 1].direcao != DIREITA)
{
velX = -1; // move horizontalmente a cabeça para esquerda
velY = 0; // e para de mover verticalmente
pedaco[tamanho - 1].direcao = ESQUERDA;
}
else if (seta_cima && pedaco[tamanho - 1].direcao != BAIXO)
{
velX = 0; // para de mover horizontalmente
velY = -1; // e move verticalmente a cabeça
pedaco[tamanho - 1].direcao = CIMA;
}
else if (seta_baixo && pedaco[tamanho - 1].direcao != CIMA)
{
velX = 0; // para de mover horizontalmente
velY = 1; // e move verticalmente a cabeça
pedaco[tamanho - 1].direcao = BAIXO;
}
As macros CIMA, DIREITA, BAIXO e ESQUERDA, são a direção que a cobra pode seguir.
A cobra é direcionada pelas setas do teclado. Cada seta está responsável pela mudança de uma direção. As setas direita e esquerda pela direção na horizontal (eixo X), e as setas para cima e para baixo, pela direção na vertical (eixo Y). Coloquei ainda uma tecla responsável por parar a cobra, esse é o "pause game".
A cobra não pode mover para uma direção inversa. Por exemplo, a direção inversa de DIREITA é a ESQUERDA, e a inversa de CIMA é a BAIXO. Se fosse permitido ir numa direção inversa, a cabeça colidiria com uma parte do corpo da cobra.
O movimento da cobra é feito alterando a velocidade X e Y e a direção da cabeça. As outras partes do corpo da cobra vão ter as velocidades alteradas a seguir, onde cada parte da cobra ficará com as propriedades da parte da frente. Ou seja, no movimento, a parte anterior fica com as coordenadas, velocidades e direção iguais à da parte da frente. Por isso a cabeça é a única parte a ser movida, já que as outras partes vão herdar as mesmas coordenadas, velocidade e direção que a cabeça.
Os valores das velocidade ao -1, 0 ou 1. No eixo X, velX igual a -1 é o mesmo que ir para esquerda, se velX igual a 1 é o mesmo que ir para direita e se velX igual a 0 está parada no eixo X. O mesmo vale para o eixo Y.
* Nota: as partes são contadas da esquerda para direita (do 0 ao tamanho - 1). O rabo (pedaco[0]) é parte mais para atrás e a cabeça (pedaco[tamanho - 1]), a parte mais a frente. Veja na image:
// se estiver além da largura da tela/mapa
if (pedaco[tamanho - 1].coorX >= mapa_largura)
{
// volte para posição coorX = 0
pedaco[tamanho - 1].coorX = 0;
}
else if (pedaco[tamanho - 1].coorX < 0) // se estiver além de 0
{
// volte para a posição da largura do mapa
pedaco[tamanho - 1].coorX = mapa_largura - 1;
}
Aqui verificamos se a cabeça (pedaco[tamanho - 1]) chegou nos limites da tela. O máximo que a cabeça pode ir para direita na tela, é até mapa_largura, chegou a isso ou passou, a cabeça volta para a posição 0 no eixo X. No contrário, se a cabeça passa a esquerda da posição 0 no eixo X, ela vai para a posição mapa_largura - 1 no eixo X. A mesma lógica se aplica ao movimento no eixo Y.
Observe que estou dividindo a tela numa espécie de matriz, como no jogo do labirinto, só que sem usar uma matriz de verdade. Veja abaixo como a tela está conceitualmente dividida:
A cobra move uma célula de cada vez, por isso velX ou velY é 1 ou -1. Cada célula do mapa conceitual mede TAMANHOIMAGEM (definido como 32 pixels). A macro TAMANHOIMAGEM serve para posicionar as imagem na tela no momento da blitagem.
O destino de cada imagem é definido pela posição da coordenada de cada peça, ou seja, a coordenada (X ou Y) vezes TAMANHOIMAGEM é o destino (X ou Y) em pixels.
Blitagem da cobra
void desenha_snake ( )
{
int i;
SDL_Rect destino;
// blitando as img_macas das peças
for (i = 0; i < tamanho - 1; i++)
{
destino.y = pedaco[i].coorY * TAMANHOIMAGEM;
destino.x = pedaco[i].coorX * TAMANHOIMAGEM;
destino.w = TAMANHOIMAGEM;
destino.h = TAMANHOIMAGEM;
SDL_BlitSurface(img_snakes, NULL, tela, &destino);
}
// blitando a cabeça
destino.y = pedaco[tamanho - 1].coorY * TAMANHOIMAGEM;
destino.x = pedaco[tamanho - 1].coorX * TAMANHOIMAGEM;
destino.w = TAMANHOIMAGEM;
destino.h = TAMANHOIMAGEM;
SDL_BlitSurface(img_cabeca, NULL, tela, &destino);
}
A função de saída, blita toda a cobra na tela. É sempre uma das últimas funções a ser chamada no loop principal. Não tenho muito o que falar sobre ela, tem um funcionamento muito simples, dá pra entender de boa.
// O loop principal
while (fim == 0)
{
tempo_inicial = SDL_GetTicks();
sprintf(hud, "SNAKE by Sam L. - TAMANHO: ATUAL = %d | ANTERIOR = %d", tamanho, tamanho_anterior);
SDL_WM_SetCaption(hud, NULL);
while (SDL_PollEvent(&evento))
{
if (evento.type == SDL_QUIT)
{
fim = 1;
break;
}
controla_snake(evento);
}
// move a cobra
move_snake();
// colisão com a maçã
if ((pedaco[tamanho - 1].coorX == maca.coorX) &&
(pedaco[tamanho - 1].coorY == maca.coorY))
{
tamanho++;
pedaco[tamanho - 1].coorX = maca.coorX;
pedaco[tamanho - 1].coorY = maca.coorY;
pedaco[tamanho - 1].direcao = pedaco[tamanho - 2].direcao;
// reinicalizando a posição da maçã
// escolhe uma posição diferente das peças da serpente
posiciona_maca();
}
/* Colisão só será atualizada no próximo loop, pois o usuário deve ver as peças sobrepostas */
if (colisao)
{
// reinicia o jogo e seta a variável colisao para 0
inicia_jogo();
}
/* Colisão entre cabeça e outras partes da cobra */
for (i = 0; i < tamanho - 2 && colisao == 0; i++)
{
if ((pedaco[tamanho - 1].coorX == pedaco[i].coorX) &&
(pedaco[tamanho - 1].coorY == pedaco[i].coorY))
colisao = 1;
}
// Blitagem
// Pintando o tela de branco
SDL_FillRect(tela, NULL, SDL_MapRGB(tela->format, 255, 255, 255));
SDL_Rect destino;
destino.x = maca.coorX * TAMANHOIMAGEM;
destino.y = maca.coorY * TAMANHOIMAGEM;
SDL_BlitSurface(img_maca, NULL, tela, &destino);
desenha_snake();
// atualizando a tela
SDL_UpdateRect(tela, 0,0,0,0);
controla_fps(tempo_inicial);
}
Vamos debulhar aos poucos cada parte do loop principal.
if ((pedaco[tamanho - 1].coorX == maca.coorX) &&
(pedaco[tamanho - 1].coorY == maca.coorY))
{
tamanho++;
pedaco[tamanho - 1].coorX = maca.coorX;
pedaco[tamanho - 1].coorY = maca.coorY;
pedaco[tamanho - 1].direcao = pedaco[tamanho - 2].direcao;
// reinicalizando a posição da maçã
// escolhe uma posição diferente das peças da serpente
posiciona_maca();
}
A colisão com a maçã é muito simples, apenas verifique se as coordenadas da cabeça são iguais às coordenadas da maçã. Se for, então o tamanho da cobra deve aumentar mais um. Isto significa que a cabeça (pedaco[tamanho - 1]) será as mesmas coordenadas da maçã e sua direção será a mesma do pedaço anterior (pedaco[tamanho - 2]). Por fim, a maçã deve ser reposicionada na tela com
posiciona_maca().
if (colisao)
{
// reinicia o jogo e seta a variável colisao para 0
inicia_jogo();
}
/* Colisão entre cabeça e outras partes da cobra */
for (i = 0; i < tamanho - 2 && colisao == 0; i++)
{
if ((pedaco[tamanho - 1].coorX == pedaco[i].coorX) &&
(pedaco[tamanho - 1].coorY == pedaco[i].coorY))
colisao = 1;
}
A colisão da cabeça com outros pedaços do corpo é feita comparando-se as coordenadas da cabeça e as coordenadas dos pedaços. O único pedaço que não é comparado com a cabeça, é o que está atrás da cabeça, o pedaco[tamanho - 2], ele não colidiria em hipótese nenhuma com a cabeça, por isso a variável
i vai só até tamanho
- 2 no vetor pedaco.
Quando é detectada uma colisão, a variável
colisao é setada para 1 e a colisão só é tratada mesmo na próxima iteração do loop principal, veja que o
if (colisão) está antes de setar a variável colisão para 1. Eu fiz isso para dar tempo do jogador poder ver a cabeça sobrepondo um dos pedaços do corpo da cobra.
// Blitagem
// Pintando a tela de branco
SDL_FillRect(tela, NULL, SDL_MapRGB(tela->format, 255, 255, 255));
SDL_Rect destino;
destino.x = maca.coorX * TAMANHOIMAGEM;
destino.y = maca.coorY * TAMANHOIMAGEM;
SDL_BlitSurface(img_maca, NULL, tela, &destino);
desenha_snake();
// atualizando a tela
SDL_UpdateRect(tela, 0,0,0,0);
controla_fps(tempo_inicial);
Chegamos a parte final do loop principal. A blitagem das imagens é feita aqui. A maçã é blitada na tela, usando um
SDL_Rect de destino, que irá por na tela a imagem da maçã.
Em seguida, é blitada a cobra na tela com
desenha_snake(), depois apenas atualizamos a tela com as imagens blitadas. E por fim, executamos
controla_fps(), que fará ou não uma chamada a
SDL_Delay(), a variável
tempo_inicial, que foi setada com
SDL_GetTicks() no início do loop, é passada para a função
controla_fps().
O que poderia ser colocado no jogo
Poderia ter passagens onde a cabeça passaria e apareceria em outra parte da tela, seria como um tele transporte.
Ou, poderia ter obstáculos fixos, que se a cobra passasse morreria, isso dificultaria mais o jogo.
Isso foi só o que consegui pensar, tente criar mais desafios ou obstáculos para o jogo.