Jak zrobić grę
Potwoor_
Jak zrobić grę ?
1. Co użyjemy
-środowisko : Delphi ( praktycznie każda wersja )
-komponenty : Timer
-funkcje matematyczne : Sinus, Cosinus, potęgowanie i pierwiastkowanie drugiego stopnia, losowość
-funkcje graficzne : okrąg, wypełnianie, ustawianie pędzla i pióra
-elementy kodu : rekordy, funkcje, procedury, pętle, tablice dynamiczne
-zdarzenia : zmiana stanu klawiszy
posiadanie choćby szczątkowych informacji o wyżej wymienionych rzeczach
bardzo ułatwi osiągniecie celu ( tutaj stworzenie prostej gry )
2. Jaka to będzie gra?
-typ : strzelanka ( shooter )
-widok : z góry ( Third Person Perspective )
-poruszanie się : względne - zależne od obranego kierunku
-tryby gry : wieloosobowa ( hotseats )
-urządzenie sterujące : klawiatura
Tak wygląda ukończona:
3. Co? Gdzie? Jak?
Najpierw potrzebujemy nowy projekt, jeśli włączysz Delphi to od razu powinien być
przygotowany, jeśli nie to z menu głównego wybierz "File\New\Application"
Na początku masz dwa okna projektu:
-Forma - w skrócie jest to okno jakie będzie miał program ( nie będziemy używać,
możesz zminimalizować lecz nie zamykać! )
-Kod - okno z polem tekstowym gdzie będziemy wpisywać kod
Przeglądnij sobie to co już masz tam wpisane, pogrubione słowa to słowa kluczowe
Wyjaśnię kilka:
-Unit - deklaracja że ten plik jest modułem ( kawałek kodu wydzielony do osobnego pliku )
tekst między Unit a średnikiem to nazwa tego modułu
-interface - początek sekcji interface, tutaj będziemy deklarować zmienne globalne,
procedury, funkcje i typy
-type - rozpoczyna deklarację typu, jak widzisz jeden typ jest już zadeklarowany, nazywa się TForm1,
jest to typ opisujący naszą formę ( okno programu )
-var - rozpoczyna deklarację zmiennych, na razie mamy tylko jedną zmienną - Form1 typu TForm1,
czyli okno programu
-implementation - początek sekcji implementation, tutaj będziemy opisywać metody i funkcje
-end. - koniec kodu ( kropka oznacza że to jest koniec całości pliku, nic za end.
nie będzie uwzględnione przy kompilacji )
4. Deklaracja typów
Bierzemy się za robotę, na początku stworzymy typy odpowiadające postaci gracza i pocisku
-Gracz - najpierw trzeba wiedzieć co ma zawierać dany typ, więc co można powiedzieć o graczu w
takiej grze ( patrz punkt drugi ) ?
- ma jakąś pozycję
- ma jakiś kierunek ruchu
- ma ileś życia
- ma odstęp czasu pomiędzy strzałami
-Pocisk - tutaj podobnie tyle że koloru nie użyjemy, a pocisk raczej nie ma życia
- ma jakąś pozycję
- ma jakiś kierunek ruchu
Na pozycję ( punkt w układzie współrzędnych ) składają się dwa koordynaty: X i Y, możemy umieścić w typie
oba w osobnych zmiennych lub użyć gotowego typu TPoint, czyli punkt
W sekcji Interface ( patrz punkt 3 ) należy znaleźć miejsce gdzie zadeklarujemy typy, polecam gdzieś pomiędzy
deklaracją typu formy ( TForm1, deklaracja kończy się na end; ) a deklaracją zmiennych ( var )
Najpierw nazwa typu i czym on ma być, tutaj użyjemy rekordu
Gracz:
type TPlayer = record
Zmienne w tym typie:
Pozycja
Pos:TPoint;
Kierunek
Dir:integer;
Życie
Life:integer;
Odstęp pomiędzy strzałami
Fire:integer;
i kończymy
end;
Pocisk:
type TBullet = record
Zmienne w tym typie:
Pozycja
Pos:TPoint;
Kierunek
Dir:integer;
dodajemy też zmienna która się przyda przy kasowaniu pocisków
Del:boolean;
i kończymy
end;
5. Deklaracja zmiennych
Żeby przechować dane o dwóch graczach zadeklarujemy jednowymiarową
tablicę ( listę ) o dwóch komórkach ( stała długość )
Do przechowania informacji o pociskach użyjemy jednowymiarowej tablicy
dynamicznej ( długość będzie zmieniać się w trakcie działania programu )
w sekcji interface, ale po deklaracji typów dopisujemy
var
Players:array[0..1] of TPlayer;
Bulelts:array of TBullet;
6. Ruch
By gracze i pociski poruszały się w danym kierunku użyjemy
trygonometrii, wyjaśnię to za pomocą rysunku:
P1 to bieżąca pozycja, P2 to nowa, c to długość "kroku"
( odcinek o jaki przesuwamy ), a to odległość w pionie, b w
poziomie obu pozycji, α to kierunek ruchu ( 0° to ruch w
prawo, stopnie przyrastają w kierunku odwrotnym do ruchu
wskazówek zegara )
X2 i Y2 to koordynaty nowej pozycji, łatwo zauważyć że
X2=X1+b
Y2=Y1+a
więc mając X1 i Y2 ( koordynaty bieżącej pozycji ) potrzebujemy
jeszcze a i b do obliczenia nowej pozycji
z podstaw trygonometrii wiemy że sinus kąta to przyprostokątna
naprzeciw tego kąta ( a ) dzielona przez przez przeciwprostokątną ( c )
a cosinus to przeciwprostokątna przy kącie ( b ) dzielona przez
przeciwprostokątną ( c ), więc:
sinα=a/c
cosα=b/c
mnożymy obustronnie przez c i otrzymujemy
sinα*c=a
cosα*c=b
obracamy i mamy wzory na a i b
a=sinα*c
b=cosα*c
jest jeszcze jeden mały szkopuł, początek układy współrzędnych ekranu
leży w lewym górnym rogu, a oś Y jest obrócona ( gdyby nie była obrócona
operowalibyśmy na wartościach ujemnych ), zmiany wyglądają tak:
więc zamiast dodawać a do współrzędnej będziemy je odejmować
X2=X1+b
Y2=Y1-a
wyprowadzamy wzór końcowy
X2=X1+cosα*c
Y2=Y1-sinα*c
w kodzie będzie to wyglądać tak
X2:=X1+Cos(DegToRad(α))*c;
X2:=X1-Sin(DegToRad(α))*c;
tylko że może się zdarzyć iż wynik będzie mieć rozwinięcie dziesiętne
by tego uniknąć zaokrąglamy funkcją Trunc()
X2:=X1+Trunc(Cos(DegToRad(α))*c);
X2:=X1-Trunc(Sin(DegToRad(α))*c);
jako że funkcje Sin i Cos przyjmują argumenty w radianach a nie stopniach musimy
skonwertować stopnie na radiany ( funkcja DegToRad )
funkcja DegToRad znajduje się w module Math, więc trzeba go dodać do listy uses
( patrz punkt #3 ), ja dodałem na końcu
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, Math;
Teraz piszemy funkcję która zwróci nową pozycję gdy podamy jej bieżącą pozycję,
kierunek i długość kroku ( c )
w sekcji interface deklarujemy funkcję
Function Move(Pos:TPoint;Dir:integer;c:integer):TPoint;
Jak widać funkcji podajemy pozycję w typie TPoint, kierunek i długość kroku w
typie liczbowym, a funkcja zwróci jako wynik nową pozycję w typie TPoint
Teraz w sekcji implementation opisujemy funkcję
Najpierw nagłówek żeby kompilator wiedział jaką funkcję opisujemy
Function Move(Pos:TPoint;Dir:integer;c:integer):TPoint;
Otwieramy blok poleceń
begin
do składowej X wyniku przypisujemy wyliczony X
Result.X:=Pos.X+Trunc(Cos(DegToRad(Dir))*c);
do składowej Y wyniku przypisujemy wyliczony Y
Result.Y:=Pos.Y-Trunc(Sin(DegToRad(Dir))*c);
i kończymy funkcję
end;
7. Obliczanie odległości
do obliczenia odległości między dwoma punktami można użyć wzoru Pitagorasa
c*c=a*a+b*b;
do obliczania kwadratu użyjemy funkcji Sqr() a do obliczania pierwiastka Sqrt()
zapisujemy wzór Pitagorasa kodem Delphi
Sqr(c):=Sqr(a)+Sqr(b);
pozbywamy się potęgi z lewej strony równania
c:=Sqrt( Sqr(a) + Sqr(b) );
tu też dobrze jest zaokrąglić bo wynik pierwiastkowania też rzadko bywa liczbą całkowitą
c:=Trunc( Sqrt( Sqr(a) + Sqr(b) ) );
odcinki a i b trzeba najpierw obliczyć
jest to różnica odpowiednich par współrzędnych dwóch punktów
nawet jeśli wynik wyjdzie ujemny nie jest to przeszkodą bo przy podnoszeniu
do kwadratu wynik zawsze jest dodatni
c:=Trunc( Sqrt( Sqr( A.X - B.X ) + Sqr( A.Y - B.Y ) ) );
teraz deklaracja funkcji w sekcji interface
function Range(A:TPoint;B:TPoint):integer;
i opis funkcji w sekcji implementation
function Range(A:TPoint;B:TPoint):integer;
begin
przypisanie do wyniku funkcji wyniku obliczeń
Result:=Trunc( Sqrt( Sqr( A.X - B.X ) + Sqr( A.Y - B.Y ) ) );
8. Sterowanie
Nie będziemy sprawdzać stale stanu klawiszy lecz użyjemy zdarzeń na
wciśnięcie oraz puszczenie klawisza gdzie zmienimy odpowiednio stan
zmiennych logicznych ( możliwe wartości True lub False )
te zmienne logiczne będziemy sprawdzać, i zależnie od ich stanu wykonamy
pewne czynności ( ruch, zmiana kierunku, strzał )
Najpierw przyszykujemy typ w którym będą zmienne odpowiadające danym klawiszom
w sekcji interface, lecz ważne by przed deklaracją typu TPlayer piszemy
type TKeys = record
Left:boolean;
Right:boolean;
Up:boolean;
Down:boolean
Fire:boolean
end;
teraz modyfikujemy typ TPlayer by zawierał zmienną typu TKeys
czyli po prostu dopisujemy gdzieś pomiędzy record a end
Keys:TKeys;
Teraz użyjemy zdarzeń do modyfikowania stanu zmiennych
w Inspektorze Obiektów ( małe okienko na dole z lewej strony ) zmieniamy
zakładkę na Events i wybieramy OnKeyDown ( wciśnięcie klawisza ),
klikamy dwa razy i zostaniemy automatycznie przeniesieni do
procedury obsługi zdarzenia ( w oknie edytora kodu )
tak więc mamy na razie
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
end;
trzeba rozpoznać jaki klawisz został wciśnięty
użyjemy instrukcji case of, zależnie od stanu sprawdzanej
zmiennej wykonamy różny czynności
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
case Key of // zmienna Key zawiera numer wciśniętego klawisza
//cyfra przed dwukropkiem określa wartość w zmiennej Key
//przy której instrukcja za dwukropkiem zostanie wykonana
//klawisze pierwszego gracza: WSAD + spacja
87:Players[0].Keys.Up:=true; // klawisz W włącza ruch do przodu
83:Players[0].Keys.Down:=true; // klawisz S włącza ruch do tyłu
65:Players[0].Keys.Left:=true; // klawisz A włącza skręcanie w lewo
68:Players[0].Keys.Right:=true; // klawisz D włącza skręcanie w prawo
32:Players[0].Keys.Fire:=true; // spacja włącza strzelanie
//klawisze drugiego gracza: klawiatura numeryczna 4568 + enter
//wpisane niżej kody oznaczają cyfry a nie klawisze funkcyjne
//więc NumLock musi być włączony
//kod 13 odpowiada obu enter'om
104:Players[1].Keys.Up:=true; // klawisz 8 włącza ruch do przodu
101:Players[1].Keys.Down:=true; // klawisz 5 włącza ruch do tyłu
100:Players[1].Keys.Left:=true; // klawisz 4 włącza skręcanie w lewo
102:Players[1].Keys.Right:=true; // klawisz 6 włącza skręcanie w prawo
13 :Players[1].Keys.Fire:=true; // enter włącza strzelanie
end; // koniec case'a
end;
teraz wybieramy zdarzenie OnKeyUp, dwukrotnie klikamy
powinno być takie coś
procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
end;
instrukcja będzie prawie identyczna, z tym że zamiast
włączać ( true ) będziemy wyłączać ( false )
procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
case Key of // zmienna Key zawiera numer wciśniętego klawisza
//klawisze pierwszego gracza: WSAD + spacja
87:Players[0].Keys.Up:=false; // klawisz W wyłącza ruch do przodu
83:Players[0].Keys.Down:=false; // klawisz S wyłącza ruch do tyłu
65:Players[0].Keys.Left:=false; // klawisz A wyłącza skręcanie w lewo
68:Players[0].Keys.Right:=false; // klawisz D wyłącza skręcanie w prawo
32:Players[0].Keys.Fire:=false; // spacja wyłącza strzelanie
//klawisze drugiego gracza: klawiatura numeryczna 4568 + enter
//wpisane niżej kody oznaczają cyfry a nie klawisze funkcyjne
//więc NumLock musi być włączony
//kod 13 odpowiada obu enter'om
104:Players[1].Keys.Up:=false; // klawisz 8 wyłącza ruch do przodu
101:Players[1].Keys.Down:=false; // klawisz 5 wyłącza ruch do tyłu
100:Players[1].Keys.Left:=false; // klawisz 4 wyłącza skręcanie w lewo
102:Players[1].Keys.Right:=false; // klawisz 6 wyłącza skręcanie w prawo
13 :Players[1].Keys.Fire:=false; // enter wyłącza strzelanie
end; // koniec case'a
end;
9. Przemieszczanie się, strzelanie, ginięcie
Tutaj użyjemy w odpowiednich pętlach funkcje przygotowane wcześniej
To co teraz napiszemy musi być wywoływane cyklicznie
Użyjemy do tego komponentu Timer ( stoper,licznik czasu )
-w górnym oknie Delphi znajduje się sporo zakładek
-wybieramy zakładkę System
-wybieramy Timer ( jedno kliknięcie na zegarek - pierwsza ikonka )
-klikamy w dowolnym miejscu na formie
-na formie pojawi się kwadracik z rysunkiem zegarka, nie będzie go
widać po włączeniu programu
-wybieramy Timer
w zakładce Events w inspektorze obiektów jest tylko jedno
zdarzenie ( OnTimer ) otwieramy je podwójnym kliknięciem
w edytorze kodu zobaczymy takie coś
procedure TForm1.Timer1Timer(Sender: TObject);
begin
end;
będziemy potrzebować liczniki dla dwóch pętli więc deklarujemy je
procedure TForm1.Timer1Timer(Sender: TObject);
var
i,j:integer;
begin
end;
nie będzie to najbardziej optymalny algorytm
gdyż starałem się go zrobić w miarę czytelnie
zostanie ulepszony a zatem utrudniony a następnych
częściach tego kursu ( jeśli w ogóle będą )
procedure SetLength ustawia ilość komórek w tablicy dynamicznej
funkcja Length zwraca ilość komórek w tablicy
funkcja High zwraca indeks ostatniego elementu w tablicy
najpierw obsługa pocisków
pamiętaj by pisać w bloku instrukcji danego zdarzenia
czyli tym wygenerowanym automatycznie
pętla po wszystkich pociskach
lecz najpierw sprawdzimy czy są jakiekolwiek pociski
jeśli długość tablicy pocisków jest różna od zero wtedy
if Length(Bullets)<>0 then
begin
pętla po wszystkich pociskach
for i:=0 to High(Bullets) do
begin
przesuwamy pocisk
Bullet[i].Pos:=Move(Bullets[i].Pos,Bullets[i].Dir,20);
sprawdzamy czy pocisk nie wyszedł poza planszę ( obszar roboczy okna )
czy nie wyszedł poza lewą lub górną krawędź okna
if (Bullets[i].Pos.X<0) or (Bullets[i].Pos.Y<0) then
Bullets[i].Del:=true;
czy nie wyszedł poza prawą lub dolną krawędź okna
if (Bullets[i].Pos.X>Form1.ClientWidth) or (Bullets[i].Pos.Y>Form1.ClientHeight) then
Bullets[i].Del:=true;
zapętlamy po wszystkich graczach by sprawdzić odległośc pocisku od tych graczy
for j:=0 to 1 do
begin
sprawdzamy czy pocisk zderzył się z jakimś graczem
czyli czy odległość pocisku od środka gracza o kształcie okręgu
jest mniejsza bądź równa promieniowi tego okręgu ( tutaj 16 pikseli)
if Range(Players[j].Pos,Bullets[i].Pos)<=16 then
begin
ustawiamy pocisk do kasowania i ranimy gracza
Bullets[i].Del:=true;
Dec(Players[j].Life,5);
zakańczamy bloki
end;
end;
end;
end;
usuwamy "zużyte" pociski
pętla po wszystkich pociskach
i:=0;
while i<Length(Bullet) do
begin
sprawdzenie czy pocisk jest przeznaczony do kasacji
if Bullets[i].Del then
begin
usuwanie pocisku
sprawdzenie czy usuwany pocisk jest na końcu tablicy
jeśli tak to usuwamy ostatnią komórkę tablicy
jeśli nie to...
if i=High(Bullets) then
SetLength(Bullets,Length(Bullets)-1)
else
begin
do usuwanej komórki przypisujemy komórkę ostatnią i skracamy tablicę
Bullets[i]:=Bullets[High(Bullets)];
SetLength(Bullets,Length(Bullets)-1);
skoro do usuwanej komórki przypisaliśmy komórkę której
jeszcze nie sprawdziliśmy wypadało by ją sprawdzić
zrobimy to "cofając" pętlę tak więc ten sam element ale
z inną zawartością zostanie sprawdzony drugi raz
Dec(i);
zwiększanie licznika i koniec pętli
end;
end;
Inc(i)
end;
teraz obsługa graczy ( ciągle w tym samym zdarzeniu! )
pętla obejmująca każdego gracza ( tablica ma komórki o indeksach
0 i 1 )
for i:=0 to 1 do
begin
sprawdzamy czy obrót w lewo danego gracza jest włączony
if Players[i].Keys.Left then
jeśli tak to zwiększamy kąt o 10
Inc(Players[i].Dir,10);
teraz sprawdzamy obrót w prawo
if Players[i].Keys.Right then
jeśli tak to zmniejszamy kąt o 10
Dec(Players[i].Dir,10);
czy ruch do przodu jest włączony
if Players[i].Keys.Up then
jeśli tak to używamy funkcji Move do zmiany pozycji
Players[i].Pos:=Move(Players[i].Pos,Players[i].Dir,10);
czy ruch do tyłu jest włączony
if Players[i].Keys.Down then
jeśli tak to używamy funkcji Move do zmiany pozycji
ruch do tyłu uzyskamy podając ujemną długośc kroku
Players[i].Pos:=Move(Players[i].Pos,Players[i].Dir,-5);
teraz trudnijesze - strzelanie
sprawdzamy czy licznik odstęu między strzałami jest wyzerowany
if Players[i].Fire=0 then
begin
sprawdzamy czy strzelanie jest włączone
if Players[i].Keys.Fire then
begin
dodajemy nowa komórkę w tablicy pocisków
pozycji nowego pocisku przypisujemy pozycję gracza z przesunięciem
które oddali pocisk na tyle żeby gracz nie postrzelił sam siebie
kierunek ruchu pocisku ustawiamy na kierunek ruchu gracza by pocisk
mógł się sam poruszać
SetLength(Bullets,Length(Bullets)+1);
Bullets[High(Bullets)].Pos:=Move(Players[i].Pos,Players[i].Dir,20);
Bullets[High(Bullets)].Dir:=Players[i].Dir;
ustawiamy odstęp przed następnym strzałem
Players[i].Fire:=2;
zakańczamy bloki instrukcji i opisujemy co ma się dziać gdy licznik
odstępu miezy strzałami nie jest wyzerowany
end;
end
else
jeśli nie jest wyzerowany to go zbliżamy o 1 do zera
jeśli zmniejszamy ( Dec ) lub zwiększamy ( Inc ) o 1
to nie trzeba podawać liczby o którą zmieniamy
Dec(Players[i].Fire);
teraz trzeba sprawdzić czy gracz jeszcze żyje
sprawdzamy czy ilość życia jest mniejsza lub równa 0 ( niedodatnia )
if Players[i].Life<=0 then
begin
wylosujemy nowa pozycję dla gracza
procedura Randomize przygotowuje silnik losujący do użycia
funkcja Random zwraca liczbę pomiędzy zero a podanym argumentem
by losować z danego zakresu z pominięciem jego brzegu
( gdzie zakres zaczyna się od 0 )
a -> "margines"
max -> koniec zakresu
Random(Max-a)+a/2;
tutaj "marginesem" będzie średnica koła będącego graczem ( 32 )
Randomize();
Players[i].Pos.X:=Random(Form1.ClientWidth-32)+16;
Players[i].Pos.Y:=Random(Form1.ClientHeight-32)+16;
Skoro postać zginęła trzeba przwyrócić ją do życia
Players[i].Life:=100;
zakańczamy blok instrukcji
end;
konieć pętli
end;
10. Rysowanie
Rysowanie musi być przeprowadzane za każdą zmianą pozycji graczy i pocisków
Czyli najlepiej je umieścić tuż za tymi zmianami, czyli robimy je w
procedurze obsługi zdarzenia Ontimer ( to jedyne zdarzenie Timer'a )
możemy tworzyć kolejne pętle gdzie będziemy rysować lub robić to od razu
po zmianie pozycji obiektów
dla graczy zrobimy to przed końcem pętli ( po ewentualnej zmianie pozycji po
"śmierci" )
w pętli kasującej pociski będziemy je rysować , ale tylko jeśli nie zostaną
skasowane, więc rysowanie będzie jeśli pocisk nie będzie przeznaczony
do kasacji
najpierw jednak trzeba wyczyścić okno z poprzednich rysunków
czyszcenie umieścimy na początku procedury obsługi zdarzenia ( przed
pierwszą pętlą )
kolor jaki forma ma domyślnie jest zapisany w stałej clBtnFace
ustawimy ją jako kolor pędzla
procedura FillRect zamaluje podany prostokąt bieżącym pędzlem
funkcja Rect "składa" podane wymiary do rekordu TRect
my zamalowujemy od lewego górnego rogu do prawego dolnego
Form1.Canvas.Brush.Color:=clBtnFace;
Form1.Canvas.FillRect(Rect(0,0,Form1.ClientWidth,Form1.ClientHeight));
teraz rysowanie pocisków
kolor pędzla ustawimy przed pętlą by robić to tylko raz bo jak
zrobimy to w pętli to pędzel będzie ustawiany za każdym razem, co jest zbędne
więc przed pętlą while
clYellow to oczywiście stała zawierająca kolor żółty
Form1.Canvas.Brush.Color:=clYellow;
rysowanie pocisków wymaga zmodyfikowania zawartości pętli
blok instrukcji dla warunku ( IF ) wygląda jak dotąd tak:
if Bullets[i].Del then
begin
if i=High(Bullets) then
SetLength(Bullets,Length(Bullets)-1)
else
begin
Bullets[i]:=Bullets[High(Bullets)];
SetLength(Bullets,Length(Bullets)-1);
Dec(i)
end;
end;
dodajemy blok do wykonania gdy warunek nie zostanie spełniony ( else )
procedura Ellipse rysuje elipsę wpisaną w prostokąt którego dwa
przeciwległe kąty są podane ( pary współrzędnych )
jako wszystkie współrzędne podamy pozycję pocisku lecz pierwszą parę
zmniejszymy o promień rysowanego koła a druga zwiększymy
if Bullets[i].Del then
begin
if i=High(Bullets) then
SetLength(Bullets,Length(Bullets)-1)
else
begin
Bullets[i]:=Bullets[High(Bullets)];
SetLength(Bullets,Length(Bullets)-1);
Dec(i)
end
else
begin
Form1.Canvas.Ellipse(Bullets[i].Pos.X-3,Bullets[i].Pos.Y-3,Bullets[i].Pos.X+3,Bullets[i].Pos.Y+3);
end;
end;
teraz rysowanie graczy
wybieramy kolor na niebieski ( clBlue )
wstawiamy przed pętlą ( for i:=0 to 1 do )
Form1.Canvas.Brush.Color:=clBlue;
przed końcm pętli ( po losowaniu pozycji w przypadku śmierci )
wstawiamy rysowanie okręgu podobnie jak przy pociskach
Form1.Canvas.Ellipse(Players[i].Pos.X-16,Players[i].Pos.Y-16,Players[i].Pos.X+16,Players[i].Pos.Y+16);
11. Modyfikacje
Jeśli uważasz że postacie ( lub pociski ) poruszają
się za wolno ( lub za szybko ) zmień trzeci argument
podawany w funkcji Move()
By nie stracić na płynności zmień odstęp czasu pomiędzy
wykonywaniem ruchu i rysowaniem
wybierasz Timer i w inspektorze obiektów zmieniasz
właściwość Interval ( ja zmieniłem na 100ms )
12. The End
Zachęcam do wprowadzenia modyfikacji w kodzie
Mam nadzieję że na tym nie poprzestaniesz =]
Jeśli będą chętni a ja będę mieć czas i chęci
zrobię następne części, gdzie kod z tej części
zostanie zoptymalizowany ( ulepszony i przyśpieszony )
Oczywiście pojawią się też nowe rzeczy, np gra sieciowa,
ustawianie klawiszy, wiele broni, punktacja, bonusy itd
No właśnie .. Gdzie gotowiec ;p ?
a gdzie ten gotowiec?
można dokładniej co nie działa? =]
U mnie to nie działa.
Mam Delphi 7 Personal, ktoś pomoże ?
no cóż... zdarza się że taki ktosik co nic nie umie chce zrobić grę, i to dla takich ktosików jest =]
starałem się by było jak najprostsze, i na początku wsadziłem do FAQ =]
jeśli chodzi o gry w delphi to używa się raczej jakiś bibliotek takich jak omega
wiele kursów na www.unit1.pl