V tomto príklade uvádzame udalosť časovača (timer event) kvôli implementácii streľby.
CannonField teraz vie strieľať.
bool isShooting() const { return shooting; }
Vracia TRUE ak je strela vo vzduchu.
void shoot();
Volanie tohto slotu spôsobí, že kanón vystrelí, ak nie je strela vo vzduchu.
protected: void timerEvent( QTimerEvent * );
Udalosť časovača (timer event) je tretí typ udalosti widgetu, ktorý stretávame. Môžete dosiahnuť, aby Qt volala obsluhu tejto udalosti v pravidelných intervaloch.
private: void stopShooting();
Táto privátna funkcia zastaví strelu vo vzduchu.
void paintShot( QPainter * );
Táto privátna funkcia nakreslí strelu.
QRect shotRect() const;
Táto privátna funkcia vracia obdĺžnik obklopujúci strelu, ak je vo vzduchu, inak je vrátený obdĺžnik nedefinovaný.
bool shooting;
Táto privátna premenná je TRUE ak je strela vo vzduchu.
int timerCount; float shoot_ang; float shoot_f; };
Tieto privátne premenné obsahujú popis výstrelu. Premenná timerCount
sleduje čas od výstrelu. Premenná shoot_ang
je uhol výstrelu a
shoot_f
je sila výstrelu.
#include <math.h>
Vkladáme knižnicu s matematikou kvôli funkciám sin() a cos().
CannonField::CannonField( QWidget *parent, const char *name ) : QWidget( parent, name ) { ang = 45; f = 0; shooting = FALSE; timerCount = 0; shoot_ang = 0; shoot_f = 0; }
Inicializujeme naše nové privátne premenné.
void CannonField::shoot() { if ( shooting ) return; timerCount = 0; shoot_ang = ang; shoot_f = f; shooting = TRUE; startTimer( 50 ); }
Táto funkcia vystrelí strelu, pokiaľ už jedna nie je vo vzduchu.
Premenná timerCount
je nastavená na nulu. Premenné
shoot_ang
a shoot_f
sú nastavené na
aktuálnu hodnotu uhla a sily kanóna. Premenná shooting
je nastavená na TRUE aby indikovala, že strela je vo vzduchu.
Nakoniec spustíme časovač.
Volanie startTimer() spôsobí, že Qt nám bude posielať udalosti časovača v pravidelných intervaloch. Tentokrát chceme udalosť každých 50 milisekúnd.
void CannonField::timerEvent( QTimerEvent * ) { erase( shotRect() ); timerCount++; QRect shotR = shotRect(); if ( shotR.x() > width() || shotR.y() > height() ) { stopShooting(); return; } repaint( shotR, FALSE ); }
Funkcia timerEvent() je obsluha udalosti, ktorá dostáva udalosti od Qt. V našom príklade je volaná každých 50 milisekúnd, pokiaľ je strela vo vzduchu.
Najprv vymažeme starú strelu pomocou funkcie QWidget::erase().
Všimnite si, že to nie je potrebné pri prvom volaní timerEvent() po výstrele (ale neuškodí to).
Funkcia shotRect()
(popísaná nižšie) vracia obdĺžnik obklopujúci strelu na
aktuálnej pozícii.
Potom inkrementujeme timerCount,
čo má za dôsledok pohyb
strely na ďalšiu pozíciu pozdĺž jej trajektórie.
Ďalej získame nové súradnice obdĺžnika okolo strely.
Ak sa strela posunula za pravý alebo pod dolný okraj widgetu, zastavíme streľbu a ukončíme obsluhu.
Ak nie, nakreslíme novú strelu prekreslením tej časti widgetu, ktorá obsahuje strelu. Argument FALSE indikuje, že určený obdĺžnik nemá byť pred odoslaním udalosti paint event widgetu vymazaný.
void CannonField::paintEvent( QPaintEvent *e ) { QRect updateR = e->rect(); QPainter p; p.begin( this ); if ( updateR.intersects( cannonRect() ) ) paintCannon( &p ); if ( shooting && updateR.intersects( shotRect() ) ) paintShot( &p ); p.end(); }
Táto funkcia obsluhy udalosti kreslenia je v princípe rovnaká ako v predchádzajúcej kapitole. Jediný rozdiel je, že nakreslíme aj strelu, ak je to potrebné.
void CannonField::stopShooting() { shooting = FALSE; killTimers(); }
Táto privátna funkcia zastaví strelu vo vzduchu. Najprv nastaví premennú
shooting
, potom zruší všetky udalosti časovača
tohto widgetu. Je tiež možné zrušiť jediný časovač.
void CannonField::paintShot( QPainter *p ) { p->setBrush( black ); p->setPen( NoPen ); p->drawRect( shotRect() ); }
Táto privátna funkcia kreslí strelu ako čierny vyplnený obdĺžnik.
QRect CannonField::shotRect() const { const double gravity = 4; double time = timerCount / 4.0; double velocity = shoot_f; double radians = shoot_ang*3.14159265/180; double velx = velocity*cos( radians ); double vely = velocity*sin( radians ); double x0 = ( barrel_rect.right() + 5 )*cos(radians); double y0 = ( barrel_rect.right() + 5 )*sin(radians); double x = x0 + velx*time; double y = y0 + vely*time - 0.5*gravity*time*time; QRect r = QRect( 0, 0, 6, 6 ); r.moveCenter( QPoint( qRound(x), height() - 1 - qRound(y) ) ); return r; }
Táto privátna funkcia počíta stredný bod strely a vracia obklopujúci obdĺžnik.
Používa počiatočné hodnoty sily a uhla (hodnoty pri výstrele) a premennú
timerCount,
ktorá rasti spolu s časom.
Použitý vzorec je klasický Newtonov vzorec pre pohyb bez odporu v gravitačnom poli. Pre jednoduchosť sme sa rozhodli nebrať ohľad na žiadne relativistické efekty.
Vypočítame centrum strely v súradnicovom systéme, kde súradnica y rastie nahor. Po jeho vypočítaní skonštruujeme QRect s rozmermi 6x6 a posunieme jeho stred na pozíciu vypočítanú vyššie. V rovnakej operácii konvertujeme bod do súradnicového systému nášho widgetu (viď suradnicový systém).
Funkcia qRound() je vložená funkcia definovaná v qglobal.h (vkladanej vo všetkých ostatných Qt hlavičkových súboroch). qRound zaokrúhli reálne číslo s dvojitou presnosťou (double) na najbližšie celé číslo.
class MyWidget : public QWidget { public: MyWidget( QWidget *parent=0, const char *name=0 ); protected: void resizeEvent( QResizeEvent * ); private: QPushButton *quit; QPushButton *shoot; LCDRange *angle; LCDRange *force; CannonField *cannonField; };
Pridáme iba tlačidlo výstrelu.
shoot = new QPushButton( "Shoot", this, "shoot" ); shoot->setGeometry( 90, 10, 75, 30 ); shoot->setFont( QFont( "Times", 18, QFont::Bold ) );
V konštruktore vytvoríme a nastavíme tlačidlo výstrelu rovnako ako sme to urobili s tlačidlom quit. Všimnite si, že prvý argument konštruktora je text tlačidla a tretí je meno widgetu.
connect( shoot, SIGNAL(clicked()), cannonField, SLOT(shoot()) );
Spája signál tlačidla výstrelu clicked() so slotom shoot() widgetu CannonField.
Kanón strieľa, ale zatiaľ nemá žiadny terč.
Upravte strelu tak, aby bola plný kruh (pomôcka: QPainter::drawEllipse() ).
Zmente farbu kanóna počas letu strely.
Teraz môžete ísť na kapitolu dvanásť.
[Predchádzajúci tutoriál] [Ďalší tutoriál] [Hlavná stránka tutoriálu]
Copyright © 1998 Troll Tech | Trademarks | Qt version 1.42
|