Blocos isométricos
Uma velha perspectiva para jogos novos

Alguém se lembra desse tipo de imagem aí ao lado? Não? Bem, então provavelmente tem menos de 20 anos. Quem tem um pouco mais que isso e descobriu o fantástico mundo dos computadores quando o mercado era dominado pelos micros da era MSX ou ainda antes, na era ZX Spectrum, então irá reconhecer aqui uma das cenas do clássico jogo Knightlore.

Aquele precursor do Indiana Jones que, em noites de lua cheia (todas eram) se transformava em um lobisomem, por conta de um feitiço que precisava ser desfeito através de uma "caldeirada", cuja receita incluia sapato velho, garrafas vazias, taças, bolas e mais um monte de objetos estranhos. Eram os primórdios da criação de jogos com qualidade gráfica excepcional. Todos originários de uma técnica chamada, na época, de Filmation II.

A Filmation II fez a cabeça de muitos programadores, que ainda estão na ativa. Ela data de 1985 e em 1987 a revista Micro Sistemas publicou uma extensa matéria, dando inclusive alguns macetes da sua estrutura. Muito do que foi tratado alí ainda é usado hoje e reproduzimos na íntegra (inclusive para os saudosistas dos tempos dourados) a referida matéria. Ela está aqui, no club TILT, na seção Reprint.

Mas o que nos interessa não são propriamente as técnicas de impressão de figuras, que tanto sucesso faziam naquela época, mas tratar de um tipo de estrutura de construção de jogos que é muito usada hoje em dia. E elas nasceram, de certa forma, do saudoso Knightlore e da Filmation II. Trata-se dos jogos que usam e abusam da perspectiva isométrica para criar o seu espaço funcional.

Neste tipo de jogo, o observador encontra-se geralmente num ponto ligeiramente acima da linha do horizonte, permitindo então que os objetos sejam criados em três dimensões. Como o ângulo de visão é bem aberto, são raras as situações onde o jogador não vê determinadas partes deste universo, por estarem encobertas por outros objetos. Muitos jogadores chamam a este estilo de "jogo de chão inclinado".

Listar aqui os jogos atuais que se valem desta estrutura iria consumir todo o nosso espaço só com os nomes. Apenas como registro, lembro que os mais famosos, além é claro do tradicional SimCity, temos atualmente o Warcraft, Diablo, Ages of Empire (um dos mais recentes) etc. O que talvez você não saiba é que esse tipo de estrutura é extremamente simples de ser construída. O maior trabalho fica mesmo por conta da criação gráfica e da arte dos elementos.

O que vou mostrar nesta matéria é um dos métodos que pode ser usado para a montagem do mundo, definido para um jogo qualquer. Classificamos como mundo um cenário estruturado por completo em um dado momento (por exemplo numa fase). A base de todo esse mecanismo é definir uma matriz de elementos (ou objetos) que irá determinar o que tem e o que não tem em cada "metro quadrado" do mundo. É claro que não precisa ser necessariamente "metro". Isso é apenas uma referência.


Então, lápis e papel na mão, digo: Delphi carregado, vamos montar a parte dos objetos tomando como ponto de partida um mundo baseado numa matriz de 10 por 10 células. Cada célula pode conter um objeto. No caso, usarei apenas cubos, que formarão uma espécie de lego digital (ei, outra idéia muito interessante para este tipo de construção: um programa educativo para a criançada construir coisas, como o pequeno arquiteto, feito com blocos de madeira).

  implementation
  {$R *.DFM}


  var
    Blocos: array[0..9,0..9] of byte;

Aqui está a matriz principal, onde definiremos a existência ou não dos cubos (objetos). Nosso mundo é visualizado através de uma TImage que já possui um diagrama das células.

  procedure MontaMundo;
  var
    Lin,Col,PosL,PosC: integer;
  begin
    ApagaCursor;
    HTel:= Form1.Image1.Canvas.Handle;
    for Lin:= 0 to 9 do begin
      for Col:= 0 to 9 do begin
        if Blocos[Lin,Col] <> 0 then begin
          case Blocos[Lin,Col] of
            1: HFig:= Form1.Cubo1.Canvas.Handle;
            2: HFig:= Form1.Cubo2.Canvas.Handle;
            3: HFig:= Form1.Cubo3.Canvas.Handle;
            4: HFig:= Form1.Cubo4.Canvas.Handle;
            5: HFig:= Form1.Cubo5.Canvas.Handle;
          end;
          PosL:= Lin * 10 + 31 - 30 + (Col * 10);
          PosC:= Col * 20 + 181 - (Lin * 20);
          BitBlt(HTel,PosC,PosL,40,41,HFig,40,0,SRCAND);
          BitBlt(HTel,PosC,PosL,40,41,HFig,00,0,SRCPAINT);
        end;
      end;
    end;
    Form1.Image1.Repaint
  end;

A montagem do mundo ocorre dentro de dois loopings (colunas/linhas) onde cada byte é testado em relação a seu conteúdo. Se o valor encontrado for zero, então na célula correspondente não existe nada. Se o valor estiver entre 1 e 5 (inclusive) então naquela célula existe um cubo.

Os cubos são definidos cada um em uma TImage distinta, que servirá de matriz para a impressão na TImage principal. Cada TImage de cubo já carrega consigo a sua respectiva máscara de impressão (já vimos em diversas matérias, aqui na TILT, a impressão com máscaras - BitBlt/SRCAND/SRCPAINT). Do Delphi 3 em diante existe uma função que cria esta máscara (Canvas.Mask), mas definindo a máscara como TImage dispomos de um maior controle sobre a qualidade das figuras e mantemos nosso sistema compatível com os outros Delphis (1 e 2).


Os cálculos de PosL e PosC são óbvios (estamos tomando como base as indicações na figura acima). Já sei, "+ 31 - 30" é redundante. Podia ser "+1", mas deixei a expressão por completo para que o leitor pudesse perceber melhor os valores envolvidos no cálculo.

Não vou detalhar aqui a impressão do cursor, afinal ele não é muito importante e pode ser compreendida diretamente do fonte completo. Sua mecânica não é muito diferente da impressão dos blocos. Também dispensa comentários a definição do cubo a ser impresso. Coisa que um evento onClick na respectiva TImage e uma var global resolve de maneira rápida e eficiente.

O que importa destacar nesta estrutura é que o tal mundo não passa de uma matriz das mais simples. Num nível simplificado como este, basta verificar cada elemento se ele contém um objeto ou se está "vazio". Estando "vazio" então um personagem poderia "passar" por ele.

Este truque, mais alguns elementos que mudam de célula (ou de posição) de forma ordenada e um evento OnTimer são mais do que suficientes para montar um jogo com animação e tudo mais. Um Knightlore moderno.

Meu propósito aqui não era criar um jogo específico, mas mostrar como esse tipo de sistema funciona. Usar essas estruturas depende do que cada um deseja criar.


Download...
Clique no link para fazer o download dos arquivos. Se sua assinatura do club TILT está para vencer, clique aqui e saiba como renová-la.

Fontes completos do exemplo da matéria
 
online