Strona:
15

Kurs Delphi - cześć 2

Pierwszy projekt, pierwsza gra

Na poprzedniej lekcji dowiedziałeś się jak sprawnie posługiwać się środowiskiem Delphi, nawet napisałeś mini aplikację, ale dziś zajmiemy się dużo ciekawszymi rzeczami - mianowicie napiszemy grę ! Pewnie teraz przeklinasz, że chciałeś nauczyć się pisać programy a nie gry. Nie obawiaj się, będzie to gra okienkowa, na niej łatwo będzie pokazać zasadzę działania instrukcji warunkowej IF. A więc do działa:

Gra będzie działać następująco:
1. Wciskamy przycisk start
2. Aplikacja generuje liczbę losową z podanego zakresu
3. Aplikacja zadaje nam pytanie o liczbę
4. Wyświetlenie odpowiedniego komunikatu
5.1 Jeśli to jest za dużo lub za mało to skok do punktu 3
5.2 Jeśli zgadliśmy to gratulacje.

Najpierw stwórzmy projekt:
Na formularzu umieść przycisk START, dwa pola tekstowe (textbox) oraz jedną etykietę (label). Powinno to wyglądać mniej więcej tak:

Mam nadzieję, ze jeszcze pamiętasz jak zmienia się etykietki oraz nazwy elementów. Przystąpmy do tej czynności - przycisk nazwij cmdstart i nadaj mu etykietę START, pierwsze pole tekstowe nazwij txtmin drugie txtmax. Może Cię teraz zdziwić fakt, że pole tekstowe nie zawiera właściwości Caption. Fakt, bo zamiast tego pole tekstowe posiada właściwość text - polu txtmin nadaj wartość 1 a polu txtmax 9999. Ostatnią czynnością jest zmienienie nazwy etykiety Label na lblwynik i także skasowanie zawartości pola caption. Teraz nie pozostało nam nic innego jak dodanie do przycisku START kodu. Ciekaw jestem czy pamiętasz jak to się robiło... 
A więc zaznacz przycisk, teraz w inspektorze obiektów przejdź do zakładki "Events" :

Zaznacz zdarzenie OnClick bo o takie nam chodzi i kliknij dwukrotnie na białym polu - powinniśmy zobaczyć cos takiego:

procedure TForm1.cmdstartClick(Sender: TObject);
begin

end;

end.

Fajnie, ale co teraz ? Będą nam potrzebne cztery zmienne - jedna będzie przechowywać wylosowaną liczbę w pamięci, druga podaną przez nas liczbę, trzecia sprawdzać czy podana została prawidłowa odpowiedź a czwarta który to już raz odpowiadamy. Musze tu wspomnieć, że będą to dwie różne zmienne, jedna będzie przechowywać liczby całkowite (bo takie będą losowane) a druga będzie przetrzymywać jedną z dwóch opcji - prawda lub fałsz. Uwaga - wszystkie zmienne deklarujemy jeszcze przed rozpoczeciem się danej procedury - czyli między liniami: procedure TForm1.FormClick(Sender: TObject); a begin. Do deklaracji zmiennych używamy słowa var - czyli nasz kod powinien wyglądać teraz tak:

procedure TForm1.cmdstartClick(Sender: TObject);
var
liczba, kolej:integer;
prop:string;
zgadnieto:bool;

begin

end;

Jak widzisz najpierw piszemy słowo var, potem pod spodem nazwę zmiennej, kolejno dwukropek i typ. Każdą prostą instrukcję kończymy średnikiem ! Prosta instrukcja to taka, która wykonuje się jakby sama, np. instrukcją prostą jest komunikat MessageBox, ale np. pętle składają się już z kilku linii i są to instrukcje złożone. 

Fajnie, mamy już zmienne, teraz musimy pomyśleć jak to ma działać. Samą aplikacje można zrobić na kilka sposobów, możemy poradzić sobie nawet bez użycia pętli, ale my jedną zastosujemy. Pętla jest instrukcją złożoną i decyduje ona o tym czy jej wnętrze ma się wykonać czy też nie. Nasza pętla będzie się wykonywać dopóty dopóki ni podamy prawidłowej odpowiedzi o czym będzie świadczyła zmienna "zgadnieto". A więc szkielecik pętli winien wyglądać tak:

procedure TForm1.cmdstartClick(Sender: TObject);
var
liczba, kolej:integer;
prop:string;
zgadnieto:bool;
begin
  while (zgadnieto=false) do
    begin

    end;

end;

Kod staje się pomału zagmatwany - ale dojdziemy do wszystkiego - najbardziej mylące mogą być te begin i end. Mamy już dwie pary - z tym, że jedna rozpoczyna i kończy zdarzenie, a druga jest ciałem pętli. W kodzie pojawiła się nowa instrukcja:

  while (zgadnieto=false) do
    begin
     
instrukcje;
    end;

While to angielskie dopóki - czyli jeśli zostanie spełniony warunek w nawiasie (w naszym przypadku jeśli w zmiennej zgadnięto będzie wyrażenie false) to wykonają się instrukcje zawarte między słowami begin i end. Ale mało tego, jeśli się wykonają to aplikacja powtórnie rozpatrzy wyrażenie - na tym polega pętla. Jeśli zdarzyło by się tak, że zmienna zgadnięto nigdy nie przyjmie wartości true to pętla będzie nieskończona i spowoduje to zawieszenie aplikacji !

Mamy już pętlę, teraz wypada wylosować jakąś liczbę z podanego przedziału. Tylko gdzie taki kawałek kodu wsadzić - do pętli czy przed pętle, czy może jeszcze za nią ? Za pętlą ? Wykluczone, w pętli ? Hmmmm, zastanówmy się - czy chcemy po każdej złej odpowiedzi losować nową liczbę ? Chyba nie, no wiec jedynie możemy zrobić to przed pętlą. Do generacji liczb losowych służy funkcja:

jakas_liczba = x + random(y);

Gdzie x jest wartością minimalną, a y maksymalną. Muszę wspomnieć o kolejnej rzeczy - w Delphim nie jest możliwe łączenie dwóch zmiennych różnych typów w sposób bezpośredni. O co chodzi - my w pole tekstowe będziemy wpisywać liczby, ale tak naprawdę zostaną one zwrócone jako łańcuch znaków a nie jako liczba.

Aby do łańcucha przeładować np. zawartość pola tekstowego to wpierw trzeba utworzyć zmienną typu string (nazwa:string), a następnie użyć instrukcji przekazania:

zmienna := poletekstowe.text;

Czy cos Ci to przypomina ? Ostatni człon to nic innego jak nazwa kontrolki i po kropce jedna z jej właściwości. Jak się okaże w przyszłości możemy tym sposobem zmieniać wszystkie właściwości. Np. chcąc zmienić etykietkę przycisku cmdstar możemy napisać:

cmdstart.caption := 'Nowa etykieta;'

I poznałeś znów kolejną rzecz - wszystkie łańcuchy tekstowe przetrzymujemy w znaczku apostrofa. Operatorem przypisania nie jest samo = jak w innych językach lecz :=  trzeba o tym pamiętać !

A więc zabierzmy się za generację liczby losowej - do kodu dopisz:

procedure TForm1.cmdstartClick(Sender: TObject);
var
liczba, kolej:integer;
prop:string;
zgadnieto:bool;
begin
zgadnieto := false;
randomize;
liczba := strtoint(txtmin.text) + random(strtoint(txtmax.text));

  while (zgadnieto=false) do
    begin

    end;

end;

Wygląda to bardzo groźnie - aby lepiej zrozumieć pokażę to porównawczo:

liczba := liczba_minimalna + random(liczba_maksymalna);

liczba := strtoint(txtmin.text) + random(strtoint(txtmax.text));

Wcześniej pisałem, że nie można sumować dwóch stringów, a więc zaraz po pobraniu danych z pól tekstowych należało je przerobić na liczby za pomocą instrukcji strtoint(jakisstring). Pod koniec tej lekcji będziemy zamieniać odwrotnie - liczby na stringa - wtedy lepiej to zrozumiesz.

A więc mamy wygenerowaną liczbę losową, jeszcze wspomnę o instrukcji randomize. Gdyby jej nie było, za każdym uruchomieniem aplikacji losowana była by ta sama liczba. Jeszcze wcześniej przyjąłem założenie, że w zmiennej zgadnięto od razu będzie false, czyli fałsz.

Teraz zadamy pierwsze pytanie - umieścimy go także przed pętlą za pomocą instrukcji:
InputBox('Tytuł','Treść','domyslnie');

Wartość zwraca stringa wpisanego do wyświetlonego pola. Jeśli w okienku naciśniemy cancel to zwrócona zostaje domyślna odpowiedź.

Do kodu więc dopiszmy:

procedure TForm1.cmdstartClick(Sender: TObject);
var
liczba, kolej:integer;
prop:string;
zgadnieto:bool;
begin
zgadnieto := false;
randomize;
liczba := strtoint(txtmin.text) + random(strtoint(txtmax.text));

prop := InputBox('Zgadula','Podaj liczbę','');
if (prop = '') then exit

  while (zgadnieto=false) do
    begin

    end;

end;

Pierwsza niebieska linijka to wyświetlenie komunikatu, druga linijka to sprawdzenie czy naciśnięto cancel. Instrukcja warunkowa IF jest bardzo powszechnie stosowana IF z angielskiego to Jeśli, a więc jeśli w zmiennej prop będzie pusto to wtedy wykona się instrukcja exit czyli zakończenie działania procedury i tym samym przerwanie gry. 

Możesz spróbować uruchomić swój projekt - zobaczysz przynajmniej okienko. Jak na razie ani OK ani CANCEL specjalnie nie działa, zmieńmy to więc.

Teraz musimy sprawdzić czy wpisana liczba do okienka była mniejsza, większa czy może równa liczbie wylosowanej. I znów posłużymy się instrukcja IF, ale teraz bardziej ją rozbudujemy - tzn na kilka linii.

procedure TForm1.cmdstartClick(Sender: TObject);
var
liczba, kolej:integer;
prop:string;
zgadnieto:bool;
label ponownie;
begin
zgadnieto := false;
kolej:= 1;
randomize;
liczba := strtoint(txtmin.text) + random(strtoint(txtmax.text));

prop := InputBox('Zgadula','Podaj liczbę','');
if (prop = '') then exit;
while (zgadnieto=false) do
begin

if (strtoint(prop) = liczba) then
begin
messagebox(0,'Gratulacje','Zgadula',MB_OK) ;
zgadnieto := true;
end;

if (strtoint(prop) < liczba) then
begin
prop := InputBox('Zgadula','Za mało !','');
kolej := kolej + 1;
if (prop = '') then exit;
goto ponownie;
end;

if (strtoint(prop) > liczba) then
begin
prop := InputBox('Zgadula','Za dużo !','');
kolej := kolej + 1;
if (prop = '') then exit;
goto ponownie;
end;

ponownie:

end;

lblwynik.caption := 'Zgadłeś za ' + inttostr(kolej) + ' razem';

end;

Tym razem 

Wszystko tutaj powinno być jasne, ale nowością jest label i goto. Label służy do deklaracji etykiety w kodzie a goto to nic innego jak przeskocz do podanej etykiety. Na końcu znów pokazane jest łączenie stringów i niezbędna konwersja z integer na string. Aplikacja gotowa - możemy już w nią grac :)

Dla bardziej wtajemniczonych - wiem, że dało się program napisać prościej, ale chciałem użyć większej liczby nowych instrukcji !

Aha - obiecałem na poprzedniej lekcji że dziś zajmiemy się inspektorem obiektów oraz poznamy dokładniej własciwości kontrolek. Pomyślałem jednak, że skoro miesiąc temu zajmowaliśmy się wyłącznie graficznym środowiskiem to dziś na odmianę napiszemy coś - lekcja o właściwościach za miesiąc...

Życzę udanej kompilacji

Slash
slash83@wp.pl