sexta-feira, 21 de dezembro de 2012

Movimento e Colisão

Nesta postagem vamos comentar sobre diversas procedures que são responsáveis por movimentar os jogadores e as bolas e testar a colisão do jogador com diversos elementos do jogo. Estas procedures são chamadas a partir da procedure jogo.

A procedure movecara movimenta os jogadores baseado no valor da variável "direcao" que recebe seu valor atual na procedure controle de acordo com as teclas pressionadas pelos jogadores.

O trecho de código abaixo mostra a lógica do movimento para a direção 1 (para cima). Para as outras direções o código é bem parecido, por isso foram omitidos.
procedure movecara;
var ind:integer;
begin
  for ind:= 1 to qjog do
    begin
      apaga(jog[ind].lin,jog[ind].col);
      case jog[ind].direcao of
        1:
           if mapa[jog[ind].lin-1,jog[ind].col]=1 then
                       morte(ind)
           else
                 jog[ind].lin:=jog[ind].lin-1;

        2: {...}
        3: {...}
        4: {...}
end;

A imagem do jogador em sua posição atual é apagada. Depois, baseado na direção que o jogador está se movendo é verificado se existe um bloco no caminho. Se o caminho estiver livre, a posição atual do jogador será modificada, mas se houver um bloco o jogador irá colidir, perderá pontos e será exibido um "*" na posição do jogador representando a colisão conforme a imagem abaixo. Essas ações estão na procedure morte().



O movimento das bolas é feito pela procedure movebola. As bolas se movimentam em diagonal e mudam de direção quando colidem com os blocos. O trecho de código abaixo mostra a lógica do movimento das bolas para a direção 1.
procedure movebola;
var ok,ind:integer;
begin
  for ind:= 1 to qbola do
   begin
     apaga(bola[ind].lin,bola[ind].col);
     ok:=0;
     repeat
        case bola[ind].direcao of
           1:begin
              if mapa[bola[ind].lin-1,bola[ind].col+1]=1 then
               begin
                 if((mapa[bola[ind].lin-1,bola[ind].col]=1) and
                    (mapa[bola[ind].lin,bola[ind].col+1]=1)) or
                    ((mapa[bola[ind].lin-1,bola[ind].col]=0) and
                    (mapa[bola[ind].lin,bola[ind].col+1]=0)) then
                      bola[ind].direcao:=3    {direcao oposta}
                 else
                  if(mapa[bola[ind].lin-1,bola[ind].col]=1) and
                    (mapa[bola[ind].lin,bola[ind].col+1]=0) then
                      bola[ind].direcao:=2    {muda movimento vertical}
                  else
                   if(mapa[bola[ind].lin-1,bola[ind].col]=0) and
                     (mapa[bola[ind].lin,bola[ind].col+1]=1) then
                       bola[ind].direcao:=4    {muda movimento horizontal}
               end
              else
                begin
                 bola[ind].lin:=bola[ind].lin-1;
                 bola[ind].col:=bola[ind].col+1;
                 ok:=1;
                end;
            end;

           2: {...}
           3: {...}
           4: {...}
end;

Os valores de 1 a 4 representam as seguintes direções para as bolas:


Se houver um bloco no local para onde a bola deveria ir será preciso decidir de que forma a bola irá mudar sua direção. Caso a direção da bola seja 1, o primeiro teste feito é para verificar se as posição acima e a direita possuem blocos ou se as duas posições estão sem blocos. Em ambos os caso a bola moverá na direção oposta.

O segundo teste verifica se a posição acima tem bloco e a direita não. Neste caso a bola muda apenas seu movimento vertical.

O terceiro teste verifica se a posição acima não tem bloco e a direita tem. Neste caso a bola muda apenas seu movimento horizontal.


A procedure testaobj verifica se os jogadores conseguiram pegar os seus itens. O código completo da procedure é este:
procedure testaobj;
var ind:integer;
begin
  for ind:=1 to qjog do
   begin
     if (jog[ind].col=obj[ind].col) and (jog[ind].lin=obj[ind].lin) then
        begin

          extrainfo[ind].score := extrainfo[ind].score + pontoobj;

          if qjog=2 then
            repeat
             inicia(obj[ind].lin,obj[ind].col);
            until (obj[1].lin<>obj[2].lin) or (obj[1].col<>obj[2].col)
          else
             inicia(obj[ind].lin,obj[ind].col);
        end;
   end;
end;

O vetor "obj" guarda as informações relacionadas aos itens que os jogadores devem pegar. Caso a posição do jogador e de seu item seja igual, o jogador ganhará a quantidade de pontos que está armazenada na variável "pontoobj". O valor desta variável é definido na procedure pegadados e depende da velocidade do jogo e da quantidade de bolas.

Depois o item deve reaparecer em outra posição. Isto é feito através da procedure inicia(). Caso 2 jogadores estejam jogando é feito um laço de repetição para garantir que o item não fique no mesmo lugar do item do outro jogador.

A procedure testamina verifica se os jogadores estão na mesma posição de uma das 5 minas que estão escondidas na área do jogo. Se estiver, será chamada a procedure morte() para o jogador e a mina será movida para outra posição. O código da procedure testamina é muito parecido com o da procedure testaobj.
        
A procedure testabola apenas verifica a colisão do jogador com as bolas do jogo e chama a procedure morte() caso ocorra. O seu código é bem simples e está abaixo.
procedure testabola;
var ind,bo:integer;
begin
  for ind:=1 to qjog do
    for bo:= 1 to qbola do
       if (jog[ind].col=bola[bo].col) and (jog[ind].lin=bola[bo].lin) then
              morte(ind);
end;