Stránka sa načítava, prosím čakajte…
© 2005 – 2024 Roman Horváth, všetky práva vyhradené. Dnes je 20. 4. 2024.
Dátum: 26. 10. 2013, pred viacerými rokmi, aktualizované: 1. 8. 2020, pred štyrmi rokmi
Na tejto stránke je publikovaná implementácia jednoduchej hry Asteroidy. Je implementovaná s využitím programovacieho rámca grafického robota.
Ukážka vzhľadu hry po spustení.
- Zdrojové a zvukové súbory projektu na prevzatie:
asteroidy.7z
194,76 kB (190,19 KiB), 1. 8. 2020
~
import knižnica.GRobot; import knižnica.Svet; import knižnica.Zoznam; import knižnica.Zvuk; /** * Hlavná trieda, ktorá slúži na inicializáciu aplikácie a koordináciu * činnosti inštancií ostatných tried. */ public class Asteroidy extends GRobot { // Faktor zmenšenia polomeru pri rozpade asteroidov, ktorý je vypočítaný // tak, aby obsah kružnice vypočítanej z veľkosti (polomeru) asteroidu // po rozpade bol polovičný ako pred rozpadom: S₂ = S₁ / 2, z čoho sa dá // odvodiť, že r₂² = r₁² / 2, z čoho vyplýva, že faktor zmenšenia // polomeru je rovný √¹⁄₂: private final static double faktor = 0.70710678118654752440084436210485; // Inštancia rakety: private static Raketa raketa; // Inštancie asteroidov: private final static Zoznam<Asteroid> asteroidy = new Zoznam<Asteroid>(); // Inštancie striel: private final static Zoznam<Strela> strely = new Zoznam<Strela>(); /** * Konštruktor hlavnej triedy. Inicializuje aplikáciu, vytvára nevyhnutné * inštancie, kreslí pozadie, konfiguruje rôzne vlastnosti a aktivuje hru. */ private Asteroidy() { // Nastavenia súvisiace s oknom aplikácie: Svet.zbaľ(); Svet.farbaPozadia(čierna); // Vypnutie automatického prekresľovania (prekreslenie je zabezpečené // v reakcii tik): Svet.nekresli(); // Nastavenie cesty k priečinku, ktorý obsahuje zvuky: Zvuk.priečinokZvukov("zvuky"); // Skoré čítanie zvukov do vnútornej pamäte sveta, aby nevznikla // pauza pred prvým pokusom o prehratie zvuku (anglicky sa tento // proces nazýva preloading): Svet.čítajZvuky("boom.wav", "explode.wav", "shot.wav"); // Skrytie hlavného robota: skry(); // Vytvorenie inštancie rakety: raketa = new Raketa(); // Nakreslenie niekoľkých dekoračných hviezd na pozadí (s použitím // hlavného robota): farba(svetložltá); veľkosť(5); for (int i = 0; i < 50; ++i) { náhodnáPoloha(); náhodnýSmer(); vyplňHviezdu(); } podlaha.rozmaž(10); // Vytvorenie úvodných inštancií asteroidov (mieriacich na raketu): for (int i = 0; i < 5; ++i) asteroidy.pridaj(new Asteroid(30, raketa)); // Aktivácia rakety (aj s časovačom): raketa.aktivuj(); } // Metóda slúžiaca na rozbíjanie asteroidov na menšie časti. Metóda // prijíma v parametri inštanciu rozbíjaného asteroidu. Najprv zistí, // či má tento dostatočnú veľkosť na to, aby ešte mohol byť rozdelený // na menšie časti. Ak áno, zmenší jeho veľkosť o faktor vypočítaný tak, // aby plocha kruhu veľkosti nového asteroidu bola polovičná oproti // starej (metódou upravVeľkosť, pretože zmena veľkosti znamená zmenu // grafiky asteroidu a úpravu niektorých ďalších vlastností), nájde // (alebo vytvorí) najbližší neaktívny asteroid a vyrobí z neho dvojičku // tohto asteroidu, čím je efekt rozbitia úplný. Ak asteroid nemá // dostatočnú veľkosť na rozbitie, tak je deaktivovaný. private void rozbi(Asteroid asteroid) { if (asteroid.veľkosť() >= 5.5) { asteroid.upravVeľkosť(asteroid.veľkosť() * faktor); // Hľadanie jestvujúceho neaktívneho asteroidu: for (Asteroid dvojička : asteroidy) if (dvojička.neaktívny()) { dvojička.aktivujDvojičku(asteroid); return; } // Vytvorenie nového asteroidu: Asteroid dvojička = new Asteroid(); asteroidy.pridaj(dvojička); dvojička.aktivujDvojičku(asteroid); } else asteroid.deaktivuj(); } /** * Reakcia na tik časovača. Tu sa kontrolujú kolízie asteroidov, striel * a rakety. Tiež sa tu podľa potreby vykonáva prekresľovanie grafiky * sveta. */ @Override public void tik() { // Najskôr sa skontrolujú kolízie všetkých aktívnych striel // s aktívnymi asteroidmi. Keď je zistená kolízia, tak je // deaktivovaná strela a zavolaná metóda rozbi (definovaná // vyššie) s dotknutým asteroidom v argumente. Potom je prehraný // zvuk signalizujúci rozbitie asteroidu. for (Strela strela : strely) if (strela.aktívny()) for (Asteroid asteroid : asteroidy) if (asteroid.aktívny() && strela.koliduje(asteroid)) { strela.deaktivuj(); rozbi(asteroid); Svet.zvuk("boom.wav"); break; } // V prípade, že raketa nemá dištanc, sú skontrolované jej prípadné // kolízie s asteroidmi. Keď detegovaná kolízia, raketa havaruje // (čo je realizované volaním metódy rakety havaruj, ktorej // „rozsudok“ je v tomto projekte mierny – iba raketu presunie // do stredu plátna a nastaví jej nový dištanc). Zároveň sa asteroid // dôsledkom nárazu rozbije (rovnako ako keby ho trafila strela). if (raketa.nemáDištanc()) for (Asteroid asteroid : asteroidy) { if (asteroid.aktívny() && raketa.koliduje(asteroid)) { raketa.havaruj(); rozbi(asteroid); Svet.zvuk("explode.wav"); break; } } // Prekreslenie sveta v prípade, že boli detegované nejaké zmeny: if (Svet.neboloPrekreslené()) Svet.prekresli(); } /** * Statická metóda, ktorá slúži na poskytovanie inštancií neaktívnych * striel na prácu s nimi. Potrebuje ju najmä raketa, ktorá si vyžiada * inštanciu strely pri každom výstrele. Metóda najskôr hľadá neaktívnu * strelu v zozname striel a ak takú nenájde, vyrobí novú. */ public static Strela dajStrelu() { // Hľadanie neaktívnych striel v zozname striel: for (Strela strela : strely) if (strela.neaktívny()) return strela; // Výroba novej strely (ak nebola nájdená vyhovujúca strela): Strela strela = new Strela(); strely.pridaj(strela); return strela; } /** * Hlavná metóda slúžiaca na spúšťanie aplikácie. * * @param args parametre prijaté z konzoly, ktorá aplikáciu spúšťala * (v tomto projekte nie sú využité) */ public static void main(String[] args) { Svet.použiKonfiguráciu("Asteroidy.cfg"); new Asteroidy(); } }
~
import knižnica.Bod; import knižnica.GRobot; import knižnica.Oblasť; import knižnica.Svet; /** * Trieda asteroidov. Paradoxne, to jest napriek tomu, že sa zdá, že * asteroid „je len kamienok pasívne plávajúci priestorom,“ je táto trieda * najkomplikovanejšou v tomto projekte. * * Hlavným dôvodom je, že správanie sa asteroidu sa najviac odchyľuje od * predprogramovaného správania sa robotov: asteroidy náhodne menia rýchlosť, * musia byť generované na náhodnej pozícii pri okraji obrazovky, majú * nepravidelný tvar (pripomínajúci neobrúsený kameň), ktorý si samy generujú * (prípadne ďalšie odchýlky). */ public class Asteroid extends GRobot { // Tento atribút (konštanta) určuje počet bodov, z ktorých bude pozostávať // nepravidelný tvar asteroidu: private final static int POČET_BODOV = 20; // V tomto atribúte (konštante) sa uchová uhol medzi dvomi bodmi tvaru // asteroidu. Musí byť rovný podielu 360 / počet bodov tvaru asteroidu: private final static double UHOL = 360.0 / (double)POČET_BODOV; // Oblasť uchovávajúca vlastný tvar tohto asteroidu: private Oblasť tvar; /*# * Nasledujú definície dvoch druhov konštruktorov. Každý je určený na * použitie v inej situácii. Prvý konštruktor (bez parametrov) je tzv. * predvolený konštruktor. Prvým príkazom druhého konštruktora je volanie * predvoleného konštruktora: this(), čiže všetko, čo zabezpečuje * predvolený konštruktor sa automaticky prejaví aj v druhom konštruktore. * * (Poznámka: Aby asteroidy nekreslili pri presune čiaru, je do * predvoleného konštruktora zaradený príkaz na zdvihnutie pera. * To isté platí pre strely aj raketu.) */ /** * Tento (predvolený) konštruktor vytvorí pasívny asteroid. Týmto * spôsobom sú asteroidy vytvárané iba pri procese ich rozbíjania na * menšie kúsky. To je dôvod, prečo tento konštruktor automaticky * negeneruje tvar asteroidu. Jeho tvar je vygenerovaný až po aktivácii * (kvázi „spárovaní sa“) ako dvojičky iného asteroidu. (Pri tom sa * určuje mení jeho veľkosť a tvar by musel byť generovaný znova, takže * sa tým šetrí výpočtový výkon.) */ public Asteroid() { farba(hnedá); hrúbkaČiary(1.75); zdvihniPero(); náhodnáRýchlosť(); skry(); } /** * Tento konštruktor vytvorí asteroid so zadanou veľkosťou nasmerovaný na * (zadanú) raketu. */ public Asteroid(double veľkosť, Raketa raketa) { this(); upravVeľkosť(veľkosť); náhodnáPolohaNaOkraji(); otočNa(raketa); } /** * Táto metóda slúži na aktivovanie dvojičky k zadanému asteroidu (kvázi * „spárovanie sa“). Dvojička vzniká vždy pri rozbíjaní asteroidu. Pri * procese aktivácie dvojičky (úmyselne sa vyhýbame slovu „spárovanie,“ * aby nevznikal dojem, že dvojičky zostávajú nejakým spôsobom naďalej * prepojené) sa vlastnosti tejto inštancie asteroidu prispôsobia * vlastnostiam zadanej inštancie asteroidu. * * Najskôr sa upraví veľkosť tohto asteroidu podľa zadaného asteroidu * (volaním metódy upravVeľkosť, ktorá zároveň generuje náhodný tvar * asteroidu), potom je tento asteroid presunutý na pozíciu zadaného * asteroidu, nasmeruje sa náhodným smerom a nakoniec je aktivovaný. * * (Táto metóda by mala byť volaná len pre neaktívne asteroidy. V tomto * projekte je toto pravidlo dodržané, preto metóda neobsahuje žiadnu * kontrolu splnenia tejto podmienky.) */ public void aktivujDvojičku(Asteroid asteroid) { upravVeľkosť(asteroid.veľkosť()); poloha(asteroid); náhodnýSmer(); náhodnáRýchlosť(); aktivuj(); } /** * Generuje náhodnú polohu asteroidu na okraji plátna. Vychádzame * z predpokladu, že na to, aby bola hra dobre hrateľná, by sa prvé * asteroidy mali vygenerovať na okraji plátna, to jest v dostatočnej * vzdialenosti od rakety, ktorá sa predvolene nachádza v jeho strede. * * Toto sa dá dosiahnuť relatívne jednoducho: * * • asteroid presunieme do stredu plátna, * • nasmerujeme ho náhodným smerom * • a posunieme ho dopredu o náhodnú avšak pomerne veľkú vzdialenosť * (v tomto prípade v rozmedzí od 300 do 310 bodov). */ public void náhodnáPolohaNaOkraji() { poloha(stred); náhodnýSmer(); dopredu(Svet.náhodnéCeléČíslo(300, 310)); aktivuj(); } /** * Nastaví asteroidu náhodnú (konštantnú) rýchlosť. */ public void náhodnáRýchlosť() { rýchlosť(Svet.náhodnéReálneČíslo(2.0, 3.5), false); } /** * Spolu s úpravou veľkosti asteroidu sa náhodne mení aj generovaný * nepravidelný tvar asteroidu. * Na vygenerovanie nového tvaru sú tvorivým spôsobom využité metódy * robota: * * • Najskôr sa uložia súradnice série bodov umiestnených relatívne * pravidelne (v súvislosti s uhlovou odchýlkou) po obvode kružnice * s polomerom veľkosti asteroidu. * • Pritom každý bod je náhodne posunutý od polomeru, takže výsledný * tvar vytvorený pospájaním bodov bude pripomínať neobrúsenú skalu. * • Nakoniec je séria bodov prevedená prostredníctvom cesty na oblasť. * * Oblasť je potom použitá v reakcii na kreslenie (nižšie). * * (Predchádzajúce vysvetlenie generovania nového tvaru by bolo * názornejšie viditeľné na obrázku. Predstavte si niekoľko bodov * (napríklad päť) tvoriacich pravidelný n-uholník (napríklad * päťuholník). Teraz si predstavte, že sa každý z bodov náhodne * posunie smerom k stredu alebo od stredu n-uholníka. Výsledný tvar * bude mierne deformovaný. Ak je počet uhlov n-uholníka vyšší, * napríklad 10 alebo 20, tak bude výsledný útvar podobný balvanu.) */ public void upravVeľkosť(double nováVeľkosť) { // Úprava veľkosti a rýchlosti otáčania tvaru asteroidu: veľkosť(nováVeľkosť); mierka(1); otáčajTvar(Svet.náhodnéReálneČíslo(-1.0, 1.0)); // Záloha pôvodnej polohy asteroidu (na obnovenie po vygenerovaní // nového tvaru): Bod poloha = poloha(); // Definícia nového ohraničenia asteroidu (podľa jeho rozmerov): poloha(stred); ohranič(Svet.najväčšieX() + nováVeľkosť, Svet.najväčšieY() + nováVeľkosť); // Pole bodov, z ktorých bude vytvorený tvar asteroidu: Bod body[] = new Bod[POČET_BODOV]; // Generovanie série bodov for (int i = 0; i < POČET_BODOV; ++i) { // Krok 1: Presunutie robota (resp. asteroidu; tento robot = // asteroid) do stredu plátna a nastavenie prislúchajúceho uhla // (násobku konštanty UHOL, riadiacej premennej i a náhodného // koeficientu): skočNa(stred); uhol(i * UHOL * Svet.náhodnéReálneČíslo(0.95, 1.05)); // Krok 2: Posun dopredu o veľkosť asteroidu jemne upravenú // náhodným koeficientom v rozmedzí ⟨0,75; 1,25⟩: dopredu(nováVeľkosť * Svet.náhodnéReálneČíslo(0.75, 1.25)); // Uloženie polohy získanej prostredníctvom predchádzajúcich // dvoch krokov: body[i] = poloha(); } // Vytvorenie cesty: poloha(body[0]); začniCestu(); for (int i = 1; i < POČET_BODOV; ++i) poloha(body[i]); // Prevedenie cesty na oblasť: tvar = new Oblasť(cesta()); // Skok (návrat) na pôvodné súradnice robota: poloha(poloha); } /** * Reakcia na kreslenie obkreslí oblasť, v ktorej je uložený tvar tohto * asteroidu (ak tvar jestvuje). */ @Override public void kresliTvar() { if (null != tvar) obkresliOblasť(tvar); } /** * Pri aktivácii sa asteroid automaticky zobrazí. */ @Override public void aktivácia() { zobraz(); } /** * Pri deaktivácii sa asteroid automaticky skryje. */ @Override public void deaktivácia() { skry(); } }
~
import knižnica.GRobot; import knižnica.Oblasť; import knižnica.Svet; import knižnica.Zoznam; import static knižnica.ÚdajeUdalostí.*; import static knižnica.Kláves.*; /** * Trieda pre raketu. Definuje správanie rakety, ktorá je ústredným prvkom * hry. * * Väčšinu jej funkčnosti zabezpečuje trieda GRobot, vďaka čomu je jej * implementácia relatívne jednoduchá. * * Relatívne dôležitou vlastnosťou, ktorá ju odlišuje od robota je tzv. * „dištanc“ (pozri definíciu atribútu nižšie). */ public class Raketa extends GRobot { // Súkromný atribút určujúci dištanc rakety. Je to v podstate časový // interval, počas ktorého raketa nedokáže strieľať a nekoliduje // s asteroidmi. // // (Technické zabezpečenie, čiže použitie tohto atribútu, je realizované // v metódach Raketa.vystreľ a Asteroidy.tik.) private int dištanc = 100; /** * Predvolený konštruktor rakety. Nastaví potrebné vlastnosti rakety * (ohraničenie, farbu, veľkosť a pod.). */ public Raketa() { ohranič(); farba(svetlotyrkysová); veľkosť(20.0); zdvihniPero(); hrúbkaČiary(1.75); } /** * Havária rakety je v tomto projekte šetrná. Raketa je premiestnená do * stredu plátna a dostane stobodový dištanc. */ public void havaruj() { skočNa(stred); dištanc = 100; } /** * Táto metóda overí, či nemá raketa dištanc. */ public boolean nemáDištanc() { return dištanc <= 0; } /** * Reakcia na aktivitu, v ktorej je zabezpečené blikanie rakety počas * trvania dištancu. */ @Override public void aktivita() { if (dištanc > 0) { // Počas dištancu raketa pravidelne bliká: if (--dištanc % 10 < 5) zobraz(); else skry(); } } // Metóda vystreľ najprv skontroluje, či nemá raketa dištanc. Ak nemá, tak // si vyžiada neaktívnu inštanciu strely a za sprievodu zvuku s nastavením // rovnakej polohy a uhla ako má raketa ju aktivuje. (Skrátene povedané: // raketa vystrelí.) private void vystreľ() { if (nemáDištanc()) { Strela strela = Asteroidy.dajStrelu(); strela.poloha(this); strela.uhol(this); strela.aktivuj(); Svet.zvuk("shot.wav"); } } /** * Reakciou na stlačenie kurzorových klávesov je otáčanie a posun rakety. */ @Override public void stlačenieKlávesu() { switch (kláves()) { case HORE: rýchlosť(10.0); break; case DOLE: rýchlosť(-10.0); break; case VPRAVO: uhlováRýchlosť(-15.0); break; case VĽAVO: uhlováRýchlosť(15.0); break; } } /** * Reakciou na uvoľnenie kurzorových klávesov je zastavenie otáčania * alebo posunu rakety. Uvoľnenie medzerníka znamená vystrelenie. */ @Override public void uvoľnenieKlávesu() { switch (kláves()) { case HORE: case DOLE: rýchlosť(0.0); break; case VPRAVO: case VĽAVO: uhlováRýchlosť(0.0); break; case MEDZERA: vystreľ(); break; } } }
~
import knižnica.GRobot; import knižnica.KreslenieTvaru; /** * Trieda pre strely. Definuje správanie striel, ktoré strieľa raketa. * Základná funkčnosť strely je definovaná pomerne jednoducho: * * • strela je predvolene deaktivovaná, * • po aktivácii sa pohybuje rovnomerne priamočiaro, kým neopustí * obrazovku, kedy sa automaticky deaktivuje, * • aktivácia strely (pri výstrele) je realizovaná v metóde Raketa.vystreľ * • a detekcia kolízií s asteroidmi v hlavnej triede. */ public class Strela extends GRobot { // Vlastný tvar strely je jednoduchý: rovná krátka čiarka. Objekt // kreslenia (inštancia triedy KreslenieTvaru) vytvorený prostredníctvom // lambda výrazu je uložený do statického atribútu, aby bol použiteľný // pre všetky strely súčasne. private static KreslenieTvaru tvar = r -> r.dopredu(10); /** * Predvolený konštruktor strely nastavujúci základné vlastnosti strely * ako je tvar (vzhľad), rýchlosť atď. */ public Strela() { ohranič(); farba(tmavotyrkysová); zdvihniPero(); hrúbkaČiary(2.5); vlastnýTvar(tvar); rýchlosť(20.0, false); skry(); } /** * Pri aktivácii sa strela automaticky zobrazí. */ @Override public void aktivácia() { zobraz(); } /** * Pri deaktivácii sa strela automaticky skryje. */ @Override public void deaktivácia() { skry(); } /** * Strela sa po opustení plátna automaticky deaktivuje. */ @Override public void mimoHraníc() { deaktivuj(); } }
Poznámka: Tento projekt je starší. Bude potrebné ho aktualizovať tak, aby bol kompatibilný s najnovšou verziou (už sa stalo) programovacieho rámca GRobot.
Na tejto stránke je publikovaná zjednodušená verzia hry Asteroidy naprogramovaná s využitím skupiny tried grafického robota. Na vygenerovanie prázdneho projektu odporúčam použiť Generátor projektov BlueJ (dostupný v sekcii Softvér na prevzatie).
Triedy projektu.
~
/** * Trieda pre strely. Definuje správanie striel, ktoré strieľa raketa. Celá * základná funkcionalita strely je definovaná pomerne jednoducho: strela je * predvolene deaktivovaná; po aktivácii sa pohybuje rovnomerne priamočiaro, * kým neopustí obrazovku, kedy sa automaticky deaktivuje; ostatné * záležitosti, ktoré súvisia s činnosťou strely (t. j. aktivácia = výstrel * a detekcia kolízií s asteroidmi) sú definované v hlavnej triede. */ public class Strela extends GRobot { // Vlastný tvar strely je jednoduchý: rovná krátka čiarka. Objekt // vzniknutý inštancionalizáciou anonymnej implementácie rozhrania // „VlastnýTvar“ je uložený do statickej premennej, aby bol použiteľný // pre všetky strely súčasne. private static VlastnýTvar tvar = new VlastnýTvar() { @Override public void kresli(GRobot r) { r.dopredu(10); } }; /** * Predvolený konštruktor strely nastaví základné vlastnosti strely ako * tvar (vzhľad), rýchlosť, atď. */ public Strela() { farba(tmavotyrkysová); zdvihniPero(); hrúbkaČiary(2.5); vlastnýTvar(tvar); rýchlosť(20.0, false); skry(); } /** * Pri aktivácii sa strela automaticky zobrazí. */ @Override public void aktivácia() { zobraz(); } /** * Pri deaktivácii sa strela automaticky skryje. */ @Override public void deaktivácia() { skry(); } /** * Strela sa automaticky deaktivuje po opustení plátna. */ @Override public void aktivita() { if (polohaX() > svet.najväčšieX() || polohaX() < svet.najmenšieX() || polohaY() > svet.najväčšieY() || polohaY() < svet.najmenšieY()) { deaktivuj(); } } }
~
/** * Trieda pre raketu. Definuje správanie rakety, ktorá je ústredným prvkom * hry. Jej základná fukncionalita je definovaná pomerne jednoducho. Väčšinu * funkčnosti zabezpečuje trieda GRobot, vďaka čomu môže byť implementácia * zjednodušená. Dôležité je vedieť najmä to, či má raketa tzv. „dištanc“ * (vysvetlené nižšie) a zabezpečiť to, aby nemohla opustiť obrazovku (vždy * po prekročení okraja sa objaví na protiľahlom kraji). Zvyšná funkcionalita * rakety (ovládanie, kolízie, streľba) je definovaná v hlavnej triede. */ public class Raketa extends GRobot { // Súkromná premenná určujúca dištanc rakety. Počas dištancu raketa // nedokáže strieľať a nekoliduje s asteroidmi. To je zabezpečené na // iných miestach projektu, konkrétne v metódach hlavnej triedy „vystreľ“ // a „aktivita“. private int dištanc = 0; /** * Predvolený konštruktor rakety. Nastaví základné vlastnosti rakety * (vzhľad a pod.) */ public Raketa() { farba(svetlotyrkysová); veľkosť(20); zdvihniPero(); hrúbkaČiary(2.5); // Nastaví „dutý“ tvar rakety (bude // kreslená predvoleným tvarom robota): predvolenýTvar(false); } /** * Havária rakety je v tomto projekte šetrná. Raketa je premiestnená do * stredu plátna a dostane stobodový dištanc. */ public void havaruj() { skočNa(0, 0); dištanc = 100; } /** * Táto metóda slúži na zistenie toho, či má raketa práve dištanc. * (Je použitá v hlavnej triede.) */ public boolean máDištanc() { return dištanc > 0; } /** * Prekrytá metóda aktivita kontroluje niektoré parametre rakety… */ @Override public void aktivita() { if (dištanc > 0) { // Počas dištancu raketa pravidelne bliká if (--dištanc % 10 < 5) zobraz(); else skry(); } // Nasledujúce správanie platí bez ohľadu na dištanc – raketa // sa po prekročení okraja plátna objaví na protiľahlej strane: if (polohaX() > svet.najväčšieX()) polohaX(svet.najmenšieX()); if (polohaX() < svet.najmenšieX()) polohaX(svet.najväčšieX()); if (polohaY() > svet.najväčšieY()) polohaY(svet.najmenšieY()); if (polohaY() < svet.najmenšieY()) polohaY(svet.najväčšieY()); } }
~
/** * Trieda pre asteroidy. Paradoxne je táto trieda najkomplikovanejšou triedou * tohto projektu, pretože správanie sa a iné vlastnosti asteroidov sú * relatívne komplikované. Musia mať nepravidelný tvar pripomínajúci * neobrúsený kameň. Musia rovnomerne rotovať a pritom sa pohybovať * priamočiarou konštantnou rýchlosťou. Definície v triede robot, ktoré * slúžia na zabezpečenie automatickej zmeny orientácie robota podľa uhlovej * rýchlosti a polohy robota podľa priamočiarej rýchlosti by sa dostali do * konfliktu, keby sme ich chceli využiť obidve naraz. Preto si treba vybrať, * ktorý vstavaný mechanizmus bude použitý a ktorý bude zabezpečený ináč. * Predovšetkým tieto dve požiadavky (tvar a spôsob pohybu) mali za následok * vznik o niečo vyššieho počtu čiastkových problémov, v porovnaní * s ostatnými triedami projektu. Riešenia sú poskytnuté nižšie… */ public class Asteroid extends GRobot { // Súkromené premenné: // – oblasť uchovávajúca vlastný tvar tohto asteroidu, private Oblasť tvar; // – hodnoty smeru a rýchlosti asteroidu, ktoré musíme definovať týmto // spôsobom, pretože sme sa rozhodli používať automatickú rotáciu // asteroidu. private double smer = Smer.SEVER; private double rýchlosť = 0.0; /*# * Nasledujú definície dvoch druhov konštruktorov. Každý je určený na * použitie v inej situácii. Pred definíciami konštruktorov je vsunutá * definícia súkromnej metódy „reset“, ktorá zoskupuje nastavovanie * spoločných vlastností asteroidov… */ private void reset() { farba(hnedá); hrúbkaČiary(2); zdvihniPero(); skry(); } /** * Tento konštruktor vytvorí asteroid so zadanou veľkosťou nasmerovaný na * raketu. */ public Asteroid(double veľkosť, Raketa r) { reset(); upravVeľkosť(veľkosť); náhodnáPolohaNaOkraji(); nasmerujNa(r); } /** * Tento (predvolený) konštruktor vytvorí pasívny asteroid určený „do * rezervy“. Asteroidy vytvorené týmto spôsobom budú využité pri procese * rozbíjania aktívnych asteroidov na menšie kúsky. */ public Asteroid() { reset(); } /** * Táto metóda slúži na aktivovanie dvojičky k zadanému asteroidu, ktorá * vzniká pri rozbíjaní. Dvojičkou je táto inštancia, ktorá sa prispôsobí * vlastnostiam zadanej inštancie „a“. Najskôr sa upravuje veľkosť tohto * asteroidu podľa zadaného asteroidu „a“ (volaním metódy „upravVeľkosť“, * ktorá zároveň generuje náhodný tvar asteroidu), potom je tento * asteroid presunutý na rovnakú polohu ako má zadaný asteroid „a“, * nasmeruje sa náhodným smerom a nakoniec je aktivovaný. Táto metóda by * mala byť volaná len neaktívne asteroidy. V tomto projekte je pravidlo * dodržané, preto metóda neobsahuje žiadnu kontrolu splnenia tejto * podmienky. */ public void aktivujDvojičku(Asteroid a) { upravVeľkosť(a.veľkosť()); skočNa(a); nasmerujNáhodne(); aktivuj(); } /** * Spolu s úpravou veľkosti asteroidu sa náhodne mení aj vygenerovaný * nepravidelný tvar asteroidu. Na vygenerovanie tvaru sú tvorivým * spôsobom využité metódy robota. Najskôr sa uložia súradnice série * desiatich bodov umiestnených pravidelne (aspoň v súvislosti s uhlovou * odchýlkou) po obvode kružnice, pričom každý bod je náhodne odchýlený * od originálneho polomeru, ktorý je rovný veľkosti robota. * Predchádzajúce vysvetlenie by bolo názornejšie viditeľné na obrázku. * Predstavte si sériu bodov, ktorých spojením by sme získali pravidelný * desaťuholník. Potom každý z bodov náhodne posuňte smerom k stredu * alebo od stredu desaťuholníka. Spojením posunutých bodov nevznikne * pravidelný desaťuholník, ale útvar podobný balvanu. Nakoniec je séria * bodov prevedená prostredníctvom cesty na oblasť. Oblasť je potom * použitá v prekrytej metóde „kresli“ (nižšie) na nakreslenie balvanu. */ public void upravVeľkosť(double veľkosť) { double x[] = new double[10]; double y[] = new double[10]; // Záloha pôvodnej polohy robota java.awt.geom.Point2D poloha = poloha(); // Generovanie série bodov for (int i = 0; i < 10; ++i) { domov(); doprava(i * 36); dopredu(veľkosť * svet.náhodnéReálneČíslo(1, 2)); x[i] = polohaX(); y[i] = polohaY(); } // Vytvorenie cesty skočNa(x[0], y[0]); začniCestu(); for (int i = 1; i < 10; ++i) skočNa(x[i], y[i]); // Prevedenie cesty na oblasť tvar = new Oblasť(cesta()); // Skok na pôvodné súradnice robota poloha(poloha); // Úprava veľkosti a uhlovej rýchlosti robota veľkosť(veľkosť); uhlováRýchlosť(svet. náhodnéCeléČíslo (-8, 8), false); } /** * Náhodná poloha asteroidu by sa mala nachádzať v dostatočnej * vzdialenosti od rakety, čiže by mala byť na okraji ďalej od stredu * plátna. Toto sa dá dosiahnuť jednoducho: asteroid sa presunie do * stredu plátna, nasmeruje sa náhodným smerom a posunie sa dopredu * o náhodnú vzdialenosť v rozmedzí 200 až 210 bodov. Aby asteroidy * nekreslili pri presune čiaru, je do konštruktora zaradený príkaz na * zdvihnutie pera. */ public void náhodnáPolohaNaOkraji() { skočNa(0, 0); náhodnýSmer(); dopredu(svet.náhodnéCeléČíslo(200, 210)); } /** * Táto metóda nasmeruje asteroid smerom na zadaného robota (ktorým * je v tomto projekte raketa). */ public void nasmerujNa(GRobot r) { smer = smerNa(r) + svet.náhodnéReálneČíslo(-15, 15); rýchlosť = svet.náhodnéReálneČíslo(2, 3.5); } /** * Táto metóda nasmeruje asteroid náhodným smerom (to je užitočné pri * rozbíjaní asteroidov). */ public void nasmerujNáhodne() { smer = svet.náhodnéReálneČíslo(0.0, 360.0); rýchlosť = svet.náhodnéReálneČíslo(2, 3.5); } /** * S cieľom zjednodušenia tejto triedy bol zvolený menej efektívny, ale * jednoduchší spôsob kreslenia vlastného tvaru. */ @Override public void kresliTvar() { if (null != tvar) obkresliOblasť(tvar); } /** * Metóda aktivita implementuje rovnomerný priamočiary pohyb posunom * o hodnoty vlastného smeru a rýchlosti. Asteroid sa po prekročení * hranice plátna objaví na protiľahlej strane plátna. */ @Override public void aktivita() { posuňVSmere(smer, rýchlosť); if (polohaX() > svet.najväčšieX() + veľkosť()) polohaX(svet.najmenšieX() - veľkosť()); if (polohaX() < svet.najmenšieX() - veľkosť()) polohaX(svet.najväčšieX() + veľkosť()); if (polohaY() > svet.najväčšieY() + veľkosť()) polohaY(svet.najmenšieY() - veľkosť()); if (polohaY() < svet.najmenšieY() - veľkosť()) polohaY(svet.najväčšieY() + veľkosť()); } /** * Pri aktivácii sa asteroid automaticky zobrazí. */ @Override public void aktivácia() { zobraz(); } /** * Pri deaktivácii sa asteroid automaticky skryje. */ @Override public void deaktivácia() { skry(); } }
~
/** * Trieda {@code HlavnáTrieda} slúži na spúšťanie aplikácie. Koordinuje * činnosť ostatných inštancií tried. * * @author Roman Horváth * @version 1.0 */ public class HlavnáTrieda extends GRobot { // Inštancie hry: private Raketa raketa = new Raketa(); private Zoznam<Strela> strely = new Zoznam<Strela>(); private Zoznam<Asteroid> asteroidy = new Zoznam<Asteroid>(); /** * Predvolený konštruktor – v tomto projekte štartuje aplikáciu. * V konštruktore definujeme obsluhu udalostí na ovládanie robota s pomocou * klávesnice a vytvárame všetky inštancie potrebné na plynulé fungovanie * hry. */ private HlavnáTrieda() { // Nastavenia súvisiace s hlavným oknom: super(400, 300); svet.zbaľ(); svet.farbaPozadia(čierna); // Vypnutie automatického prekresľovania: svet.nekresli(); // Skrytie hlavného robota: skry(); // Definícia obsluhy udalostí: new ObsluhaUdalostí() { @Override public void stlačenieKlávesu() { switch (údajeUdalostí.kláves()) { case Kláves.HORE: raketa.rýchlosť(10.0); break; case Kláves.DOLE: raketa.rýchlosť(-10.0); break; case Kláves.VPRAVO: raketa.uhlováRýchlosť(-15.0); break; case Kláves.VĽAVO: raketa.uhlováRýchlosť(15.0); break; } } @Override public void uvoľnenieKlávesu() { switch (údajeUdalostí.kláves()) { case Kláves.HORE: case Kláves.DOLE: raketa.rýchlosť(0.0); break; case Kláves.VPRAVO: case Kláves.VĽAVO: raketa.uhlováRýchlosť(0.0); break; case Kláves.MEDZERA: vystreľ(); break; } } }; // Vytvorenie 10 inštancií striel: for (int i = 0; i < 10; ++i) strely.pridaj(new Strela()); // Vytvorenie 3 inštancií asteroidov mieriacich na raketu: for (int i = 0; i < 3; ++i) asteroidy.pridaj(new Asteroid(30, raketa)); // Vytvorenie 30 inštancií asteroidov „do rezervy“: for (int i = 0; i < 30; ++i) asteroidy.pridaj(new Asteroid()); // Aktivácia prvých troch asteroidov: for (int i = 0; i < 3; ++i) asteroidy.daj(i).aktivuj(); // Aktivácia rakety: raketa.aktivuj(); // Aktivácia hlavného robota a zapnutie automatického prekresľovania: aktivuj(); svet.kresli(); } // Metóda vystreľ najprv skontroluje, či má raketa dištanc, ak nie, // nájde neaktívnu inštanciu strely a aktivuje ju (za sprievodu zvuku // a s nastavením rovnakej polohy a uhla ako má raketa) – skrátene // povedané: raketa vystrelí… private void vystreľ() { if (raketa.máDištanc()) return; for (Strela strela : strely) { if (strela.neaktívny()) { svet.zvuk("shot.wav"); strela.skočNa(raketa); strela.otoč(raketa); strela.aktivuj(); break; } } } // Metóda prijíma do parametra „a“ inštanciu asteroidu. Zistí, či má // asteroid dostatočnú veľkosť. Ak áno, upraví jeho veľkosť na polovicu // (spolu s tým sú upravené aj niektoré ďalšie vlastnosti), nájde // najbližší neaktívny asteroid a vyrobí z neho dvojičku. Ak asteroid // nemá dostatočnú veľkosť, je deaktivovaný. private void rozbi(Asteroid a) { if (a.veľkosť() >= 5) { a.upravVeľkosť(a.veľkosť() / 2); for (Asteroid asteroid : asteroidy) { if (asteroid.neaktívny()) { asteroid.aktivujDvojičku(a); break; } } } else a.deaktivuj(); } /** * Prekrytá metóda aktivita kontroluje kolízie asteroidov, striel * a rakety. */ @Override public void aktivita() { // Najskôr sa skontrolujú kolízie všetkých aktívnych striel // s aktívnymi asteroidmi. Keď je zistená kolízia je prehraný zvuk, // deaktivovaná strela a zavolaná metóda „rozbi“ (definovaná vyššie) // s daným asteroidom v argumente. for (Strela strela : strely) { if (strela.aktívny()) { for (Asteroid asteroid : asteroidy) { if (asteroid.aktívny()) { if (strela.koliduje(asteroid)) { svet.zvuk("boom.wav"); strela.deaktivuj(); rozbi(asteroid); break; } } } } } // V prípade, že raketa nemá dištanc, sú skontrolované aj jej // prípadné kolízie s asteroidmi. Keď nastane kolízia, raketa // „havaruje“ (je zavolaná metóda rakety „havaruj“, ktorá je v tomto // projekte šetrná – iba raketu presunie do stredu a nastaví jej // určitý dištanc). Zároveň sa asteroid rozbije. if (!raketa.máDištanc()) { for (Asteroid asteroid : asteroidy) { if (asteroid.aktívny()) { if (raketa.koliduje(asteroid)) { svet.zvuk("explode.wav"); raketa.havaruj(); rozbi(asteroid); break; } } } } } /** * Metóda {@code main} slúži na spúšťanie aplikácie. * @param args parametre prečítané z príkazového riadka */ public static void main(String[] args) { new HlavnáTrieda(); } }