Časovač

Časovač je nevizuálny komponent (počas behu programu ho nevidíme, ale prejavuje sa svojou činnosťou). Jeho úlohou je v pravidelných intervaloch generovať udalosť OnTimer. Niektoré zaujímavé vlastnosti časovača:

  • Interval ... určuje čas v milesekundách medzi dvoma "tiknutiami" (udalosťami OnTimer)
  • Enabled
... povolenie (true) alebo zakázanie (false) činnosti časovača

časovač na palete s komponentmi.

Do formulára položíme komponenty:

  • TTimer (nachádza sa medzi komponentmi na záložke System);
  • TLabel

Pri udalosti časovača OnTimer sa zavolá funkcia Timer1Timer:

int Cas=0;

void __fastcall TForm1::Timer1Timer(TObject *Sender) { Cas++; Label1->Caption=Cas; }

Môžeme používať aj viacero časovačov, pričom každý z nich "tiká" podľa svojich intervaloch.

int Cas=0;

void __fastcall TForm1::Timer1Timer(TObject *Sender) { Cas++; Label1->Caption=IntToStr(Cas); }

int Cas2=0;

void __fastcall TForm1::Timer2Timer(TObject *Sender) { Cas2++; Label2->Caption=Cas2; }

void __fastcall TForm1::Button1Click(TObject *Sender) { Timer1->Enabled=!Timer1->Enabled; Timer2->Enabled=!Timer2->Enabled; }

Na prvý pohľad bežia oba časovače paralelne, v skutočnosti sa veľmi rýchlo strieda vykonávanie funkcií Timer1Timer a Timer2Timer.

Animácia

Do formulára vložíme komponent obrázok a časovač. Na vykresľovanie animácie vyžijeme grafickú plochu obrázka: TCanvas *g; g=Image1->Canvas; Časovaču treba nastaviť Interval na hodnotu 50. Čím je tento interval kratší, tým sa za sekundu nakreslí viac obrázkov, a preto bude animácie plynulejšia (nebude trhaná).

int y=0;

void __fastcall TForm1::Timer1Timer(TObject *Sender) { g->Brush->Color=clWhite; g->FillRect(Image1->ClientRect); y=y+5; if (y>=Image1->Height) y=0; g->Brush->Color=clRed; g->Ellipse(100-10, y-10, 100+10, y+10); }

Rovnomerne zrýchlený pohyb:

float y=0, dy=0;

void __fastcall TForm1::Timer1Timer(TObject *Sender) { g->Brush->Color=clWhite; g->FillRect(Image1->ClientRect); dy=dy+0.5; y=y+dy; if (y>Image1->Height-10) { y=Image1->Height-10; dy=-dy; } g->Brush->Color=clRed; g->Ellipse(100-10, y-10, 100+10, y+10); }

Ovládanie pomocou klávesnice

Šípkami na klávesnici budeme určovať smer pohybu štvorčeka. Využijeme udalosti formulára:

  • OnKeyDown ... vzniká pri stlačení klávesu
  • OnKeyUp ... vzniká pri pustení klávesu

Ukážka udalosti OnKeyDown (OnKeyUp funguje analogicky):

void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { Caption=Key; }

V parametri Key je uložený kód práve stlačeného klávesu - celé 16-bitové číslo (typ WORD). Pri stláčaní klávesov mení nadpis formulára. Tak sa dajú zistiť kódy rôznych klávesov, napríklad, kláves šípka vľavo má kód 37 - namiesto čísel sa však ľahšie pamätá konštanta VKLEFT. Analogicky existujú konštanty VKRIGHT, VKUP, VKDOWN.

Udalosti klávesnice vznikajú iba vtedy, keď je okno našej aplikácie aktívne. Pozor: ak v aplikácii použijeme tlačidlá, editovacie políčka a podobné komponenty, ktoré reagujú na vstup z klávesnice, treba formuláru nastaviť vlastnosť KeyPreview na true a funkcie k udalostiam OnKeyDown a OnKeyUp programovať veľmi obozretne.

Ovládanie pohybu šípkami na klávesnici:

int x=0, y=0, dX=0, dY=0;

void __fastcall TForm1::Timer1Timer(TObject *Sender) { g->Brush->Color=clWhite; g->FillRect(Image1->ClientRect); x=x+dX; y=y+dY; g->Brush->Color=clRed; g->Rectangle(x-10, y-10, x+10, y+10); }

void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { if (Key==VK_LEFT) { dX=-5; Key=0; } else if (Key==VK_RIGHT) { dX=+5; Key=0; } else if (Key==VK_UP) { dY=-5; Key=0; } else if (Key==VK_DOWN) { dY=+5; Key=0; } }

void __fastcall TForm1::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift) { if (Key==VKLEFT || Key==VKRIGHT) { dX=0; Key=0; } else if (Key==VKUP || Key==VKDOWN) { dY=0; Key=0; } }

Všimnime si, že do Key priraďujeme hodnotu 0. Tým povieme, že iné komponenty, ktoré s klávesnicou pracujú, už nemajú reagovať na stlačenie tohto klávesu (pozn.: parameter Key nie je vďaka symbolu & iba obyčajný parameter - bude o tom podrobnejšia prednáška).

Predchádzajúci program vyzerá na prvý pohľad komplikovane. Spôsobuje to viacnásobné vnáranie príkazu if ... else. Pritom chceme podľa hodnoty v premennej Key zvoliť iba jednu zo štyroch možných alternatív a vykonať pár príkazov na nastavenie smeru pohybu. Pre takéto situácie existuje v C++ príkaz switch - pomocou neho zapíšeme predchádzajúci program elegantne.

Najčastejšie používaná syntax príkazu:

switch (výraz) {

case hodnota1:

príkaz; ... príkaz; break;

case hodnota2:

príkaz; ... príkaz; break; ...

default:

príkaz; ... príkaz; }

Ako príkaz funguje:

  • vyhodnotí sa výraz a podľa jeho hodnoty sa rozhodne, ktorá vetva case sa vykoná:
o ak výraz==hodnota1, začnú sa vykonávať príkazy za vetvou case hodnota1: ... o ak výraz==hodnota2, začnú sa vykonávať príkazy za vetvou case hodnota2: ... o ...

  • ak sa hodnota výrazu nezhoduje so žiadnou hodnotou uvedenou za case, vykonajú sa príkazy za vyhradeným slovom default: (táto vetva je nepovinná - nemusíme ju písať)
  • kľúčové slovo break ukončí vykonávanie príkazu switch. Keby sme break zabudli napísať, prípadne ho naschvál nenapísali, pokračovalo by sa vo vykonávaní príkazov aj z nasledujúcej vetvy case (prípadne default).

void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { switch (Key) {

case VK_LEFT:

dX=-5; Key=0; break;

case VK_RIGHT:

dX=+5; Key=0; break;

case VK_UP:

dY=-5; Key=0; break;

case VK_DOWN:

dY=+5; Key=0; } }

void __fastcall TForm1::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift) { switch (Key) {

case VK_LEFT:

case VK_RIGHT:

dX=0; Key=0; break;

case VK_UP:

case VK_DOWN:

dY=0; Key=0; } }

Animácia viacerých objektov

V prípade animovania viacerých objektov nepoužijeme pre každý objekt zvlášť časovať. Bolo veľmi neefektívne a objekty by sme ťažko synchronizovali. Preto používame iba jediný časovač a funkciu, ktorá obsluhuje udalosť OnTimer iba mierne modifikujeme:

  • zmažeme grafickú plochu
  • pohneme všetky objekty
  • nakreslíme všetky objekty

class TVlocka {

private:

int X, Y;

public:

void Nastav(int nX, int nY); void Pohni(); void Kresli(); };

void TVlocka::Nastav(int nX, int nY) { X=nX; Y=nY; }

void TVlocka::Pohni() { if (random(4)==0) X=X+random(3)-1; Y=Y+2+random(2); if (Y>Form1->Image1->Height) Y=0; }

void TVlocka::Kresli() { g->Pixels[X][Y]=clWhite; }

const Max=1000; TVlocka v[Max]; int poc=0;

void __fastcall TForm1::Timer1Timer(TObject *Sender) { g->Brush->Color=clBlack; g->FillRect(Image1->ClientRect); g->Brush->Color=clRed; for (int i=0; i=Max) return; v[poc].Nastav(X, Y); poc++; }

void __fastcall TForm1::Image1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if (!Shift.Contains(ssLeft) || poc>=Max) return; v[poc].Nastav(X, Y); poc++; }