|
Os
conceitos básicos
Então você aceitou mesmo o desafio de aprender/criar sete jogos clássicos em sete dias. Bem, pelo menos é corajoso. Mas vamos ao que interessa. Em primeiro lugar quero dizer que escolhi esses sete jogos em especial porque eles permitirão mostrar uma coisa básica, em desenvolvimento de jogos: uma estrutura pode (mediante pequenos ajustes) servir para diversos modelos de jogos. E em segundo lugar, servirão para mostrar como fazer um jogo é fácil, bastando que esteja com o pensamento focado naquilo que realmente importa: o jogo e não tanto a programação, a linguagem, a cor da cueca do Romero ou no quanto os brasileiros são mais criativos e geniais que os desenvolvedores americanos, embora continuemos pobres e copiando Quakes e Starcrafts.
O Delphi, é claro. Mas dentro do Delphi, que recursos usaremos? Nada de openGL, direct-X, engines, dlls, vudus, magia negra, branca ou azul celeste e muito menos matemática quântica, com 50 casas decimais de precisão. Vamos usar inteiros. Smallints, para ser mais objetivo. Isso porque um smallint vai de -32.768 até 32.767, ou seja, 16 bits (2 bytes). O integer usa 32 bits e efetivamente gastamos processamento à toa com valores que nunca passam da casa dos mil e qualquer coisa. Pense um pouco: tirando o placar, que podemos variar entre milhões de valores, todo o processamento de um jogo se restringe à área de vídeo onde ele acontece. Qual é a máxima resolução que o seu monitor permite? Pouco mais de mil pixels, não é mesmo? Então, é nessa faixa que estarão as coordenafas X,Y de tudo aquilo que for mandado para a tela. Também usaremos TBitmaps, que são os elementos gráficos mais importantes. São imagens definidas na memória, as quais temos acesso rápido e sem firulas. Serão usados para conter as figuras e elementos gráficos, bem como o buffer de construção da imagem do jogo. Usaremos um TTimer para criar um looping de processamento e um TPanel para servir de visor de jogo. Tudo o que for referente ao jogo, estará sendo mostrado nesse visor. Creio ser desnecessário lembrar mesmo aos programadores com pouca quilometragem no Delphi, que é preciso declarar as variáveis globais e as procedures que criaremos, nos seus respectivos lugares dentro do fonte. Para efeito de padronização, usarei em todos os exemplos uma medida de visor de 512 pixels de largura por 320 pixels de altura. As imagens serão monocromáticas, para facilitar a construção dos exemplos (a maioria virá mesmo dos próprios jogos clássicos) e para que o aprendiz mais ousado se sinta tentado a criar, ou modificar, o seu próprio set de elementos. Se for atento, perceberá logo de cara que basta pegar uma figura, pintá-la ou modificá-la como achar mais "artístico" e o jogo continuará funcionando do mesmo jeito. Monocromático significa preto e branco? De jeito nenhum. Monocromático significa uma única cor, porém com variação de tons (do mais claro ao mais escuro). Basicamente usarei esse tom verde musgo amazônico de fundo e seu parente mais claro. Assim, fica parecendo joguinho de celular, em fundo de cristal azulado.
Começamos sempre criando um novo form no Delphi. Lembre-se que qualquer jogo pode ser dividido em 4 partes: procedimentos de inicialização, rotinas e procedures, looping de processamento da partida, procedimentos de finalização. Para a inicialização usamos o evento OnCreate, do form1. Nele criamos todos os TBitmaps, carregamos as imagens e tudo mais que for necessário para controlar corretamente uma partida. Por exemplo, vamos declarar (global) o buffer de tela:
No evento OnCreate fazemos a definição desse buffer, tomando como ponto de partida um TPanel, chamado Pan, com 512 de largura por 320 de altura. Se ainda não o criou, basta fazê-lo. Aproveite e limpe o Caption dele e coloque o BevelOuter em bvNone. Ah, e de preferência, posicione-o no topo do form.
Desnecessário dizer que o jogo se ajustará às medidas desse TPanel, para que possamos mais tarde fazer modificações nos formatos e dimensões do jogo, sem precisar retornar à listagem para corrigir as dimensões e valores usados. Aproveitando o embalo, é aconselhável sempre que criar algum TBitmap para uso global, já inserir a sua destruição, quando o programa for encerrado pelo usuário. No evento OnDestroy do form1 fazemos:
Isso vale para todos os gráficos criados dentro do âmbito global. O passo seguinte é produzir uma procedure que monte a imagem da tela e a remeta ao TPanel, para ser vista pelo jogador. Faremos uma procedure à parte e a primeira providência é declará-la na seção public, lá no começo da listagem:
E em alguma parte do fonte (geralmente coloco logo após o evento OnDestroy):
Note a beleza dessa estrutura: tudo que desejamos mostrar para o jogador é colocado no buffer (Buff) e no final do processamento, mandamos uma cópia dele para o visor. Você pode ainda colocar uma chamada MontaTela no final do processamento do evento OnCreate, do form1. Assim, quando usuário rodar o programa. já terá uma visão do jogo.
Sim, eu sei que tem umas coisas estranhas neste princípio de programação e tentarei explicá-las de forma bem simples: Buff.PixelFormat:= pfDevice; Isso faz que com o formato gráfico do buffer, ou seja a sua profundidade de cor, seja igual a que está sendo usada no Windows. Assim, todas as transferências do buffer para a "tela" serão feitas usando-se as rotinas otimizadas da sua placa aceleradora de vídeo, causando uma operação rápida e evitando os famigerados flicks de tela. Buff.Canvas.FillRect(bounds(0,0,Pan.Width,Pan.Height)); Sempre que trabalhamos com um retângulo, no Delphi, podemos escolher que método usamos para definí-lo. Bounds tem como parâmetros as coordenadas do canto superior esquerdo mais largura e altura do retângulo. Rect usa as coordenadas do canto superior esquerdo e as coordenadas do canto inferior direito. Particularmente uso bounds porque fica mais óbvia a programação.
Hpan:= GetDC(Pan.Handle); Hpan é um handle para um bitmap e como o TPanel não tem canvas, precisamos "capturar" um handle para ele. Usamos BitBlt para transferir e em seguida liberamos o handle. A vantagem de usar o TPanel é que essa transferência ocorre diretamente para a memória de vídeo, sem escalas. Se usássemos um TImage, haveria a transferência para o componente e só depois para a memória de vídeo. Para os mais acomodados, o link abaixo permite fazer o download desse fonte introdutório, onde foi acrescentado uma chave liga/desliga para o jogo (OnClick no TPanel, manipulando a variável Enable do Timer1).
|