Red Alert: Reactor Overheat!

Reaktor, v ktorom nadmerne stúpne teplota, vie narobiť riadnu paseku.

Motivácia

Vitaj vo výcvikovom stredisku, kadet, kde z teba počas 12 týždňového tréningu spravíme ozajstného chlapa (rozumej objektového programátora)! O formu a podobu tvojho výcviku sa bude starať náš taktický a strategický tím, ktorý má s výcvikom kadetov tvojho formátu dlhoročné skúsenosti a rozhodne ťa nenechá vydýchnuť. Tvoj výcvik je totiž jeho prioritou.

Dnes to bude len zahrievacie kolo, v ktorom uvidíme, čo si zač, čo v tebe je a čo od teba teda môžeme očakávať. Aj keď sa jedná len o zahrievacie kolo, mnoho kadetov trhá tím a zostáva pri plnení dnešnej misie ešte niekoľko týždňov. Veríme, že ty partiu trhať nebudeš!

Z operačného strediska zdraví Manager.

Ciele

  • Naučiť sa pracovať s projektom vo vývojovom prostredí.
  • Naučiť sa vytvárať vlastné triedy, ich konštruktory a metódy.
  • Naučiť sa preťažiť implementáciu metódy pre rôzne typy parametrov.
  • Naučiť sa reprezentovať stav objektu členskými premennými (zapuzdrenie údajov).
  • Naučiť sa riadiť viditeľnosť členských premenných a metód.
  • Naučiť sa vytvárať inštancie objektov z tried.

Postup

Krok 1: Warming up

Kadet! Aby tvoj výcvik mohol začať, musíš si najprv pripraviť svoje prostredie. Stiahneš si kostru projektu, v ktorom budeš pracovať.

Úloha 1.1

Stiahnite si balíček s projektom, rozbaľte ho a otvorte vo vývojovom prostredí IntelliJ IDEA.

Poznámka

Predpokladom pre prácu s projektom je nainštalovaný Java Development Kit (JDK) verzie 10. Môžete ho stiahnuť z tejto stránky, alebo využiť balíčkovací systém vášho operačného systému.

Poznámka

Použitie IntelliJ IDEA nie je nevyhnutné pre prácu s projektom, ten bude funkčný v ľubovoľnom IDE s podporou zostavovacieho nástroja Gradle. V rámci týchto materiálov však budeme predpokladať použitie práve prostredia IntelliJ IDEA.

Pri otváraní projektu použite možnosť Open z uvítacej obrazovky prostredia, alebo z menu File. Zvoľte adresár, kde máte rozbalený stiahnutý projekt (daný adresár obsahuje súbor build.gradle.kts). A keďže v projekte používame zostavovací nástroj Gradle, zobrazí sa následne okno, v ktorom je potrebné nastaviť vytvorenie IDEA projektu.

Nastavenie importu Gradle projektu v prostredí _IntelliJ IDEA_
Obr. 1: Nastavenie importu Gradle projektu v prostredí IntelliJ IDEA

Skontrolujte, že máte nastavenia tak, ako sú na uvedenom obrázku okna. Ak pre nastavenie Gradle JVM nemáte položku pre Javu 10, kliknite na tlačidlo ... a zvoľte adresár, kde máte nainštalovaný JDK 10.

Po kliknutí na tlačidlo OK sa otvorí projekt a spustí sa konfigurácia nástroja Gradle a synchronizácia závislostí projektu (knižníc, ktoré daný projekt používa). Tento proces môže trvať aj niekoľko minút, keďže je potrebné stiahnuť Gradle a všetky závislosti projektu. Proces sa ukončí indexovaním stiahnutých knižníc, o ktorom vás bude prostredie informovať v spodnom stavovom riadku.

Úloha 1.2

Overte funkčnosť projektu jeho spustením.

Pre prvé spustenie projektu je potrebné otvoriť nástrojové okno pre Gradle, vyhľadať úlohu (task) run v skupine application (viď obrázok nižšie) a spustiť ju (napr. dvojklikom).

Poznámka

Nástrojové okno Gradle má tlačidlo vpravo hore na vertikálnej lište pozdĺž okraja okna prostredia. Ak túto lištu nevidíte, zapnite ju v menu ViewTool Buttons.
Prvé spustenie projektu
Obr. 2: Prvé spustenie projektu

Ak je všetko v poriadku, po kompilácii projektu sa vám zobrazí východzia mapa základného levelu spolu s oknom inšpektora.

Východzia podoba levelu
Obr. 3: Východzia podoba levelu

Poznámka

Prvým manuálnym spustením akcie run sa v prostredí IntelliJ IDEA vytvorí tzv. run konfigurácia s názvom "project-ellen [run]", ktorú nájdete v prostredí na horizontálnej lište vpravo hore. Pokiaľ je táto konfigurácia zobrazená, ďalšie spustenia sú možné pomocou tlačidla zelenej šípky vedľa, alebo pomocou klávesovej skratky Shift+F10.

Krok 2: Reactor Class

Tvojou úlohou je vytvoriť triedu, ktorá bude reprezentovať reaktor v prostredí hry Alien Breed. Stav reaktora bude reprezentovaný aktuálnou teplotou jeho jadra, celkovým poškodením reaktora a animáciou, ktorá aktuálny stav vizualizuje.

Diagram tried, ktorý vyjadruje vzťah triedy Reactor s triedou AbstractActor.
Obr. 4: Diagram tried, ktorý vyjadruje vzťah triedy Reactor s triedou AbstractActor.

Úloha 2.1

Vytvorte v projekte Java balík sk.tuke.kpi.oop.game.

Keďže projekt používa nástroj Gradle, je potrebné dodržať jeho konvenciu na umiestňovanie Java zdrojových súborov, a preto je potrebné vytvoriť tento balík v adresári src/main/java.

Úloha 2.2

V balíku sk.tuke.kpi.oop.game vytvorte triedu Reactor, ktorá bude potomkom triedy AbstractActor.

Trieda AbstractActor, ktorú použijete ako rodičovskú triedu novej tried Reactor, je súčasťou knižnice GameLib, ktorá je definovaná medzi závislosťami vášho projektu. Nie je preto potrebné ju vytvárať samostatne! Stačí ju importovať do súboru vytvoreného pre triedu Reactor. V tomto vám ale dokáže pomôcť samotné vývojové prostredie.

Poznámka

Príkaz import, ktorý prostredie doplní, slúži len na skrátenie zápisu plného mena triedy, ktorým je sk.tuke.kpi.gamelib.framework.AbstractActor. Po použití importu stačí v súbore používať krátke meno AbstractActor.

Poznámka

Keď budete potrebovať importovať nejakú triedu z poskytnutej knižnice a prostredie vám ponúkne na výber z niekoľkých možných tried s rovnakým krátkym názvom, tú správnu možnosť identifikujete podľa prefixu balíka sk.tuke.kpi.gamelib.

O dedičnosti toho zatiaľ veľa neviete, preto vám trochu napovieme. Zápis, ktorý hovorí o tom, že trieda A je potomkom triedy B, je nasledovný:

class A extends B {

}

Úloha 2.3

Do triedy Reactor pridajte členské premenné, ktoré budú reprezentovať stav reaktora: aktuálnu teplotu, poškodenie a animáciu reprezentujúcu reaktor.

Jedná sa o nasledovné členské premenné:

  • temperature - aktuálna teplota reaktora, reprezentovaná celým číslom, pričom minimálna hodnota je 0 stupňov.
  • damage - poškodenie reaktora, percentuálna hodnota, reprezentovaná celým číslom v rozsahu od 0 (nepoškodený) do 100 (zničený).
  • normalAnimation - objekt animácie, ktorá reprezentuje bezporuchový stav reaktora. Použijeme typ Animation reprezentovaný triedou Animation z balíka sk.tuke.kpi.gamelib.graphics.

Pri tvorbe členských premenných nezabúdajte na to, že nechceme, aby boli voľne viditeľné mimo triedu!

Poznámka

V počítačovej grafike (hlavne v súvislosti s 2D hrami) je možné sa veľmi často stretnúť s termínom sprite. Sprite je malý dvojrozmerný obrázok (alebo animácia), ktorý je integrovaný do väčšej scény. Efekt animácie často vzniká postupným zobrazením niekoľkých obrázkov (snímkov) na rovnakej pozícii.
Snímky sprite-u hlavnej postavy hry _Jet Set Willy_ idúcej vpravo
Obr. 5: Snímky sprite-u hlavnej postavy hry Jet Set Willy idúcej vpravo
Výsledná animácia postupným prehratím jednotlivých obrázkov animácie kombinovaná s horizontálnym posúvaním
Obr. 6: Výsledná animácia postupným prehratím jednotlivých obrázkov animácie kombinovaná s horizontálnym posúvaním
V hernej knižnici, ktorú používame, budeme pracovať s objektami typu Animation, ktoré je možné rovnako použiť na jednoduché obrázky, ako aj na obrázky obsahujúce snímky animácie sprite-u.

Úloha 2.4

Vytvorte bezparametrický konštruktor triedy Reactor, v ktorom novovytváranému objektu inicializujete premenné reprezentujúce jeho stav.

V rámci inicializácie objektu nastavte:

  • animáciu, ktorá bude znázorňovať reaktor, na nasledujúci obrázok

    Animácia reactor_on.png (rozmery sprite-u: _80x80_, trvanie snímku: _0.1_ sekundy)
    Obr. 7: Animácia reactor_on.png (rozmery sprite-u: 80x80, trvanie snímku: 0.1 sekundy)
  • počiatočnú teplotu jadra reaktora na hodnotu 0, a

  • počiatočnú percentuálnu hodnotu poškodenia na hodnotu 0.

Animácie sa nachádzajú v projekte v priečinku src/main/resources/sprites/. Pri nastavovaní cesty k animácii používajte relatívnu cestu, pričom za koreňový priečinok je považovaný src/main/resources/. Cesta k animácii reaktora preto bude vyzerať takto sprites/reactor_on.png.

Poznámka

Pri písaní ciest k súborom animácie môžete tiež využiť skratku Ctrl+Space na dopĺňanie názvov súborov a adresárov.

Pre nastavenie animácie využite nasledovný fragment kódu umiestnený v konštruktore triedy Reactor:

// create animation object
normalAnimation = new Animation("sprites/reactor_on.png", 80, 80, 0.1f, Animation.PlayMode.LOOP_PINGPONG);
// set actor's animation to just created Animation object
setAnimation(normalAnimation);

Poznámka

Dokumentáciu k jednotlivým triedam, ich konštruktorom a metódam môžete získať priamo v rámci vývojového prostredia umiestnením textového kurzora na meno požadovaného elementu a použitím skratky Ctrl+Q. Táto skratka je dostupná aj pri prechádzaní zoznamom možností ponuky automatického dopĺňania kódu (Ctrl+Space).

Úloha 2.5

Overte správnosť svojej implementácie vytvorením inštancie reaktora a jeho vložením do hernej scény.

Pokiaľ ste postupovali správne, bude vedieť po spustení projektu (Shift+F10) pomocou inšpektora vytvoriť inštanciu triedy Reactor a umiestniť ju do hry.

Plne funkčný reaktor
Obr. 8: Plne funkčný reaktor

Úloha 2.6

Vytvorte metódy getTemperature() a getDamage(), pomocou ktorej budete vedieť získať hodnotu aktuálnej teploty jadra a jeho poškodenia.

Tieto metódy poskytujú prístup na čítanie hodnôt teploty a poškodenia. Samozrejme, ich viditeľnosť je potrebné nastaviť na public. Následne viete opäť skontrolovať ich funkcionalitu pomocou inšpektora.

Poznámka

Všimnite si, že metóda na získanie hodnoty členskej premennej temperature sa volá getTemperature() a pre damage sa volá getDamage(). Takéto pomenovanie metód na získanie hodnôt premenných pridaním predpony get k názvu premennej je konvenčný a budeme ho často využívať. Výsledná metóda sa zvykne "hovorovo" nazývať aj getter.

Krok 3: Intermezzo - Method Overloading

Teraz, keď už vieš, ako vytvoriť triedu a jej metódy, ukážeme ti jednu z užitočných vlastností polymorfizmu (ku ktorému sa ešte viackrát vrátime). Tou vlastnosťou je preťaženie metód (anglicky method overloading).

Úloha 3.1

V balíku sk.tuke.kpi.oop.game vytvorte triedu Computer ako potomka triedy AbstractActor.

Ako animáciu použite sprite obrázok computer.png.

Animácia computer.png (rozmery sprite-u: _80x48_, trvanie snímku: _0.2_)
Obr. 9: Animácia computer.png (rozmery sprite-u: 80x48, trvanie snímku: 0.2)

Úloha 3.2

V triede Computer vytvorte metódy pre vykonanie základných aritmetických operácií add() a sub() pre číselné údajové typy int a float.

Každá z týchto metód bude mať 2 parametre rovnakého typu pre operandy danej aritmetickej operácie. Keďže raz to budú parametre typu int, raz float, implementáciou takýchto metód nastane ich tzv. preťaženie.

Poznámka

To, ktorá konkrétna metóda z dvoch možných pre meno add (a taktiež sub) bude zavolaná, je určené staticky na základe typov argumentov volania metódy.

Implementáciu si môžete overiť pomocou nástroja Inšpektor.

Krok 4: Reactor (Over)Heating

Späť k reaktoru! Získať jeho stav už vieš, teraz sa pozrieme na to, ako jeho stav meniť.

Úloha 4.1

Vytvorte metódu increaseTemperature(), pomocou ktorej bude možné zvýšiť aktuálnu teplotu jadra reaktora.

Táto metóda nebude vracať žiadnu hodnotu a bude mať jeden celočíselný parameter (pomenovaný napr. increment), ktorý bude reprezentovať hodnotu, o ktorú sa má aktuálna teplota jadra zvýšiť.

Pri implementovaní metódy však zohľadnite nasledovné skutočnosti:

  • S nárastom teploty lineárne zvyšujte poškodenie reaktora. Reaktor sa začne kaziť po prekročení teploty 2000 stupňov a prestane byť funkčný po dosiahnutí 6000 stupňov. Následné ochladenie reaktora ale nezníži úroveň poškodenia, ktoré už vysoká teplota spôsobila (to znamená, že po volaní tejto metódy nemôže byť hodnota poškodenia reaktora nižšia ako pred jej volaním). Pri výpočte úrovne poškodenia zaokrúhľujte desatinnú časť výsledku nadol.

    Závislosť zvyšovania poškodenia reaktora pri raste jeho teploty
    Obr. 10: Závislosť zvyšovania poškodenia reaktora pri raste jeho teploty

    Poznámka

    Znižovanie poškodenia reaktora bude možné jedine jeho opravou, ktorú budete riešiť nabudúce.
  • Ak teplota po zvýšení prekročí hodnotu 4000 stupňov, vzhľad reaktora bude od tohto momentu reprezentovaný nasledujúcou animáciou:

    Animácia reactor_hot.png (rozmery sprite-u: _80x80_, trvanie snímku: _0.05_)
    Obr. 11: Animácia reactor_hot.png (rozmery sprite-u: 80x80, trvanie snímku: 0.05)
  • Ak je poškodenie reaktora v intervale <33%, 66%>, teplota rastie 1.5-násobne; ak prekročí hodnotu 66%, teplota porastie dvojnásobne. V oboch prípadoch je zmena uvedená oproti pôvodnej hodnote parametra increment. Po výpočte novej hodnoty teploty výsledok zaokrúhlite vždy nahor na najbližšie celé číslo.

  • Ak teplota po zvýšení dosiahne (alebo prekročí) hodnotu 6000 stupňov, dôjde ku zničeniu reaktora (poškodenie reaktora dosiahne hodnotu 100%). Zničený reaktor bude reprezentovaný nasledujúcou animáciou:

    Animácia reactor_broken.png (rozmery sprite-u: _80x80_, trvanie snímku: _0.1_)
    Obr. 12: Animácia reactor_broken.png (rozmery sprite-u: 80x80, trvanie snímku: 0.1)
  • Ak poškodenie reaktora dosiahlo hodnotu 100%, akékoľvek ďalšie zvyšovanie teploty už nemá žiadny efekt.

Poznámka

Je výhodné vytvoriť objekty animácií už pri inicializovaní objektu a ich hodnoty uložiť do ďalších členských premenných. Keďže reaktor sa môže nachádzať v niekoľkých stavoch (v našom prípade v troch - stav ok, prehriaty a zničený reaktor), animácie reprezentujúce tento stav sa budú meniť. Ak sa v priebehu hry zmení stav reaktora napr. 50x, znamená to, že pri každej zmene potrebujete načítavať inú animáciu zvlášť - spolu teda 50 načítaní súborov animácií. Ak si však všetky animácie načítate pri inicializácii objektu, načítate spolu iba 3 súbory animácií, ktoré sa už budú používať podľa potreby.

Úloha 4.2

Vytvorte metódu decreaseTemperature(), pomocou ktorej bude možné znížiť aktuálnu teplotu jadra reaktora.

Táto metóda nebude vracať žiadnu hodnotu a bude mať jeden celočíselný parameter (pomenovaný napr decrement), ktorý bude reprezentovať hodnotu, o ktorú sa má aktuálna teplota jadra znížiť. Pozor, zníženie teploty neznižuje poškodenie, ktoré vysoká teplota už spôsobila!

Pri implementovaní metódy zohľadnite nasledovné skutočnosti:

  • Ak je poškodenie jadra aspoň 50%, reálne znižovanie teploty bude len polovičné oproti hodnote parametra decrement.
  • Ak je poškodenie jadra na úrovni 100%, znižovanie teploty už nemá žiaden efekt.
  • Ak teplota jadra pri ochladzovaní klesne na 4000 stupňov alebo menej, zmeňte animáciu reprezentujúcu stav reaktora na reactor_on.png.

Úloha 4.3

Vytvorte metódu updateAnimation(), ktorá na základe aktuálnej teploty nastaví animáciu reaktora.

Pri pozornom pohľade na metódy increaseTemperature() a decreaseTemperature() si všimnete, že obe metódy riešia nastavenie správnej animácie reaktora v závislosti na jeho teplote. Takáto duplicita je však zbytočná a komplikuje prípadnú zmenu funkcionality. Preto refaktorujte svoj kód tak, že túto spoločnú funkcionalitu oddelíte do samostatnej metódy updateAnimation().

Úloha 4.4

Upravte implementáciu metód increaseTemperature() a decreaseTemperature() tak, aby ste pre zmenu aktuálnej animácie využili práve vytvorenú metódu updateAnimation().

Po refaktorizácii z predchádzajúcej úlohy nezabudnite zmeniť aj pôvodné metódy, kde sa extrahovaný kód nachádzal, aby vyžívali novú metódu.

Poznámka

Metóda updateAnimation() reprezentuje len implementačný detail a nie je potrebné (ba dokonca ani žiadúce) aby bola dostupná používateľom objektu reaktora. Skryte preto túto metódu vhodným modifikátorom viditeľnosti.

Úloha 4.5

Pomocou Inšpektora umiestnite objekt reaktora do mapy a otestujte správnosť svojej implementácie.
Pokazený (prehriaty) reaktor
Obr. 13: Pokazený (prehriaty) reaktor

Doplňujúce úlohy

Úloha A.1

Ošetrite prípad, kedy sa pri volaní metód increaseTemperature() a decreaseTemperature() použije záporný parameter.

Ak k takémuto volaniu dôjde, nevykonajte žiadnu zmenu teploty.

Úloha A.2

Upravte reaktor tak, aby rýchlosť pulzovania jeho animácie závisela od úrovne poškodenia.

Pri vyššom poškodení reaktora by trvanie jedného snímku animácie malo byť kratšie, pre dosiahnutie efektu rýchlejšieho pulzovania reaktora.