Pierwsza gra

Jest sobotni wiecz≤r. W│a╢nie sko±czy│em siΩ kuµ do morderczej klas≤wki z biologii, kt≤ra bΩdzie w poniedzia│ek. Postanowi│em nie obejrzeµ kiepskich i napisaµ ten oto artyku│. O czym on bΩdzie? BΩdzie o pierwszej grze, kt≤r▒ mo┐na zacz▒µ wielk▒ przygodΩ z programowaniem gier. WiΩc usiaµ wygodnie,
skup siΩ i przeczytaj m≤j artyku│.

Na pocz▒tek proponuje trochΩ refleksji filozoficznych na temat programowania gier. WiΩkszo╢µ os≤b chce zacz▒µ programowanie gier od jakiego╢ prostego tetrisa. Ja uwa┐am inaczej! Zacznijmy od czego╢ oryginalnego! Proponuje zacz▒µ od dupy strony... najpierw tytu│ hmm... wiem! "SQUASH"!!! YEAH!!! zapowiada siΩ nie╝le. Teraz na czym bΩdzie polegaµ. BΩdzie to gra dla, dw≤ch graczy (┐ywych). Gra polega odbijaniu pi│ki o ╢cianΩ (na zmianΩ przez dw≤ch graczy). Ten kto nie odbije traci punkt. Gra siΩ do piΩtnastu. Zasady s▒ proste ale w│a╢nie one zapewni▒ miodno╢µ.

Czas przej╢µ do spraw trudniejszych... programowanie. Postanowi│em, ┐e kod przedstawiΩ w Pascalu. Nasz▒ grΩ ju┐ napisa│em w Delphi u┐ywaj▒c komponentu DelphiX. Najwa┐niejsze  ┐eby w tym momencie przyj▒µ za│o┐enia odno╢nie kodu. W grze dostΩpne bΩd▒ trzy obiekty:
1. Pi│ka
2. Paletka

Obydwa s▒ jakimi╢ obiektami o wsp≤lnych cechach takich jak:
1. Po│o┐enie
2. Wektor przemieszczenia (wektor czyli jak siΩ zmienia jego po│o┐enie)

Powinno siΩ zdefiniowaµ trzy klasy: TSprite, TPaletka, TBall. TSprite czyli obiekt podstawowy dla
TPaletka i TBall.

WstΩpne za│o┐enia przyjΩte. Czas zabraµ siΩ za pisanie kodu. S▒ dwie metody pisania. Od og≤│≤w do
szczeg≤│≤w lub odwrotnie. Osobi╢cie polecam pisanie od szczeg≤│≤w, poniewa┐ daje to nam mo┐liwo╢µ wczesnego wykrywania i usuwania b│Ωd≤w. Zaczynamy!

Najpierw trzeba utworzyµ nowy projekt wstawiµ do niego nastΩpuj▒ce komponenty:
-TDXDraw (ustaw property align na alClient)
-TDXTmier (ustaw property interval na 0)
-TDXInput (ustaw property mouse/enabled na true)

Ponadto utw≤rz nowy modu│ engine.pas i dodaj go do g│≤wnego unitu. Teraz bierzemy siΩ za engine.pas.
Najpierw klasa TSprite :
TSprite=class // klasa podstawowa dla wszystkich obiekt≤w
x,y:real;
vx,vy:real;
sx,sy:integer; // rozmiary
mx,my:integer; // rozmiary pola po jakim mozna sie poruszac
deadx:integer; // linia za ktora pilke uwaza sie martwa
points:integer; // jakies punkty
procedure ustaw(wx,wy,wvx,wvy:real;wsx,wsy,wmx,wmy,wdeadx:integer);virtual;
function xx:integer;virtual; // zwraca calkowita wartosc x
function yy:integer;virtual; // zwraca calkowita wartosc y
procedure draw(dxdraw:TDXDraw); virtual;
end;

implementation

/////////////
TSPRITE
/////////////
procedure TSprite.ustaw(wx,wy,wvx,wvy:real;wsx,wsy,wmx,wmy,wdeadx:integer);
begin
x:=wx;
y:=wy;
vx:=wvx;
vy:=wvy;
sx:=wsx;
sy:=wsy;
mx:=wmx;
my:=wmy;
deadx:=wdeadx;
points:=0;
end;

function TSprite.xx:integer;
begin
xx:=trunc(x);
end;

function TSprite.yy:integer;
begin
yy:=trunc(y);
end;

procedure TSprite.draw(dxdraw:TDXDraw);
begin
dxdraw.surface.canvas.Rectangle(xx-sx div 2,yy-sy div 2,xx+sx div 2,yy+sy div 2);
end;

Opis poszczeg≤lnych procedur i w│a╢ciwo╢ci:
ustaw- inicjuje wszystkie zmienne
xx i yy: funkcje zwracaj▒ ca│kowit▒ warto╢µ zmiennych x i y. Z pewno╢ci▒
            ciekawi ciebie dlaczego to s▒ typy real. DziΩki takiemu zabiegowi
            otzrymamy p│ynno╢µ ruchu. Nale┐y tak┐e pamiΩtaµ, ┐e efekt p│ynnego
            ruch otzrymamy dopiero dziΩki funkcji trunc.
            UWAGA!!! U┐ycie funkcji round niweluje jaki kolwiek p│ynny ruch.
mx, my: rozmiary ,,boiska''
sx, sy: rozmiary sprita
draw: rysuje sprita w postaci zwyk│ego prostok▒ta

Teraz nastΩpna klasa:
TKontrol=(kKey,KMouse);
TPaletka=class(TSprite)
procedure paletka(dxdraw:TDXDraw;input:TDXInput;kontroler:TKontrol);
procedure move;
procedure draw(dxdraw:TDXDraw); overload;
end;

implementation

procedure TPaletka.paletka(dxdraw:TDXDraw;input:TDXInput;kontroler:TKontrol);
begin
vx:=0;
vy:=0;
case kontroler of
kKey:begin
if input.Keyboard.Keys[vk_up]=true then vy:=-input.Keyboard.ButtonCount / 10;
if input.Keyboard.Keys[vk_down]=true then vy:=input.Keyboard.ButtonCount / 10;
if input.Keyboard.Keys[vk_left]=true then vx:=-input.Keyboard.ButtonCount / 10;
if input.Keyboard.Keys[vk_right]=true then vx:=input.Keyboard.ButtonCount / 10;
end;
kMouse:begin
vx:=(input.Mouse.X*x) / x;
vy:=(input.Mouse.y*y) / y;
end;
end;
move;
draw(dxdraw);
end;

procedure TPaletka.move;
var
tx,ty:real;
begin
tx:=x+vx;
ty:=y+vy;
if (ty<=0) or (ty>=my) then
begin
vy:=0;
ty:=y;
end;
if (tx<=0) or (tx>=mx) then
begin
vx:=0;
tx:=x;
end;
x:=tx;
y:=ty;
end;

procedure TPaletka.draw(dxdraw:TDXDraw);
begin
dxdraw.surface.canvas.brush.Color:=RGB(0,0,255);
dxdraw.surface.canvas.pen.Color:=RGB(0,0,255);
dxdraw.surface.canvas.Ellipse(xx-sx div 2,yy-sy div 2,xx+sx div 2,yy+sy div 2);
end;

Opis poszczeg≤lnych procedur i w│a╢ciwo╢ci:
paletka- wywo│uje procki move i draw
move- dodaje vx i vy do x i y, inaczej przesuwa paletkΩ :)
draw: rysuje paletkΩ jako elipsΩ

Ostania klasa:
TBall=class(TSprite)
procedure ball(dxdraw:TDXDraw;var paletka:TPaletka;var who:byte);
procedure fire; // wystrzeliwuje pilke
procedure move(var paletka:TPaletka;var who:byte);
procedure draw(dxdraw:TDXDraw); overload;
end;

implementation

procedure TBall.ball(dxdraw:TDXDraw;var paletka:TPaletka;var who:byte);
begin
move(paletka,who);
draw(dxdraw);
end;

procedure TBall.fire;
begin
x:=deadx;
y:=my div 2;
vx:=-(random+5.2);
vy:=(random - random)*2;
end;

procedure TBall.move(var paletka:TPaletka;var who:byte);
var
tx,ty:real;
x1,x2,y1,y2:real;
begin
tx:=x+vx;
ty:=y+vy;
begin
x1:=paletka.x - paletka.sx / 2 - vx;
y1:=paletka.y - paletka.sy / 2 - vy;
x2:=paletka.x + paletka.sx / 2 + vx;
y2:=paletka.y + paletka.sy / 2 + vy;
end;
if (tx>=x1) and (ty>=y1) and (tx<=x2) and (ty<=y2) then
begin
tx:=x;
vx:=-(2*vx-paletka.vx) / 2;
vy:=-(2*vy+paletka.vy) / 2;
tx:=tx+vx;
inc(paletka.points);
who:=abs(who-1);
end;
if (tx<=0) or (tx>=mx) then
begin
if (tx>=mx) and ((vx>0) or (vy>0)) then
begin
dec(paletka.points);
vx:=0;
vy:=0;
end;
vx:=-vx;
tx:=tx+vx;
end;
if (ty<=0) or (ty>=my) then
begin
vy:=-vy;
ty:=ty+vy;
end;
x:=tx;
y:=ty;
end;

procedure TBall.draw(dxdraw:TDXDraw);
begin
dxdraw.surface.canvas.brush.Color:=RGB(255,0,0);
dxdraw.surface.canvas.pen.Color:=RGB(255,0,0);
dxdraw.surface.canvas.Ellipse(xx-sx div 2,yy-sy div 2,xx+sx div 2,yy+sy div 2);
end;

Opis poszczeg≤lnych procedur i w│a╢ciwo╢ci:
ball- wywo│uje procki move i draw
move- dodaje vx i vy do x i y, inaczej przesuwa paletkΩ oraz sprawdza czy:)
draw: rysuje pi│kΩ jako ko│o

C≤┐ mamy zrobiony engine. Powy┐szy kod jest trochΩ nie dopracowany ale dzia│a.
W tej chwili najwa┐niejszy jest mechanizm. Odrywamy siΩ od modu│u mapa. pas i wracamy
do g│≤wnego Teraz utw≤rz dwa zdarzenia:
-zdarzenie OnCreate g│≤wnego formularza
-zdarzenie OnTimer komponentu DXTimer

Dopisz tak┐e dwie trzy: paletka1, paletka2:TPaletka, who:byte i ball:TBall;

Teraz w procedurze obs│uguj▒cej zdarzenie OnCreate:
procedure TForm1.FormCreate(Sender: TObject);
begin
paletka1:=TPaletka.Create;
paletka2:=TPaletka.Create;
ball:=TBall.Create;
paletka1.ustaw(dxdraw.width,dxdraw.height div 2,0,0,10,50,dxdraw.width,dxdraw.height,dxdraw.width);
paletka2.ustaw(dxdraw.width,dxdraw.height div 2,0,0,10,50,dxdraw.width,dxdraw.height,dxdraw.width);
ball.ustaw(dxdraw.width,random(dxdraw.height)
,0,0,10,10,dxdraw.width,dxdraw.height,dxdraw.width);
who:=0;
end;

Natomiast w timerze:
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
begin
if (ball.vx=0) and (ball.vy=0) and (dxinput.keyboard.keys[VK_RETURN]=true) then
ball.fire;
dxdraw.surface.Fill(clwhite);
dxinput.update;
paletka1.paletka(dxdraw,dxinput,kmouse);
paletka2.paletka(dxdraw,dxinput,kkey);
case who of
0:ball.ball(dxdraw,paletka1,who);
1:ball.ball(dxdraw,paletka2,who);
end;
dxdraw.surface.Canvas.Release;
dxdraw.Flip;
end;

Tym sposobem napisali╢my najprostsza z mo┐liwych gier. Mo┐esz ╢ci▒gn▒µ ╝r≤d│a  st▒d [squash.zip].
Mam nadzieje, ┐e co╢ za│apa│e╢(a╢)... oraz, ┐e tkn▒│em w tobie tak cenne natchnienie tw≤rcze Bez
niego nic nie da siΩ napisaµ. Oto propozycje rozszerzenia:
- dodanie jaki╢ zasad punktowania
- bitmapy zamiast zwyk│ych figur
- t│o
- d╝wiΩki
- i cokolwiek zdo│asz wymy╢liµ

Je┐eli masz jakie╢ pytania pisz na moj▒ skrzynkΩ. ChΩtnie pomogΩ...

Grzegorz GREG Ta±czyk
warsztat@poczta.fm
 

 Copyright © 2000 PTiK. Wszystkie prawa zastrze┐one.
 Kopiowanie tekst≤w w ca│o╢ci lub we fragmentach bez zgody redakcji i autor≤w zabronione.