Desenvolver
Jogos em
DelphiX
Passos
• Adicionar o TDXDraw
– Onde se passa toda a acção
• Adicionar um TDXImageList
–
–
–
–
Um por cada tipo de imagens(heroi, balas...)
Adicionar as imagens aos Items
Associar ao TDXDraw
NOTA: ao adicionar uma imagem esta deve de ficar em TDIB
• Adicionar um TDXWaveList
– Para termos sons prontos a utilizar
• Adicionar um TDXTimer:
– Colocar a propriedade enabled a false
– Definir o intervalo de disparo
– É aqui que está o focus da questão!!
Classe’s VS Actores
• Construir Classes/UNIT’s para cada um
dos actores do Jogo :
type
TBullet = class(TImageSprite)
private
{colocar aqui atributos do actor}
public
{overload dos métodos que desejarmos redefinir}
procedure DoMove(moveCount:integer);override;
procedure DoDraw();override;
procedure DoCollision(Sprite:TSprite;varDone:Boolean);override;
end;
{ utilizar class completion: ctrl^c }
DXSprite Unit
•
Components
– TDXSpriteEngine
•
Objects
– TBackgroundSprite
• Chips property can be specified
• collision judgment can be set with the CollisionMapproperty
– TImageSprite
• Onde a imagem é mostrada
• Atributos:
– Image – imagem a mostrar
• surface é Image.PatternSurfaces[AnimStart+AnimPos]
– TSprite
• Class abstracta que apresenta os métodos:
– DoDraw
– DoMove
– DoColision
– TSpriteEngine
• Controlo os sprites a ele associados
Implementar os métodos de
DXDraw para gestão dos eventos
• DXDrawInitialize
{colocar o timer com 60 segs}
dxtimer1.Interval := 1000 div 40;
dxtimer1.enabled := true;
{outras coisas que desejar iniciar}
• DXDrawFinalize
dxtimer1.enabled := true;
Nota: as afectações ao Timer devem de ser feitas sempre
nestes métodos por definição.
TDXTimer – o responsável (1)
•
Este objecto é quem contém o código importante da aplicação, pois permite
o evoluir do jogo:
{se não conseguirmos desenhar, terminamos}
if (dxdraw1.canDraw = false) then exit;
{actualiza a situação de input nos estados}
DXInput1.Update();
{desenhar o fundo: sem stretch}
DXImageFundos.Items[0].Draw( dxdraw1.surface, 0, 0, 0);
{colocar a superficie com cor}
DXDraw1.Surface.Fill( dxdraw1.surface.colormatch(
RGB(0,100, 200)) );
{actualizar o ecrã, limpando os objectos mortos}
DXSpriteEngine1.Dead();
{chamada aos DoMove dos actores com um numero de pixels}
DXSpriteEngine1.move(2);
{desenhar os sprites na superficie de fundo}
DXSpriteEngine1.Draw();
TDXTimer – o responsável (2)
• O seguinte código também pode ser adicionado:
{utilizado para mostrar os frames per segundo e valores de
variaveis}
with DXDraw1.Surface.canvas do
begin
Brush.Style := bsClear;
Font.Color := clRed;
Font.Size := 20;
TextOut(0, 0, 'fps: ' +
intToStr(DXTimer1.FrameRate));
Release(); {obrigatorio}
end;
• Nunca esquecer NO FIM:
{nao aparece nada sem esta chamada}
DXDraw1.flip();
Criação dos objectos
• A criação é feita no Form onCreate event handler:
{criar o jogo}
theGame := TGame.Create();
theGame.sprite := dxSpriteEngine1.engine;
theGame.spriteEngine := dxdraw1;
theGame.input := dxInput1;
theGame.sound := dxSound1;
theGame.imageListFUndos := dxImageFundos;
theGame.imageListWalls := DXImageListWall;
theGame.imageListHeroi := DXImageListHeroi;
theGame.waveListHeroi := DXWaveHeroi;
theGame.pontos := 0;
{criar os actores}
// criar o Heroi
theHeroi := THeroi.Create(theGame);
•
•
theGame e theHeroi é um atributo da form1
Na criaçao do jogo faz-se sempre a associação entre os componentes
DelphiX da form e a instância de TGame
TImageSprite – atributos
• TImageSprite
–
–
–
–
–
–
–
–
AnimCount
AnimLooped
AnimPos
AnimSpeed
AnimStart
Image
PixelCheck
Tile
•Derived from TSprite
–BoundsRect
–ClientRect
–Collisioned
–Count
–Engine
–Height
–Items
–Moved
–Parent
–Visible
–Width
–WorldX
–WorldY
–X
–Y
–Z
Um Actor
• Um Actor encontra-se associado a um jogo, logo para se construir
um novo Actor deriva-se da classe TActor:
type
TActor = class (TImageSprite)
protected
game: TGame;
public
{apresenta um contructor apenas}
constructor Create(game: TGame);
end;
implementation
constructor TActor.Create(game: TGame);
begin
inherited Create(game.sprite); {associa o sprite}
self.game := game;
{associa-se ao jogo}
self.z := 10;
{atribui um valor por omissão ao Z}
end;
Dica
• Não esquecer de fazer a destruição de
todos os actores e game no form
onDestroy event handler:
theGame.free();
theGun.free();
TGame
• O Tipo TGame representa o Jogo
• Existe para simplificar e aproveitar as capacidades
drag-drop do Delphi
• Difere de jogo para jogo
• Apresenta como atributos :
– tudo o que representa o jogo
• (e.g.) score, vidas, tempo
– os elementos DelphiX utilizados
• (e.g.) TDXWaveList’s, TDXImageList’s, TDXInput...
• Os atributos não são privados o que viola as regras
de OOP, no entanto foi o melhor possível...
• Os atributos são afectados no evento de Create da
form que contém os componentes.
(e.g) TGame
type
TGame = class
// o que é indispensável {sempre}
sprite : TSprite;
spriteEngine: TDXDraw;
input: TDXInput;
sound: TDXSound;
// imageLists necessarias
imageListFUndos: TDXImageList;
imageListWalls: TDXImageList;
imageListHeroi: TDXImageList;
// wavelists necessarias
waveListHeroi: TDXWaveList;
// atributos necessarios ao Jogo
pontos: integer;
vidas: integer;
end;
Definição de um novo Actor
Derivar da classe TActor que deriva de
TImageSprite:
THeroi = class(TActor)
private
left, right, up, down: boolean;
public
constructor Create(game:TGame);
procedure DoDraw();override;
procedure DoMove(moveCount:integer);override;
procedure DoCollision(sprite: TSprite; var Done:boolean);override;
end;
•
•
•
Redefinir os métodos em que estamos interessados.(exemplos de
seguida)
Nota: os atributos que se encontram apresentados são específicos
deste Tipo
Nota: cada Actor deve de existir numa única UNIT: encapsulamento.
O Constructor
• A cada Actor está associado sempre um Game que é afectado
no Constructor – método Create(..):
constructor THeroi.Create(game: TGame);
begin
inherited Create(game); {obrigatório}
self.Image := self.game.imageListHeroi.Items[0];
self.Width := self.Image.Width;
self.height := self.Image.Height;
self.x := self.game.spriteEngine.Width div 2;
self.y := self.game.spriteEngine.height (2*self.Image.Height);
left := false;
right := false;
up := false;
down := false;
end;
• É no constructor que se afectam os atributos que representam o
Actor
Os Intervenientes
Movimento dos Objectos
• A redefinição do método onMove que é responsável
pelo movimento:
procedure THeroi.DoMove (moveCount: integer);
begin
Pode ser
inherited DoMove(moveCount);
utilizado para
if(isLeft in self.game.input.States) then mexer todos os
actores de igual
self.X := self.X - moveCount;
forma.
if(isRight in self.game.input.States) then
É afectado no
self.X := self.X + moveCount;
evento de
if(isUp in self.game.input.States)then
DXTimer
construção do
self.y := self.y - moveCount;
if(isDown in self.game.input.States) then TGame
self.y := self.y + moveCount;
collision(); {para testar se houve colisões}
end;
BMP’s especiais – animação do
Heroi
// animar o heroi
self.AnimPos := 0;
self.AnimStart := 20;
self.AnimCount := 6;
self.AnimSpeed := 5/1000;
self.AnimLooped := true;
Nota: as propriedades ParentHeight
e ParentWidth têm que ser
alterardas para representar o
tamanho do Heroi
Choque – detecção de colisões?
• Sempre que é feito um movimento verifica-se se houve choque
com outro actor de um tipo especificado:
procedure THeroi.DoCollision(sprite: TSprite; var Done:
boolean);
begin
inherited;
if (sprite is TWall) then
begin
self.game.waveListHeroi.items[0].play(false);
{gritar!!??}
end;
end;
Acesso a imagens ou sons
• Todos os TActor estão associados um game que conhece:
type
TGame = class
// o que é indispensável {sempre}
// imageLists necessarias
imageListFUndos: TDXImageList;
imageListWalls: TDXImageList;
imageListHeroi: TDXImageList;
// wavelists necessarias
waveListHeroi: TDXWaveList;
// atributos necessarios ao Jogo
pontos: integer;
vidas : integer:
False: nao espera
terminar o som
True: bloqueia
• Logo o acesso é feito da seguinte forma:
self.game.waveListHeroi.items[0].play(false);
self.game.imageListHeroi.Items[0]; {[‘nomeImagem’]}
Exemplo de um movimento
pseudo-aleatorio
// a utilizar no DoMove
if(random(30)=1) then
begin
self.Image := game.DXImageLaden.items[random(2)];
self.x := random(game.dxdraw1.Width);
self.y := random(game.dxdraw1.height-self.Image.Height);
end;
Não esquecer da chamada á
função randomize(); no
evento de criação da Form
que contém o jogo.
Carregar paredes
(leitura da informação de um Ficheiro)
assignFile(fich, 'level1.txt');
reset(fich);
cnt:=0;
while (not eof(fich)) do
begin
read(fich, ch);
if(not ((ch = #13) or (ch=#10)))then
begin
aux[cnt] := ch;
cnt := cnt + 1;
end;
end;
cnt:=0;
for yy:=0 to 6 do
for xx:=0 to 9 do
begin
if(aux[cnt]='0') then
TWall.Create(theGame, xx, yy);
cnt := cnt + 1;
end;
var
xx, yy: integer;
fich: file of char;
aux : array[0..100] of char;
cnt: integer;
ch: char;
Opções de Projecto – em Delphi
Project --> Options -->
application
Pode-se alterar várias
definições:
– icon
– title
– Help file
Download

desenvolver um Jogo em Delphix