Programování pro hračičky/Krok 7

Z Wikiverzity
Skočit na navigaci Skočit na vyhledávání
Jak používat klasifikační nálepkuTato stránka je součástí kurzu:
středoškolská
Příslušnost: skupinová

Komunikace mezi objekty[editovat]

v prvním kroku tohoto kurzu jsme si uvědomili, že virtuální svět Prahů se skládá z objektů, které mají určité vlastnosti a vzájemné vztahy. Nyní se pokusíme popsat si přesněji, jak vlastně objekty se svými vlastnostmi zacházejí, či jak přesně se k jejich vlastnostem přistupuje jak z objektu samotného, tak z objektů jiných.

Objekty si můžeme představit jako vzájemně nezávislé programy, umístěné kdesi v počítači. Když chceme spustit MUD Prahy, spustíme takzvaný herní ovladač (též „driver“ nebo „engine“), který můžeme brát za jakýsi praobjekt, s nímž stojí a padá bytí všech dalších objektů. Ovladač následně jako první z dalších objektů spustí řídicí objekt celé hry (též „hlavní řídicí objekt“ nebo „master“), a spolu s ním podle interního seznamu ještě několik desítek dalších objektů (zejména řídicí objekty dílčích oblastí hry, ale také třeba objekt jídelny v Domě, tedy oné první místnosti, v níž se nový hráč ocitne po nalogování). Některé z těchto objektů pak požádají ovladač o spuštění dalších objektů — například řídicí objekt pravidelných spojů dá spustit sem a tam plující liburnu Haedus, pašeráckou Vichřici i přívoz u Pelusia, zatímco řídicí objekt domény Oikúmené dá mimo jiné postavit do severozápadního rohu jeruzalémského chrámového nádvoří plačící Sáru a do ulic Cesareje vypustit opilého Bibonia, ale také spustit řídicí objekt legionářských hlídek, který následně začne obdobným způsobem vysílat patroly po celé Římské říši. Ještě než se do hry přihlásí první hráč, ožívají takto světy mezi Prahy stovkami objektů, tedy spuštěných programů.

Každý z těchto programů běží sám o sobě, nezávisle na ostatních.[1] Aby však mohl spolu s ostatními vytvářet společné herní prostředí, musí s nimi komunikovat. Příkladem takové komunikace je už výše zmíněné spouštění dalších objektů; s určitým zjednodušením můžeme například vypuštění objektu opilce Bibonia rozepsat takto:

  1. Řídicí objekt domény Oikúmené pošle zprávu ovladači, že má být vytvořen objekt Bibonius, založený na tom a tom programu.
  2. Ovladač spustí příslušný program a vzniklému objektu pošle zprávu, že byl vytvořen.
  3. Objekt Bibonius si probere seznam akcí, které má provést hned po svém vytvoření, a jako jednu z nich pošle zprávu sobě samému, že se má přesunout na určité místo v Cesareji.
  4. Objekt Bibonius pošle zprávu objektu místnosti, do níž se hodlá přesunout.
  5. Objekt místnosti si zapíše objekt Bibonius do svého inventáře a pošle zprávu všem dalším objektům ve svém inventáři, že se k nim přesunul objekt Bibonius.

Zprávy, které si objekty takto posílají, se vždy odkazují na určité funkce osloveného objektu (viz výklad o akcích s objekty ve druhém kroku tohoto kurzu), k nimž se pak pojí určité parametry, tedy vysvětlení, jak se má dotyčná funkce provést; programátorsky se pak většinou zapisují parametry do závorky za jméno funkce, tedy má-li se provést funkce print s parametrem "Ahoj!", zapíše se to print("Ahoj!"). Výše uvedené děje bychom tedy mohli exaktněji zapsat takto:

  1. Řídicí objekt domény Oikúmené posílá zprávu ovladači:
    touch("/d/oikumene/cesarea/touch/opilec")
  2. Ovladač posílá zprávu objektu Bibonius:
    create()[2]
  3. Objekt Bibonius posílá zprávu objektu Bibonius:
    move("/d/oikumene/cesarea/pristav/nabr1")
  4. Objekt Bibonius posílá zprávu danému objektu místnosti:
    notify("move_in",this_object())
  5. Objekt místnosti postupně všem objektům ve svém inventáři:
    receive_message(MT_LOOK,MA_MOVE,"Kde se vzal, tu se vzal, najednou tu stojí Bibonius.")

Proč to ale vysvětlujeme takhle složitě? Nešlo by prostě říci, že objekt A zavolá v objektu B funkci fu()?[3] Jistě by to tak říci šlo, často se to tak říká, a ve druhém kroku tohoto kurzu jsme to tak i přímo napsali. Nyní nám však jde o to, abychom si uvědomili, že každý objekt žije svým životem svébytného programu, a byť je mu občas poslán pokyn, aby provedl funkci fu(), neznamená to nutně, že funkce fu() bude provedena: může se stát, že objekt B, tedy adresát poslané zprávy, žádnou funkci fu() nezná, takže ji ani nemůže provést, může se stát, že objekt A nemá právo po objektu B požadovat provedení funkce fu(), a může se také stát, že zpráva k objektu B z nějakého důvodu vůbec nedorazí. Někdy může něco z toho nastat nechtěně, tedy chybou v programu, ale často se toto vzájemné chování přímo programátorsky využívá. Proto je vhodnější si objekty představovat jako nezávislé programy, které spolu komunikují, než jako pouhé shluky funkcí, které se na zavolání provedou.

Představa o posílání zpráv mezi objekty je vhodná i proto, že některé funkce mají výslednou hodnotu (přesně se jí říká návratová hodnota funkce), která je po provedení funkce zaslána objektu, jenž vyslal požadavek na provedení funkce. Tak třeba výše zmiňovaná funkce move() po svém provedení pošle volajícímu objektu (ve výše uvedeném příkladu je to objekt Bibonius) krátkou zprávu, zda byl pohyb objektu zdárně proveden (pak zní tato zpráva MOVE_OK), nebo z jakého důvodu případně proveden nebyl (pak může zpráva znít MOVE_NOT_ALLOWED, MOVE_NO_ROOM, MOVE_DEST_CLOSED, MOVE_ENV_CLOSE nebo MOVE_NO_DEST).[4] Chceme-li o nějakém objektu cokoliv zjistit, musíme se ho zeptat tím, že mu pošleme zprávu, tedy požadavek na spuštění určité funkce — a odpověď na svou otázku se dozvíme právě ze zprávy, kterou nám dotyčný objekt pošle nazpět, až funkci vykoná, tedy z návratové hodnoty funkce.

Zjišťování a změny vlastností objektů[editovat]

Pokud hráč zadá příkaz k prozkoumání (tedy prohlédni si, očichej a pod.) některého jiného objektu ve hře, pak objekt hráče vyšle k dotyčnému objektu zprávu s požadavkem na spuštění funkce, která pošle nazpět zprávu obsahující daný smyslový popis. V programovém kódu Prahů se názvy takových funkcí zpravidla skládají z předpony query_ a interního označení vlastnosti, která je zjišťována. Je-li kupříkladu zápach interně označován anglickým slovem smell, pak se tato funkce jmenuje query_smell(), a její návratovou hodnotou je buď textový řetězec (tedy posloupnost znaků, která se pro odlišení v programovém textu uzavírá do tzv. programátorských uvozovek ""), nebo 0. V prvním případě objekt hráče vypíše na terminál obdržený řetězec, ve druhém případě přednastavený „nulový zápach“, tedy Necítíš nic.[5]

Podobné zjišťování vlastnosti jiného objektu probíhá velmi často mezi různými herními objekty bez přímé účasti hráčů. Tak se například předmět, který má být přesunut do nádoby, ptá této nádoby, zda je v ní pro něj dost místa, tak se například skřet, který potkal jinou živou bytost, ptá na její rasu, aby na ni v případě, že se nejedná o skřeta, neprodleně zaútočil, tak se například rýč ptá místnosti, ve které má rýt, zda se v ní vůbec rýt dá.

Téměř každá (výjimky nás v tuto chvíli nezajímají) funkce s předponou query_ má svůj protějšek ve funkci s předponou set_, která tutéž vlastnost nastavuje, potažmo mění. Pokud můžeme zápach zjistit funkcí query_smell(), můžeme jej změnit funkcí set_smell(). Takové funkci samozřejmě musíme předat jako parametr nový zápach, což by se pak programátorsky zapsalo třeba set_smell("Tahle sekera smrdí krchovem!"). Jakožto měničští učni zatím nemůžeme používat takovéto funkce přímo, ale měničská mušle se svým příkazem změň nám k takovým změnám bude dobrým prostředníkem:

> změň zápach sekery
Chystáš se změnit zápach zubaté sekery.
Soustředíš se na měnění...
Hodnota po změně: Tahle sekera smrdí krchovem!
Změnil jsi zápach zubaté sekery jen na krátkou chvilku.

Po zadání příkazu změň (a propočítání naší dovednosti a náhody, zda se nám tentokrát má změna povést) vyšle naše mušle, či přesněji naše měničská dovednost směrem k sekeře zprávu s požadavkem na změnu zápachu na "Tahle sekera smrdí krchovem!". A skutečně, když následně sekeru někdo očichá, tedy vyšle k ní požadavek, aby spustila funkci query_smell(), obdrží následně v odpověď: Tahle sekera smrdí krchovem!

Údaj „jen na krátkou chvilku“ (nebo při větším štěstí třeba „na poměrně dlouho“) nám říká, jak účinně zapůsobilo naše měnění, tedy jak dlouho tak může naše změna trvat. Po uplynutí dotyčné doby (po nějaké té měničské praxi si vycvičíme odhad, jakou dobu která hláška asi znamená) se ozve Plop. a sekera se vrátí do původního stavu. A tady nás možná napadne, že tu něco nehraje. Nastolit původní stav přece není tak snadné — naše mušle by se nejprve musela zeptat na původní vlastnosti sekery (a obdobně všech objektů, které začneme měnit), někde si je pamatovat, nepoplést si je, když změníme tutéž vlastnost vícekrát za sebou nebo když ji mezitím změní někdo jiný, atd.

Vskutku, mušle to dělá jinak, využívajíc právě jednoho z oněch výše zmíněných případů, kdy zpráva nedojde k cílovému objektu. Jak jsme si prozradili už v pátém kroku tohoto kurzu, pro měničské úkony se používá zastínění. Začneme-li měnit zápach sekery, vyrobí mušle zastínění, tedy nový objekt, který nám podstrčí místo sekery. Zastínění si pak do svých vlastností uloží změnu, kterou chceme se sekerou provést. Zeptá-li se následně jakýkoli objekt sekery na její zápach (tedy typicky ji třeba očichá nějaký hráč), pak zastínění tento dotaz — poslanou zprávu s požadavkem na spuštění funkce query_smell() — jako zákeřný man in the middle odchytí a vrátí tazateli změněný zápach. Objekt sekery přitom vůbec netuší, že navenek smrdí krchovem, a hráč očichávající sekeru zas netuší, že mu zprávu o krchovském smradu nepředala sekera sama. Ukončení změny po určitém čase je pak náramně jednoduché — zastínění jej prostě s ohlasným Plop. vyřadí ze svého seznamu aktivních změn.

Zastínění se stará též o případné skládání a překrývání změn. Pokud po změně zápachu sekery ještě změníme její povrch a krátký popis, uloží si to zastínění do svých vlastností, a bude následně za sekeru odpovídat i na požadavky query_feel() a query_short(). Pokud v době trvání minulé změny podruhé změníme zápach sekery, pak tato druhá změna prostě překryje tu první. Pokud některá z provedených změn dosáhne své doby trvání a má skončit, zastínění si ji vyřadí ze svých záznamů, a pokud vyřadilo všechny změny, zničí se.

Herní využití změněných vlastností[editovat]

Už během testovacího provozu měničského bratrstva se ukázalo, že leckterý hráč se dokáže na poměrně dlouhou dobu zabavit už samotnou skutečností, že může spoluhráči Pepovi změnit zápach z původního nulového (který se pak při očichání projeví jako Necítíš nic.) na Pepa smrdí. V situaci, kdy se změna zápachu provádí nejsnadněji a hráč se prostě snaží získat více měničské zkušenosti, aby mu měnění šlo lépe a aby mohl povýšit v cechu, protože tovaryšské a mistrovské měničské dovednosti přinášejí daleko více herních možností, je to pochopitelné. Přesto se v řadě herních situací dá i samotný příkaz změň využít k zajímavým zásahům do hry. Některé ozkoušené nápady zde proto pro inspiraci uvádíme:

  • Chování hráčů po příchodu do místnosti je mnohdy určováno jejím na první pohled viditelným inventářem. Je-li v místnosti přítomen objekt primárně nebezpečný, většina hráčů jen co nejrychleji proběhne místnost. Chcete-li se tak kupříkladu v okolí Calenhadu vyhnout tomu, aby blížící se hráč otrhal váš oblíbený ostružiník, změňte mu krátký popis třeba na Divočák (Krvelačný skřet, Skalní obr atd.).
  • Pokud máme obavu, že ostružiník, který se teď naším přičiněním tváří jako divočák, by na sebe upozornil tím, že by případného zvědavce, jenž se rozhodl neutéci a posečkat, včas nenapadl, můžeme ostružiníku přiřadit raději krátký popis, který z něho učiní objekt pro jiné hráče nezajímavý — třeba habr nebo skočec — takže kolem něho pravděpodobně prostě projdou, aniž by se na něj podívali.
  • Jiných hráčů se můžeme zbavit nejen tím, že je necháme odejít jinam, nýbrž také tím, že je necháme v místnosti uvíznout a sami odejdeme jinam. Člověk často uvízne u něčeho, co ho zajímá nebo co potřebuje. Co takhle dát šípku rostoucímu u cesty krátký popis Gondorský zlaťák? Jistě, i méně bystrý spoluhráč po čase zjistí, že tenhle „zlaťák“ se nedá ani odnést, a kdyby nezjistil, ozve se po čase Plop! a místo zlaťáku je tu zase šípek — ale autor změny je mezitím za horami, nebo přinejmenším o několik místností dál.
  • Hráče může upoutat i výskyt jiného hráče. Co když místo šípku najednou stojí u cesty hráč Pepa? Nebo dokonce místo vás samotných? Nebo když místo šípku stojíte vy sami? A co když pak u tohoto šípku, který se nyní tváří jako vy sami, zůstanete a stojíte u cesty dvojmo? A co když tam stojíte s Pepou, a najednou přijde pravý Pepa?
  • Měnit jde všechno, na co jde slovem odkázat, tedy při troše šikovnosti i předměty v inventářích jiných hráčů, ba dokonce (některé) místnosti. A dokáže někdo nezasvěcený pochopit, jak se stalo, že i když ještě před chvílí měl přece zánovní pochodeň a zatím ji nezapálil, je najednou „skoro dohořelá“, nebo že i když ještě před chvílí stál na tržišti, nachází se najednou uprostřed pustiny?
  • Opatrní hráči neznámou vodu očichají, než se napijí. Jaké možnosti z toho plynou pro měniče, asi není třeba více rozvádět.

Úkoly[editovat]

Povinné[editovat]

  • Vyzkoušejte si změnu alespoň dvou různých vlastností u dvou různých objektů.
  • Pokuste se vytvořit změnu, kterou se vám podaří aspoň na chvilinku napálit někoho ze spoluhráčů.
  • Cvičte si měnění co nejvíce. S každým dalším — byť nepodařeným — použitím příkazu změň roste vaše měničská zkušenost, a s ní též pravděpodobnost úspěchu a průměrná trvanlivost změny.

Dobrovolné[editovat]

  • Pokuste se u jednoho objektu o změnu několika různých vlastností a poté o několikanásobnou změnu téže vlastnosti.
  • Vyzkoušejte si změnu všech vlastností, které příkaz změň nabízí. Všimněte si, které se mění snáze (s menším úbytkem životních a duševních bodů, s větší pravděpodobností úspěchu), a které naopak hůře.
  • Vemte si dva různé objekty, např. sušenku a jablko: zjistěte vlastnosti jednoho z nich (jak vypadá/voní/zní atd.) a pokuste se postupně změnit vlastnosti druhého na shodné hodnoty.

Pomocné stránky[editovat]

Poznámky[editovat]

  1. Úplně přesně vzato si herní ovladač udržuje seznam všech objektů, které jsou ve hře, a postupně jim dává slovo, takže vždy objekt, který je na řadě, vykoná kousek svého programu.
  2. Pokud funkce nemá žádné parametry, zůstane závorka prázdná.
  3. Abychom odlišili programátorské označení funkcí od programátorského označení vlastností objektu, píšeme za název funkce vždy závorku, do které bychom jindy třeba vepsali nějaké ty parametry.
  4. Kdybychom chtěli být úplně přesní, museli bychom si na tomto místě říci, že MOVE_OK, MOVE_NOT_ALLOWED atd. jsou jen lidské názvy, definičně přiřazené určitým celým číslům, a že návratová hodnota funkce move() je toto celé číslo, a nikoliv nějaké MOVE_OK nebo jiný čitelný text. Zatím si však tuto nepřesnost můžeme dovolit.
  5. Objekt hráče se tu vlastně chová k reálnému hráči, sedícímu u počítače, jako k dalšímu objektu — prostě mu pošle zprávu, stejně jako od něho předtím přijal třeba zprávu očichej odpadkový koš.