Motivácia
Máme problém! Na 347. poschodí došlo k úniku vysoko toxického materiálu do ovzdušia! Bezpečnostný systém automaticky uzamkol kontaminované miestnosti a pracovníci, ktorí neboli kontaminovaní, boli evakuovaní do bezpečia. Zlyhal však odsávací systém, ktorý mal zabezpečiť automatické odsatie kontaminovaného vzduchu. Keďže však máte bohaté skúsenosti s opravovaním predmetov kladivom, boli ste vybratí, aby ste so svojou Ripleyovou pokazený ventilátor opäť uviedli do chodu.
Taktický tím pre vás pripravil plán misie, ktorého striktné dodržanie musí bezpodmienečne viesť k jej úspešnému splneniu.
Z operačného strediska zdraví Manager.
Ciele
- Porozumieť herným mapám vytváraným v editore Tiled .
- Naučiť sa využívať návrhový vzor Abstract Factory .
- Využiť návrhový vzor Observer (publish/subscribe).
- Oboznámiť sa s runtime reprezentáciou tried.
- Využiť statické členy triedy.
Postup
Krok 1: Welcome to the Real World!
Primárnym cieľom tejto misie je naučiť Ripleyovú pohybovať sa v novom prostredí. Na dosiahnutie tohto cieľa potrebujete pri vytváraní hernej scény načítať mapu reprezentujúcu prostredie Ripleyovej, spolu s okolitými, viac či menej živými, aktérmi.
Aby to bolo možné, potrebujete najskôr vytvoriť factory triedu, pomocou ktorej bude vedieť vaša simulácia nakonfigurovať patričnú situáciu a rozmiestniť aktérov do ich počiatočných pozícií podľa údajov zaznačených v mape. A keďže mapa môže obsahovať aj steny, bude potrebné naučiť Ripleyovú ich rozoznávať a neprechádzať skrze ne.
Úloha 1.1
Stiahnite si balíček s mapou pre dnešnú misiu a rozbaľte ho do adresára src/main/resources/maps/
v projekte.
Výsledná štruktúra súborov v adresári src/main/resources/maps
by mala byť nasledovná:
src/main/resources/maps/
├── tilesets/
│ └── tileset01.png
└── mission-impossible.tmx
Úloha 1.2
V main()
metóde projektu upravte volanie konštruktora triedy World
tak, že k názvu scény pridáte druhý argument v podobe cesty k .tmx
súboru s mapou, ktorá sa má do scény načítať.
Cestu k mape zapíšete ako maps/mission-impossible.tmx
.
Úloha 1.3
V balíku pre scenáre vytvorte novú triedu pre scenár pomenovanú podľa tejto misie: MissionImpossible
.
Trieda musí, samozrejme, implementovať rozhranie SceneListener
a scenár budete neskôr písať do metódy sceneInitialized()
.
Úloha 1.4
V triede scenára MissionImpossible
vytvorte vnorenú verejnú statickú triedu s názvom Factory
, ktorá implementuje rozhranie ActorFactory.
Táto trieda bude reprezentovať návrhový vzor továrenská metóda (Factory Method) a trieda samotná bude obsahovať len jednu metódu create(String type, String name)
.
Úloha 1.5
Implementujte metódu create()
v triede MissionImpossible.Factory
tak, aby vytvárala inštancie aktérov potrebných pre dnešnú misiu.
Herná mapa dnešnej misie obsahuje objekty s menami access card
, door
, ellen
, energy
, locker
, ventilator
. Zatiaľ viete poskytnúť inštancie pre Ripleyovú (ellen
) a lekárničku (energy
). Pre iné mená z metódy create()
vráťte zatiaľ hodnotu null
.
Gamelib
Metóda create()
je volaná hernou knižnicou pri spracovávaní mapy. Mapy sa vytvárajú pomocou editora Tiled. Sú to v podstate XML súbory, ktoré vo viacerých vrstvách definujú vzhľad herného sveta. Každý aktér, ktorého mapa definuje, má typ a meno (odtiaľ parametre metódy create()
v rozhraní ActorFactory
), ako aj svoju počiatočnú polohu. Úlohou vytváranej továrne na aktérov je teda vytvoriť inštancie, ktoré sa majú použiť na reprezentáciu daného aktéra. Umiestnenie aktérov na danú pozíciu definovanú v mape už automaticky zabezpečí scéna volaním metódy setPosition()
nad získaným aktérom.
Úloha 1.6
Nastavte inštanciu triedy MissionImpossible.Factory
ako továreň na aktérov pre scénu.
Vytvorenú inštanciu triedy MissionImpossible.Factory
prepojte s inštanciou scény tak, že ju v metóde main()
odovzdajte ako tretí argument konštruktora triedy World
:
Scene missionImpossible = new World("mission-impossible", "maps/mission-impossible.tmx", new MissionImpossible.Factory());
Úloha 1.7
V scenári zabezpečte nastavenie ovládania Ripleyovej pomocou ovládačov MovableController
a KeeperController
, zobrazenie jej batohu a stavu energie.
Keďže je teraz Ripleyová vytváraná cez továreň na základe jej umiestnenia v mape scény, inštanciu Ripleyovej už v scenári nevytvárajte, ale získajte už existujúcu inštanciu zo scény.
Poznámka
Aby ste nemuseli duplikovať kód potrebný na zobrazenie stavu (energie) Ripleyovej do každého scenára, vytvorte v triede Ripley
metódu showRipleyState()
v ktorej zabezpečíte vykreslenie potrebného textu. Túto metódu potom zavolajte v metóde sceneUpdating()
v scenári.
Gamelib
Ak chcete, aby scéna sledovala pohyb Ripleyovej, využite na to metódu scene.follow(Actor actor)
.
Poznámka
Nezabudnite pridať novú triedu pre dnešný scenár ako listener na scénu v metóde main()
.
Úloha 1.8
Overte správnosť implementácie.
Ak ste postupovali správne, po spustení uvidíte scénu s mapou, na ktorej sa už bude nachádzať lekárnička a Ripleyová. Ripleyovú by ste mali vedieť ovládať pomocou klávesnice, a zatiaľ by mala zvládnuť prechádzať aj cez steny.
Úloha 1.9
Zabezpečte, aby Ripleyová neprechádzala cez steny.
Nášmu taktickému tímu sa ako najvhodnejšie javí implementovať túto funkcionalitu do triedy akcie, ktorá sa stará o pohyb aktérov.
Gamelib
Zistiť, či je objekt typu Actor
v kolízii so stenou, môžete pomocou volania metódy intersectsWithWall(Actor actor)
nad objektom mapy. Objekt mapy získate zo scény pomocou metódy getMap().
V mape sú steny definované v rámci vrstvy s názvom walls
. Táto vrstva nie je v rámci scény graficky vykreslená, avšak informácie z tejto vrstvy (vyplnené, respektíve prázdne dlaždice) sú použité na vytvorenie stien v miestach vyplnených dlaždíc.
Krok 2: Alohomora!
Pri implementácii triedy MissionImpossible.Factory
ste si určite všimli, že ste nemali k dispozícii všetkých potrebných aktérov definovaných v mape. V rámci plnenia tohto kroku misie niekoľko chýbajúcich predmetov vytvoríte. Konkrétne budete vytvárať dvere a prístupovú kartu, pomocou ktorej bude možné zamknuté dvere odomknúť.
Pri plnení tohto kroku misie bude potrebné aj rozšíriť možnosti použiteľných predmetov, aby ste ich mohli používať interaktívne - pomocou klávesnice.
Úloha 2.1
V novom balíku sk.tuke.kpi.oop.game.openables
vytvorte rozhranie Openable
, ktoré bude reprezentovať otvárateľné objekty.
Rozhranie bude rozširovať rozhranie Actor
a bude mať nasledovné metódy:
void open()
- otvorenie predmetu (aktéra)void close()
- zatvorenie predmetu (aktéra)boolean isOpen()
- vráti true, ak je predmet (aktér) otvorený, v opačnom prípade vráti false
Úloha 2.2
V balíku pre otvárateľných aktérov vytvorte podľa vyššie uvedeného diagramu triedu Door
reprezentujúcu bežné dvere v hre a implementujte v nej metódy z rozhraní Openable
a Usable
.
Dvere budú použiteľné s akýmkoľvek aktérom, preto ako typový argument typu Usable
použite typ Actor
. Pre aktuálnu misiu budeme potrebovať vertikálne otočené dvere, preto ako animáciu použite obrázok vdoor.
Vo východzom stave budú dvere zavreté. Pri použití dverí niektorým z aktérov dvere otvorte, ak sú zavreté, a zatvorte ich, ak už sú otvorené.
Gamelib
Pri animovaní otvárania a zatvárania dverí viete využiť režimy prehrávania animácie ONCE
a ONCE_REVERSED
, ktoré viete nastavovať metódou setPlayMode()
na existujúcej animácii, spolu s jej metódami play()
a stop()
.
Úloha 2.3
Doplňte implementáciu dverí tak, aby cez ne nebolo možné prejsť, keď sú zavreté.
Nášmu taktickému a strategickému tímu sa ako najlepší spôsob znepriechodnenia zatvorených dverí javí vytvoriť na ich mieste v mape stenu. Pri ich otvorení je zase potrebné stenu zrušiť.
Gamelib
Všetky steny sú v rámci hernej mapy definované vo vrstve walls, ktorú ste videli už na obrázku vyššie. Pri práci s editorom Tiled sú mapy vytvárané pomocou dlaždíc, ktoré sú v našom prípade veľké 16x16 pixelov. Každá vyplnená dlaždica vo vrstve walls reprezentuje stenu.
Aj v rámci objektovej reprezentácie mapy máme k dispozícii dlaždice. Každá dlaždica pokrývajúca mapu má svoju x-ovú a y-ovú súradnicu, ktorá reprezentuje poradie dlaždice v smere osi x a y, pričom počiatok súradnicového systému je v ľavom dolnom rohu mapy. Dlaždica na tomto umiestnení má súradnice [0, 0].
Konkrétnu dlaždicu viete získať pomocou metódy getTile(int tileX, int tileY)
nad objektom mapy. Táto dlaždica je typu MapTile a okrem jej pozície a rozmerov z nej viete metódou isWall()
zistiť, či daná pozícia v mape je považovaná za stenu. Konečne, metódou setType()
viete typ dlaždice zmeniť na jednu z dvoch hodnôt enumerácie MapTile.Type
:
CLEAR
- miesto, kde sa dlaždica tohto typu nachádza, je priechodzie,WALL
- miesto, kde sa dlaždica tohto typu nachádza, je považované za stenu.
Poznámka
Uvedomte si, že jednotky súradníc dlaždíc sa nezhodujú s jednotkami súradníc napr. aktérov. Preto budete potrebovať robiť prepočet z pozícií definovaných v pixeloch do pozícií definovaných v indexe dlaždice v mriežke. Pri výpočte vychádzajte z veľkosti dlaždice.
Poznámka
Nezabudnite na to, že dvere svojou plochou pokrývajú dve dlaždice. Obom je potrebné meniť stav pri otváraní a zatváraní dverí.
Úloha 2.4
Upravte create()
metódu v MissionImpossible.Factory
tak, aby pre meno aktéra "door"
vrátila inštanciu triedy Door
a overte svoju implementáciu.
V scéne by mali pribudnúť dvere a Ripleyová by sa nemala cez ne vedieť dostať do menšej miestnosti.
Úloha 2.5
Do rozhrania Usable
a do každého konkrétneho aktéra implementujúceho toto rozhranie pridajte metódu getUsingActorClass()
.
V nasledujúcej úlohe budete pridávať podporu pre plánovanie akcie Use
pomocou klávesnice. Scenár použitia je napríklad takýto:
- Ripleyová príde k dverám, ktoré implementujú rozhranie
Usable
. - Stlačením priradenej klávesy hráč signalizuje zámer otvoriť dvere.
- Akcia
Use
je naplánovaná tak, aby sa dvere použili s Ripleyovou.
Iný scenár použitia, ktorý budete implementovať neskôr, bude nasledovný:
- Ripleyová s prístupovou kartou v batohu príde k uzamknutým dverám.
- Stlačením priradenej klávesy hráč signalizuje zámer použiť predmet na vrchu batoha.
- Akcia
Use
je naplánovaná tak, aby sa prístupová karta (predmet z batoha) použila so zamknutými dverami (predmet v scéne v kolízii s Ripleyovou).
Poznámka
Ako je viditeľné predovšetkým v druhom prezentovanom scenári použitia, Ripleyová tu účinkuje ako sprostredkovateľ použitia predmetu, ktorý sa nachádza v jej blízkosti.
Vzhľadom na obmedzenie JVM platformy nie je možné počas behu programu pracovať priamo s typmi reprezentovanými typovými parametrami (tie existujú len počas kompilácie). Preto pridávanou metódou getUsingActorClass()
budeme pre každý použiteľný predmet explicitne vracať runtime reprezentáciu triedy aktéra, s ktorým daný použiteľný predmet dokáže pracovať. Táto reprezentácia triedy sa využije v ďalšej úlohe pri hľadaní kompatibilného aktéra (aktéra, ktorý je inštanciou danej triedy), s ktorým môže byť akcia Use
naplánovaná.
Nová metóda v rozhraní Usable
nech má nasledujúcu signatúru:
Class<T> getUsingActorClass(); // T reprezentuje typovy parameter v triede Usable
V každom použiteľnom aktérovi, ktorý dosadzuje konkrétny typ kompatibilného aktéra do typového argumentu pre Usable
, vráťte z tejto metódy referenciu na runtime reprezentáciu triedy kompatibilného aktéra. V prípade kladiva (triedy Hammer
), ktoré je použiteľné s reaktorom, bude implementácia tejto metódy teda vyzerať nasledovne:
public Class<Reactor> getUsingActorClass() {
return Reactor.class;
}
Úloha 2.6
V akcii Use
pridajte metódu scheduleForIntersectingWith(Actor mediatingActor)
, pomocou ktorej bude možné naplánovať Use
akciu na aktérovi, ktorý je kompatibilný s Usable
predmetom odovzdaným konštruktoru akcie a nachádza sa v kolízii so sprostredkujúcim aktérom reprezentovaným parametrom mediatingActor
.
Táto metóda je potrebná v triede akcie Use
kvôli tomu, lebo potrebujeme v kontexte, v ktorom typový systém jazyka má k dispozícii typový argument Usable
predmetu, nájsť prvého kompatibilného aktéra na scéne v kolízií so sprostredkujúcim aktérom (predovšetkým Ripleyovou). To nám v ďalšej úlohe umožní interaktívne plánovať akciu Use
ako reakciu na stlačenie klávesy.
Za predpokladu, že máte funkčnú implementáciu akcie Use
, kde členská premenná usable
reprezentuje použiteľného aktéra odovzdaného konštruktoru akcie a T
reprezentuje typový parameter akcie, je možné túto metódu implementovať nasledovne:
public Disposable scheduleForIntersectingWith(Actor mediatingActor) {
Scene scene = mediatingActor.getScene();
if (scene == null) return null;
Class<T> usingActorClass = usable.getUsingActorClass(); // `usable` je spominana clenska premenna
for (Actor actor : scene) {
if (mediatingActor.intersects(actor) && usingActorClass.isInstance(actor)) {
return this.scheduleFor(usingActorClass.cast(actor)); // naplanovanie akcie v pripade, ze sa nasiel vhodny akter
}
}
return null;
}
Poznámka
Ak ste sa už oboznámili so stream API pre prácu s kolekciami a chceli by ste ho využiť v tomto prípade, ekvivalentná implementácia tejto metódy môže byť nasledovná:
public Disposable scheduleForIntersectingWith(Actor mediatingActor) {
Scene scene = mediatingActor.getScene();
if (scene == null) return null;
Class<T> usingActorClass = usable.getUsingActorClass(); // `usable` je spominana clenska premenna
return scene.getActors().stream() // ziskame stream aktérov na scene
.filter(mediatingActor::intersects) // vyfiltrujeme akterov, ktori su v kolizii so sprostredkovatelom
.filter(usingActorClass::isInstance) // vyfiltrujeme akterov kompatibilneho typu
.map(usingActorClass::cast) // vykoname pretypovanie streamu akterov
.findFirst() // vyberieme prveho (ak taky existuje) aktera zo streamu
.map(this::scheduleFor) // zavolame metodu `scheduleFor` s najdenym akterom a vratime `Disposable` objekt
.orElse(null); // v pripade, ze ziaden vyhovujuci akter nebol najdeny, vratime `null`
}
Úloha 2.7
Do ovládača KeeperController
pridajte plánovanie akcie Use
pre predmety na scéne.
Pri stlačení klávesy U
naplánujte použitie prvého nájdeného použiteľného predmetu v kolízii s ovládaným aktérom. Ovládaný aktér bude následne vystupovať v úlohe sprostredkovateľa pre nájdenie kompatibilného predmetu pomocou pridanej metódy scheduleForIntersectingWith()
.
Poznámka
Pri riešení úlohy budete v ovládači potrebovať vykonať pretypovanie všeobecného aktéra na Usable
aktéra, no nebudete mať informáciu o tom, ktorý konkrétny podtyp aktéra je typovým argumentom nájdeného použiteľného aktéra. Použite teda zástupný symbol ?
namiesto konkrétneho typového argumentu. Pretypovanie môže vyzerať takto:
(Usable<?>) actor
Úloha 2.8
Overte správnosť svojej implementácie.
Ak postupujete správne, Ripleyová by mala vedieť otvoriť dvere, ku ktorým sa priblíži, stlačením klávesy U
.
Úloha 2.9
Podľa diagramu tried na začiatku kroku vytvorte v balíku pre otvárateľných aktérov triedu LockedDoor
, ktorá bude dediť od triedy Door
a bude reprezentovať uzamknuté dvere.
Odstrániť bezpečnostný zámok však bude možné len použitím správnej prístupovej karty, čo budete implementovať v ďalšej úlohe. Po odomknutí dverí ich už bude možné kedykoľvek otvoriť a zatvoriť, tak ako bežné dvere.
V triede implementujte metódy:
void lock()
- zamknutie (a zároveň zatvorenie) dverí,void unlock()
- odomknutie (a zároveň otvorenie) dverí,boolean isLocked()
- vráti aktuálny stav dverí - či sú zamknuté alebo nie.
Úloha 2.10
Podľa diagramu tried na začiatku kroku vytvorte v balíku sk.tuke.kpi.oop.game.items
triedu reprezentujúcu prístupovú kartu AccessCard
.
Táto karta bude implementovať rozhrania Collectible
a Usable
a bude použiteľná práve s uzamknutými dverami LockedDoor
. Použitím karty sa uzamknuté dvere odomknú.
Animácia reprezentujúca prístupovú kartu je uložená v súbore key.
Úloha 2.11
Pridajte do ovládača KeeperController
naplánovanie akcie Use
pre predmety na vrchu batoha.
Pri stlačení klávesy B
naplánujte akciu Use
na predmete na vrchu batoha v prípade, že je tento predmet použiteľný. Opäť využite ovládaného aktéra ako sprostredkovateľa na nájdenie kompatibilného aktéra v jeho blízkosti na scéne.
Úloha 2.12
Upravte create()
metódu v MissionImpossible.Factory
tak, aby pre meno "door"
vytvárala inštanciu LockedDoor
namiesto Door
, pre meno "access card"
zas inštanciu AccessCard
a overte svoju implementáciu.
Overte, že použitím klávesy U
už nie je možné otvoriť zamknuté dvere a že tieto sa otvoria, ak si Ripleyová vezme do batohu prístupovú kartu a použijete klávesu B
po priblížení sa k dverám.
Krok 3: Missing Items
Pre úplnosť aktérov definovaných v mape ešte chýba skrinka a ventilátor. Tie vytvoríte v tomto kroku. Takisto budeme potrebovať rozšíriť opravovacie schopnosti kladiva (schovávajúceho sa v skrinke) na akékoľvek opraviteľné predmety.
Úloha 3.1
Upravte kladivo tak, aby vedelo opravovať ľubovoľný Repairable
predmet a nie iba Reactor
.
Nezabudnite zmeniť aj triedu získavanú metódou getUsingActorClass()
. Rovnako ako triedu Reactor.class
môžete vrátiť aj runtime reprezentáciou rozhrania Repairable
ako Repairable.class
.
Poznámka
Keďže typovým argumentom rozhrania Usable
môže byť len podtyp Actor
-a, zmena je potrebná aj v rozhraní Repairable
: toto rozhranie upravte tak, aby rozširovalo rozhranie Actor
.
Úloha 3.2
Podľa uvedeného diagramu tried vytvorte v balíku sk.tuke.kpi.oop.game
triedu Locker
, ktorá bude reprezentovať skrinku, po otvorení (použití) ktorej hráč nájde kladivo.
Nájdenie kladiva realizujte tým, že kladivo zo skrinky vypadne, akonáhle Ripleyová skrinku preskúma. Skrinka nech je použiteľná len raz, takže Ripleyová preskúmaním jednej skrinky získa len jedno kladivo.
Animácia reprezentujúca skrinku je uložená v súbore locker.
Úloha 3.3
Podľa uvedeného diagramu vytvorte v balíku sk.tuke.kpi.oop.game
aj triedu Ventilator
reprezentujúcu ventilátor, ktorý bude musieť Ripleyová opraviť kladivom.
Animácia reprezentujúca ventilátor je uložená v súbore ventilator. Keďže ventilátor začne v scéne vystupovať ako pokazený, zastavte spočiatku prehrávanie jeho animácie. V metóde repair()
zas reprezentujte opravenie ventilátora spustením prehrávania animácie.
Úloha 3.4
Dokončite implementáciu MissionImpossible.Factory
pre vytváranie objektov na základe mien "ventilator"
a "locker"
.
Krok 4: Entering Contaminated Zone
Vstupujeme do poslednej fázy dnešnej misie. Cieľom je opraviť pokazený vetrák v zamorenej oblasti, čím nebezpečenstvo zamorenia pominie. Pri plnení tohto kroku si ukážeme ako pomocou správ vieme reagovať na vzniknuté udalosti, pričom využijeme návrhový vzor Observer.
Úloha 4.1
Pridajte do triedy Door
témy správ o ich otvorení, respektíve zatvorení.
Rôzne časti hry (iní aktéri, váš kód v rámci scenára, ...) môžu chcieť reagovať na to, ak sa nejaké dvere otvoria alebo zatvoria. To umožníme vytvorením témy správ s definovaným typom obsahu (typom objektu, ktorý sa bude v danej téme posielať) a publikovaním správy (konkrétneho objektu daného typu) do zbernice správ na scéne. V prípade dverí bude typom správy samozrejme typ Door
.
Témy správ vytvoríte pomocou factory metódy Topic.create()
, ktorej prvý argument je názov témy a druhý typ objektu posielaný v správach.
Keďže k témam budeme chcieť pristupovať bez potreby mať referenciu na konkrétne dvere, implementujte ich ako statické členské premenné triedy Door
. Téma pre otvorenie dverí môže vyzerať takto:
public static final Topic<Door> DOOR_OPENED = Topic.create("door opened", Door.class);
Analogicky vytvoríte aj tému pre zatvorenie dverí.
Úloha 4.2
Publikujte správy o otvorení, respektíve zatvorení dverí.
V metódach open()
a close()
dverí publikujte správu o tom, ktoré dvere boli práve otvorené, respektíve zatvorené. Využite metódu publish()
na objekte zbernice správ scény MessageBus, ktorý zo scény získate metódou getMessageBus()
.
Úloha 4.3
V scenári naplánujte akciu, v rámci ktorej bude dochádzať k postupnému znižovaniu energie Ripleyovej v dôsledku rozširovania kontaminácie po otvorení dverí.
Zbernica správ MessageBus
má metódu subscribe(Topic<M> topic, Consumer<M> listener)
, ktorou sa vieme prihlásiť k "odberu" správ publikovaných v rámci určitej témy. Prvým argumentom je objekt témy správ a druhým je lambda výraz (resp. objekt typu funkcionálneho rozhrania Consumer
) s jedným parametrom - objektom odoslaným v rámci konkrétnej správy.
Prihláste sa k téme správ Door.DOOR_OPENED
a zareagujte na otvorenie dverí naplánovaním patričnej akcie na znižovanie energie Ripleyovej.
Poznámka
Odoberanie energie riešte vo vhodnom intervale, aby neprebiehalo príliš rýchlo a Ripleyová mala ešte čas na dokončenie misie.
Úloha 4.4
Overte svoju implementáciu.
Po otvorení dverí by mala začať Ripleyovej klesať energia.
Úloha 4.5
Upravte triedu Ripley
tak, aby sa po dosiahnutí energie 0 jej animácia zmenila na player_die a publikujte správu o jej smrti.
Pre animáciu použite režim prehrávania Animation.PlayMode.ONCE
. Vytváranú tému správ pomenujte RIPLEY_DIED
.
Úloha 4.6
Zabezpečte, aby Ripleyová nebola po smrti ovládateľná ovládačmi MovableController
a KeeperController
.
Gamelib
Volanie metódy registerListener()
nad objektom Input
vracia referenciu na Disposable objekt, ktorým môžete zrušiť registrovaný listener (obdoba Disposable
objektov vrátených z metódy scheduleFor
na rušenie akcií).
Upravte implementáciu scenára tak, aby ste po prijatí správy o smrti Ripleyovej zrušili listenery pre spomínané ovládače a znemožnili tak ďalšie ovládanie Ripleyovej.
Úloha 4.7
Zabezpečte, aby sa po oprave ventilátora (pri čom dôjde k "odvetraniu" kontaminovaného priestoru) zastavilo znižovanie energie Ripleyovej.
Túto úlohu tiež riešte vytvorením témy správ pre opravenie ventilátora (pomenovanej VENTILATOR_REPAIRED
). V reakcii na túto správu v scenári zastavte naplánované znižovanie energie Ripleyovej.
Poznámka
Pri riešení myslite na to, že premenné definované v rámci lambdy nie sú viditeľné mimo lambdy a do lokálnych premenných definovaných mimo lambdy nemôžete priraďovať v rámci lambdy iné objekty. Lokálne premenné zachytené v lambde sú totiž efektívne finálne.
Úloha 4.8
Overte implementáciu celej misie.
Po oprave ventilátora kladivom by Ripleyovej mala prestať klesať energia. Ak opravu nestihne do času, keď jej energia klesne na 0, nemali by ste vedieť Ripleyovú už ovládať.
Doplňujúce zdroje
- Editor Tiled používaný na vytváranie herných máp.
- Návrhový vzor Abstract factory.