terça-feira, 27 de novembro de 2012

Procedure pegadados


A procedure pegadados é executada antes de iniciar uma partida do Mina 2. Através desta procedure é possível especificar os nomes dos jogadores, o número da fase, a velocidade do jogo e a quantidade de bolas. Estas informações são guardadas em variáveis que serão acessadas em outras procedures.

O código completo da procedure está abaixo.
procedure pegadados;
var resp1:char;
    code,ind:integer;
    resplong:string;

begin
    apagatela;
    for ind:= 1 to qjog do
      begin
        gotoxy(5,3+(2*ind));
        write('Digite o NOME do jogador ');
        if qjog=2 then
         begin
           if ind = 1 then write('da DIREITA ');
           if ind = 2 then write('da ESQUERDA ');
         end;
        write('(max. 10 letras) : ');
        readln(resplong);
        extrainfo[ind].nome:=copy(resplong,1,10);
      end;

    repeat
      gotoxy(5,9);
      write('Digite o N§ da FASE (1 a 5) : ');
      readln(fase);
    until (fase>=1) and (fase<=5);

    repeat
      gotoxy(5,11);
      write('Digite o N§ da VELOCIDADE (1 a 5) : ');
      readln(vel);
    until (vel>=1) and (vel<=5);

    repeat
      gotoxy(5,13);
      write('Digite o N§ de BOLAS (0 a 8) : ');
      readln(qbola);
    until (qbola>=0) and (qbola<=8);

    espera:=35-(5*vel);
    tempo:=49+(10*vel);
    pontoobj:=vel+qbola;
end;

A variável "qjog" contém o número de jogadores. Ela é preenchida na procedure menu de acordo com a opção selecionada.

Os nomes dos jogadores são armazenados na variável extrainfo cuja definição é feita da seguinte forma:
extrainfo: array[1..2] of tiporecorde;

O "tiporecorde" é um novo tipo definido no início do programa para armazenar o nome e a pontuação de um jogador.

As demais informações são guardadas nas seguintes variáveis:
  • fase: Número da fase.
  • vel: Velocidade do jogo
  • qbola: Número de bolas na fase. 

A partir dessas variáveis outras são calculadas:
  • espera: Tempo de pausa em milisegundos de cada quadro do jogo. Quanto maior a velocidade menor será o valor de "espera".
  • tempo: Tempo de duração de uma partida do Mina 2. Quanto maior a velocidade maior será o valor de "tempo".
  • pontoobj: Pontos que o jogador irá ganhar ao pegar um objeto do jogo. Seu valor é igual a soma das variáveis "vel" e "qbola".
     

segunda-feira, 26 de novembro de 2012

Procedure menu


A procedure menu exibe o título do jogo e mostra diversas opções que podem ser selecionadas pelo jogador utilizando as teclas de setas e pressionando ENTER.

As opções que o jogador pode escolher estão armazenadas em um array de string, que é preenchida logo no início da procedure menu.
procedure menu;
 var escolha,sair:integer;
     opcoes: array[1..5] of string[20];

 begin
      sair:=0;
      opcoes[1]:='   1 JOGADOR    ';
      opcoes[2]:='   2 JOGADORES  ';
      opcoes[3]:='   RECORDES     ';
      opcoes[4]:='   INSTRUCOES   ';
      opcoes[5]:='   FINALIZAR    ';
      ...

O trecho de código abaixo é responsável pela lógica da seleção e da exibição em cor diferente da escolha atual. A opção que está selecionada é exibida com a cor branca (cor=15) e o fundo vermelho escuro (cor=4). As demais opções são exibidas com a cor verde claro (cor=10) e fundo preto (cor=0). A variável inteira "escolha" guarda o valor da opção selecionada.
      repeat
            textbackground(4);
            textcolor(15);
            gotoxy(28,10 + (2 * escolha));
            write(opcoes[escolha]);
            enfeite;
            tecla:=ord(readkey);
            if tecla=0 then tecla:=ord(readkey)+255;
            textbackground(0);
            textcolor(10);
            gotoxy(28,10 + (2 * escolha));
            write(opcoes[escolha]);
            case tecla of
                 CI1: begin
                           escolha:=escolha-1;
                           if escolha=0 then escolha:=5;
                      end;
                 BA1: begin
                           escolha:=escolha+1;
                           if escolha=6 then escolha:=1;
                      end;
                 ENT: case escolha of
                           1: begin
                               qjog:=1;
                               sair:=1;
                              end;
                           2: begin
                               qjog:=2;
                               sair:=1;
                              end;
                           3: procrecorde;
                           4: instrucoes ;
                           5: begin 
                               gravarecordes;
                               textcolor(15);
                               textbackground(0);
                               clrscr;
                               halt;              {finaliza o programa}
                              end;
                      end;
            end;
      until tecla=ENT;

No início do bloco "repeat / until" a opção atual é desenhada em destaque e depois a procedure enfeite é chamada. Quando uma tecla for pressionada, a opção atual é redesenhada com a cor padrão igual as demais opções. As únicas teclas que fazem alguma ação no menu são: seta para cima, seta para baixo e tecla ENTER. Os valores dessas teclas estão armazenados nas constantes CI1, BA1 e ENT.

As setas apenas alteram o valor da variável "escolha". Caso a tecla ENTER seja pressionada, será verificada o valor atual da variável "escolha" para decidir o fluxo do programa.

sexta-feira, 23 de novembro de 2012

Procedure enfeite


A procedure enfeite é usada para fazer a animação da borda da tela de uma forma semelhante aqueles antigos placares eletrônicos. Esta procedure é usada em várias telas do Mina 2. O código fonte da procedure está abaixo.
procedure enfeite;
 var asteriscos:string;
     a,b,c:integer;

 begin
      asteriscos:='*    *    *    *    *    *    *    *    *    *    *    *    *    *    *    *    *    ';
      textcolor(11);
      textbackground(0);
      repeat
            for a:=1 to 5 do
             begin
                  gotoxy(1,1);
                  write(copy(asteriscos,a,80));
                  gotoxy(1,22);
                  write(copy(asteriscos,6-a,80));
                  escondecursor;
                  textcolor(11);
                  textbackground(0);
                  delay(60);
                  for b:=2 to 21 do
                   begin
                     c:=(a+b) mod 5;
                     if c = 1 then
                      begin
                        gotoxy(80,b);
                        write('*');
                        gotoxy(1,23-b);
                        write('*');
                      end
                     else
                      begin
                        gotoxy(80,b);
                        write(' ');
                        gotoxy(1,23-b);
                        write(' ');
                      end;
                   end;
             end;
      until keypressed;
 end;

Antes de explicar a lógica utilizada nesta procedure, vou comentar sobre algumas procedures que aparecem neste código:
  • copy(string,inicio,tamanho): A função copy() retorna uma parte de uma string especificada pelos parâmetros inicio e tamanho. 
  • delay(tempo): Esta procedure é usada para fazer uma pausa na execução do programa que dure o tempo especificado em milisegundos.
  • escondecursor: Esta é uma procedure utilitária que fiz para esconder o cursor durante a execução do Mina 2. Ela define a cor de frente e de fundo como preto em uma posição da tela e coloca o cursor lá. No tempo do DOS essa solução funcionava corretamente, mas quando o Mina 2 é executado em uma janela do Windows é possível ver o cursor aparecendo em várias partes da tela.

A animação das bordas é feita de duas formas, sendo uma para as bordas horizontais e a outra para as bordas verticais.

Para as bordas horizontais, criei uma longa string que contém todos os asteriscos que devem ser exibidos na parte superior e inferior da borda. Entre os asteriscos existem 4 espaços em branco.

Utilizando a função copy() eu desloco a string com asteriscos para a esquerda na borda superior e para a direita na borda inferior. O trecho de código relacionado está abaixo:
for a:=1 to 5 do
 begin
   gotoxy(1,1);     {borda superior}
   write(copy(asteriscos,a,80));

   gotoxy(1,22);    {borda inferior}
   write(copy(asteriscos,6-a,80));
   ...

Para as bordas verticais, um asterisco é desenhado a cada 5 posições levando em conta a posição atual das bordas horizontais. Nas outras posições é colocado um espaço em branco para apagar os asteriscos antigos.

Quando uma tecla for pressionada a procedure enfeite é encerrada e o fluxo do programa volta para a procedure anterior que chamou a procedure enfeite.

quinta-feira, 22 de novembro de 2012

Procedure instrucoes


A tela de Instruções do Mina 2 é a mais simples de todas. Ela apenas exibe um texto para o usuário e aguarda que seja pressionada a tecla ENTER.

Evitei usar acentuação no texto de Instruções porque haviam muitos PCs da época que não estavam configurados corretamente e acabavam mostrando caracteres estranhos.

O código completo da procedure instrucoes é o seguinte:
procedure instrucoes;
 var tecla_res:integer;
 begin
   apagatela;
   textcolor(14);
   textbackground(0);
   gotoxy(1,2);
   writeln('                            INSTRUCOES  ');
   writeln('    MINA 2 eh um jogo para um ou dois jogadores. O seu objetivo eh pegar ');
   writeln('  o maximo de objetos que aparecem na tela em um tempo limitado. Voce pode');
   writeln('  alterar a velocidade, o n§ de bolas e a fase onde quer jogar. No campo');
   writeln('  do jogo existem 5 minas escondidas. Se encontrar uma mina, bater na');
   writeln('  parede ou ser atingido por uma bola, voce morre e perde 5 pontos. ');
   writeln('    A cada 5 unidades de tempo as minas mudam de posicao.');
   writeln('  Cada vez que pegar um objeto ganhara pontos de acordo com a seguinte');
   writeln('  formula : pontos = (n§ da velocidade) + (n§ de bolas).');
   writeln('    Por isso quanto maior for a velocidade e o n§ de bolas , mais pontos');
   writeln('  voce podera ganhar. Se estiver no modo de 2 jogadores, o jogador da');
   writeln('  direita pega o objeto ',chr(4),' e o da esquerda pega o ',chr(6),' .');
   writeln('  Controles do jogo:  P - pausa/despausa    ESC - encerra uma partida');
   writeln('                      1§ jogador: usa as setas p/ se movimentar.');
   writeln('                      2§ jogador:      W');
   writeln('                                     A S D');
   writeln('  OBS: Voce nao precisa segurar a tecla para mover o personagem. Basta');
   writeln('  da um toque na direcao desejada que ele ira.');
   writeln('  Ass: Marcos Romero G. de Almeida. E-mail: maromero@zaz.com.br');
   writeln('                    PRESSIONE ENTER P/ CONTINUAR');
   repeat
    enfeite;
    tecla_res:=ord(readkey);
    if tecla_res=0 then tecla_res:=ord(readkey)+255;
   until tecla_res=ENT;
 end;

A procedure auxiliar apagatela foi feita para limpar a tela devagar e é utilizada em várias partes do Mina 2 para fazer a transição entre telas.

O texto é escrito a partir da 2ª linha da tela usando a cor amarela (valor 14) e o fundo preto (valor 0).

A procedure writeln(texto) escreve um texto e depois coloca o cursor no início da próxima linha.

No código fonte, onde aparece o símbolo § deveria aparecer º. Na execução do programa o símbolo º aparece corretamente.

Para escrever os símbolos ♦ (ASCII = 4) e ♠ (ASCII = 6), utilizei a função chr() na seguinte linha:
writeln('  direita pega o objeto ',chr(4),' e o da esquerda pega o ',chr(6),' .');

Para fazer a leitura das teclas pressionadas no Mina 2, utilizo as seguintes funções do Pascal:
  • keypressed: Esta função retorna True caso alguma tecla tenha sido pressionada.
  • readkey: Esta função retorna o caractere associado a tecla que foi pressionada.  
  • ord(char): Esta função retorna o código ASCII de um caractere.

Uma observação sobre a função readkey. Algumas teclas especiais como as de funções (F1, F2...) e as Setas do teclado possuem dois Bytes. Por isso a primeira leitura do readkey retornará 0 (zero), sendo necessário chamar o readkey de novo para pegar o valor desta tecla especial. Para diferenciar as teclas especiais das teclas normais, eu adiciono o valor 255 quando identifico que é uma tecla especial.

Esta lógica está no código abaixo:
tecla_res := ord(readkey);
if tecla_res = 0 then tecla_res := ord(readkey) + 255;

A procedure enfeite, que se encontra dentro do bloco repeat / until, é usada para fazer a animação da borda da tela até que uma tecla seja pressionada. Dentro dela está a chamada para a função keypressed. A procedure enfeite será analisada no próximo artigo.

Se a tecla pressionada for o ENTER, cujo código ASCII está armazenado na constante ENT, então a procedure instrucoes encerrará e o programa voltará para o menu.

quarta-feira, 21 de novembro de 2012

Modo Texto em Pascal

O Mina 2 é executado em modo texto no qual a tela é dividida em 25 linhas e 80 colunas. Cada posição da tela pode ser ocupada por um caractere ou símbolo.

As procedures abaixo são muito utilizadas no Mina 2:
  • textcolor(cor): Define a cor do texto que será utilizada.
  • textbackground(cor): Define a cor de fundo do texto.
  • gotoxy(coluna,linha): Coloca o cursor na posição especificada.
  • write(texto): Escreve um texto na posição atual do cursor usando as cores que estão definidas.  

Existem 16 cores que podem ser utilizadas nas procedures textcolor(cor) e textbackground(cor). Basta passar o número da cor como parâmetro. A imagem abaixo mostra as cores disponíveis. Para saber o número da cor, conte da esquerda para a direita começando em 0. Exemplos: preto = 0, vermelho escuro = 4, verde claro = 10 e branco = 15.


Como exemplo, o código abaixo escreve o texto "1 JOGADOR" na posição (coluna = 30, linha = 15) usando a cor branca com a cor de fundo vermelho escuro.


textcolor(15);
textbackground(4);
gotoxy(30,15);
write('   1 JOGADOR    ');

Para usar o Modo Texto de uma forma efetiva é preciso conhecer o código ASCII que é uma representação numérica dos caracteres usados no computador. O padrão ASCII define o código de 128 caracteres baseado no alfabeto inglês. Existe a versão estendida do ASCII que incluem mais 128 caracteres. Esta versão estendida era usada principalmente em países que utilizam acentuação como aqui no Brasil. Consulte a página www.asciitable.com para ver os símbolos existentes no padrão ASCII normal e estendido.

O problema é que na codificação atualmente usada pelos computadores foi mantida a compatibilidade apenas com o código ASCII original, dessa forma os caracteres que utilizavam a versão estendida do ASCII aparecem trocados. Por exemplo, no código fonte do Mina 2 eu utilizo muito o símbolo █ que representa um bloco do jogo. Este símbolo possui o código ASCII 219. No padrão atual, o código 219 representa o caracter Û.

Por isso que no código da Procedure menu do Mina 2 aparecem diversos caracteres estranhos, como pode ser visto abaixo.
write('Ü   Ü   Ü   Ü   Ü   ÜÜÜÜÜ     ÜÜÜÜÜ'); gotoxy(20,4);
write('ÛÛ ÛÛ   Û   ÛÛ  Û   Û   Û         Û'); gotoxy(20,5);
write('Û Û Û   Û   Û Û Û   ÛßßßÛ     Ûßßßß'); gotoxy(20,6);
write('Û   Û   Û   Û  ßÛ   Û   Û     ÛÜÜÜÜ');

O que deveria aparecer era algo assim:





Mas na hora da execução do programa, os caracteres são interpretados corretamente de acordo com a tabela ASCII.

Outra forma possível de utilizar os caracteres ASCII estendidos é através da função chr() que recebe um código ASCII e retorna o caractere equivalente. Como exemplo a linha abaixo escreverá o símbolo █ na tela.
write(chr(219));

terça-feira, 20 de novembro de 2012

Laço Principal

O código que contém o laço principal do Mina 2 está no fim do arquivo MINA2.PAS. O seu conteúdo é o seguinte:
begin
  clrscr;           {1ª parte}
  randomize;
  assign(frec,'recordes.mi2');
  verificarecordes;

  repeat            {2ª parte}
   sair:=0;
   cont:=0;
   menu;
   pegadados;
   tela;

   while sair = 0 do    {3ª parte}
    begin
      jogo;
      controle;
    end;

  until 1=0;
end.

Para facilitar a explicação, dividi este código em 3 partes.

A 1ª parte contém algumas procedures de inicialização. Segue uma breve descrição de cada uma delas:
  • clrscr: Limpa a tela. 
  • randomize: Inicializa o gerador de números aleatórios.  
  • assign(frec,'recordes.mi2'): Associa uma variável a um arquivo externo chamado recordes.mi2. 
  • verificarecordes: Verifica a existência do arquivo com Recordes e carrega no programa. 

A 2ª parte contém um laço infinito (repeat / until 1=0). Este laço contém todo o fluxo do jogo, que se inicia no Menu. A partir do Menu o jogador pode ir para as telas de Instruções e de Recordes, ou pode iniciar o jogo. O encerramento do programa também é feito a partir do Menu principal.

A procedure "pegadados" é usada para permitir que o jogador especifique alguns dados para o jogo como seu nome, número da fase e velocidade. A procedure "tela" desenha na tela a fase que foi escolhida pelo jogador.

A 3ª parte contém um laço while relacionado a execução do jogo. Quando o tempo do jogo acaba, a variável "sair" recebe o valor 1 fazendo com que o programa volte para o Menu principal.

A imagem abaixo mostra a ordem de execução das diversas procedures do Mina 2.


segunda-feira, 19 de novembro de 2012

Definição das variáveis

O código fonte em Pascal do Mina 2 está no arquivo MINA2.PAS. Logo no começo do arquivo temos as definições das variáveis e constantes utilizadas no jogo Mina 2 que são as seguintes:
program MINA2;
uses crt,dos;

const CI1 = 327;     {setas}
      BA1 = 335;
      ES1 = 330;
      DI1 = 332;
      CI2 = 87;      {w, a, s, d}
      BA2 = 83;
      ES2 = 65;
      DI2 = 68;
      PA  = 80;
      ESC = 27;
      ENT = 13;
      QM  = 5;  {qt de minas}
      QF  = 5;  {qt de fases}
      altura  = 17;
      largura = 50;
      diflin = 6;
      difcol = 15;

type
     geral = record
              col,lin,cara,cor,direcao:integer;
             end;
     tiporecorde = record
                      nome:string[10];
                      score:integer;
                   end;

var
     mina: array[1..QM] of geral;
     bola: array[1..8] of geral;
     jog,obj: array[1..2] of geral;
     extrainfo: array[1..2] of tiporecorde;
     recordes: array[1..QF] of tiporecorde;
     mapa: array[1..altura,1..largura] of integer;
     frec: file of tiporecorde;
     i,j,qjog,qbola,vel,fase,espera,tecla,cont: integer;
     sair,pontoobj,tempo: integer;

Vamos começar analisando a variável "mapa" que é responsável por representar a área do jogo.

A definição da variável "mapa" é feita da seguinte forma:
mapa: array[1..altura,1..largura] of integer;

Isto significa que "mapa" é uma matriz cujas dimensões são definidas pelas constantes "altura" e "largura". A constante "altura" está definida com o valor 17 e a constante "largura" com o valor 50. Cada posição desta matriz pode guardar um valor inteiro. No Mina 2, cada posição da variável "mapa" só guarda 0 ou 1, sendo que o valor 1 significa que existe um bloco nesta posição.

A imagem abaixo mostra a área de jogo do Mina 2. Coloquei uma grade por cima da imagem para que fique fácil de visualizar como a matriz representa a imagem. Cada posição da grade equivale a uma posição da matriz.


Agora vamos analisar as variáveis que representam a imagem dos jogadores e dos objetos que precisam ser coletados. A definição é feita desta forma:
jog,obj: array[1..2] of geral;

As variáveis "jog" e "obj" são do mesmo tipo, que consiste em um vetor de duas posições do tipo "geral". Cada posição do vetor representa um jogador.

Eu declarei o tipo "geral" usando o registro do Pascal para agrupar diversas variáveis comuns que são usados por vários itens no jogo Mina 2. A declaração foi feita desta forma:
type geral = record
              col,lin,cara,cor,direcao:integer;
             end;
         
Como exemplo de uso destas variáveis, o código abaixo define que o objeto que deve ser pego pelo 2º jogador irá aparecer na posição (linha: 10, coluna: 10) da área do jogo:
obj[2].lin := 10;
obj[2].col := 10;

As demais variáveis e constantes serão analisadas em outros artigos de acordo com o uso delas.

sexta-feira, 16 de novembro de 2012

Exemplo de um Jogo em Pascal


O objetivo deste blog é explicar o funcionamento do código fonte do jogo Mina 2 que eu programei com o Turbo Pascal há muito tempo atrás. Apesar de Pascal ser uma linguagem de programação antiga, muitas escolas e faculdades ainda continuam utilizando Pascal para ensinar algoritmos e lógica de programação.

Eu possuo um blog sobre desenvolvimento e programação de jogos chamado Romero Games. A postagem mais popular desse blog é uma chamada "Jogos em Pascal", o que mostra que existe um público interessado neste assunto aqui no Brasil. Provavelmente são pessoas que perceberam que é bem mais interessante aprender a programar fazendo um jogo.

O jogo Mina 2 com código fonte pode ser baixado no link: Mina2.zip

Para facilitar a leitura dos artigos na ordem correta, criei uma página de Sumário que pode ser acessada na barra lateral direita do blog.