Egyáltalában
nem mindegy, hogy egy kalandjátékban miféle ütemhez igazítva telik-múlik
az idő. A legegyszerűbb megoldás, különösen az egyszereplős kalandjátékokra
leginkább jellemző, hogy mindig egy-egy sikeres parancs végrehajtása
lépteti tovább az egész rendszert egyetlen időegységgel, mintha
valamiféle, változékony hosszúságú órajelet adna neki ezzel. Ameddig
gépelünk, a következő mondatunkat szerkesztjük, addig a játékban
áll az idő – mihelyst azonban leütjük az ENTER-t (és be is írtunk
neki valamit), egycsapásra mindenki öregebbé válik, mondjuk egy
negyed órával. Tulajdonképpen nem is időhöz, hanem lépésszámhoz
vannak szinkronizálva egy ilyen játék történései – pl. olyan időzítéseket
alkalmaznak bennük, hogy – mondjuk egy kopogtatást követően –
öt lépés múlva kinyitja nekünk az ajtót valaki. Tetszőleges ideig
lehet töprengeni a pillanatnyi helyzet megoldása fölött, és hiába
ugrik a nyakunkba egy vérszomjas démon, nyugodtan elmehetünk megvacsorázni,
mielőtt végképp az arcunkra fagyna az az utolsó vigyor… A program
belső számlálóit, amelyek az eljövendő események titkos előhírnökei,
a végrehajtott utasítások csökkentik rendszeresen eggyel. És ez
nagyon helyesen van így! Egy elsősorban képzeletre és gondolkodásra
épülő játéknál ez egy ideális helyzet – tökéletesen megengedhető
és elfogadható, hogy ki-ki a saját belső ritmusa szerint haladjon
előre a feladatok megoldásában. (Hogy is nézne ki, ha egy gyakorlott
gyors- és gépíró illetéktelen előnyre tenne szert a lassúkezűvel
szemben…)
Csakhogy
egy többszereplős kalandjátékban már egyszerűen tarthatatlanná
válik ez az állapot. Ha a program mondjuk hat különböző játékos
parancsait fogadja párhuzamosan, akik mind eltérő ritmus szerint
gépelnek, akkor mégis melyikükhöz alkalmazkodjon a többi? Amikor
az egyik semmit nem csinál, hanem csak karbatett kézzel és összeráncolt
homlokkal bámulja a képernyőt, akkor is kötelessége a programnak,
hogy pontosan a történések idejében haladéktalanul értesítse őt
a másik lépéseiről, ha az éppen akkor halad el mellette, vagy
ugyanazon a helyszínen tevékenykedik. Az efféle programokban eszerint
nem tehetünk mást: valós idejű időzítéseket vagyunk kénytelenek
alkalmazni bennük. Ez viszont maga után vonja, hogy a bevitel
és kiírás funkcióját az eddigieknél sokkal élesebben el kell határolnunk
egymástól – mert mi történjék, hogyha véletlenül éppen akkor érkezik
valakihez egy fontos kiírandó üzenet, amikor ő saját mondata szerkesztésének
a kellős közepénél tart; esetleg föl sem pillantva a képernyőre,
keresi a megfelelő billentyűt? Nem lehet csak úgy otrombán kettévágni
a félig begépelt mondatot, és a közepébe belenyomtatni a szöveget!
(De lehet: a TELNET-es játékok sajnos pontosan ezt teszik…) Okvetlenül
kétfelé kell bontanunk a képernyőt, úgy, hogy külön legyen egy
felület a kiírás és szintén külön a bevitel számára (utóbbinak
két-három sor is elég lesz). Így azok már nem zavarják egymást
– vagy mégis?! Mit tehetünk akkor, ha a megjelenítés mezejében
egy különösen hosszú leírás kezd el szépen, megfontoltan kibontakozni
az ismeretlenség homályából, mialatt mi szorgosan a parancsunkat
gépeljük? Bevett gyakorlat, hogyha egy szöveg hosszabb annál,
mint amennyi a képernyőre egy adagban kifér, akkor oldalanként
meg-megszakítva, minden oldal végén egy billentyű-lenyomásra várakozva
fokozatosan léptetjük azt tovább (<More> vagy magyarul <Tovább>
funkció). De még ha ez az eset nem is forog fenn, akkor is az
ablak görgetése több másodpercig is eltarthat, és rendkívül illúzióromboló
lenne, ha erre az időre hirtelen megakadna az alsó sorokban a
bevitel. Másik probléma: mi van, ha a kiírás-ablakban egy félig
megjelenített szöveg éppen ENTER-rel való továbbléptetésre vár,
de mi nem törődünk vele, és zavartalanul csak a mondatunkra figyelünk
– hol várakozzon addig a szöveg hátralevő része, és ha kiírás
közben újabb üzenetek érkeznek, azokat miképpen várakoztassuk?
Meg kell oldanunk tehát azt is, hogy a kétféle funkció ne csak
térben, de időben is egymástól teljesen független és párhuzamos
legyen: mialatt gépelünk, tényleg aközben folyjon odaát
a kiírás! A még kiíratlan, de a küldőtől már átvett „szűz” szöveget
pedig addig is egy átmeneti pufferban kell tárolnunk, ahol egy
bizonyos határig gyűlhetnek és halmozódhatnak a sorok és a mondatok,
de ha a puffer betelt, akkor haladéktalanul ki kell görgetnünk
őket a képernyőre – akár tetszik a felhasználónak, akár nem. Különösen
mókás tud lenni, amikor a szereplőt a játékban egy súlyos baleset
érte, de ő még valahol tíz oldallal följebb tart a szövegek olvasásában,
miközben a többiek már réges-régen értesültek róla, hogy ájultan
hever a földön, és apránként kipakolják a hátizsákjából az értékesebb
cuccokat…
Egyetlen
megoldás létezik a legsimább párhuzamosság elérésére, az, ha a
program központi, vezérlő része sohasem „ragad le” valamilyen
szubrutinnál, hanem egy örökös végtelen ciklusban megállás nélkül
kering három alapvető tevékenység: a beviteli mező szerkesztése,
az események végrehajtása (beleértve természetesen a saját és
a többiek által kiadott utasítások végrehajtásait is) és a szövegkiírás
alapvető fázisai közt. Ehhez az szükséges, hogy valamennyi funkciót
apró, szétválasztható és önállóan végrehajtható kis lépésekre
tagoltan valósítsuk meg. A szövegkiírás esetében ilyen építőkocka
lehet pl. az átmeneti puffer egyetlen sorának kiléptetése a képernyőablakba,
vagy a beviteli rutin esetében egyetlen lenyomott billentyű beolvasása
a billentyűzet-pufferből (ha van olyan), és annak megfelelően
a beviteli mező módosítása. A program tehát úgy fog működni, hogy
folyton figyeli, történnie kell-e valamilyen eseménynek, s ha
igen, akkor végrehajtja azt, és a neki megfelelő szöveget a puffer
alsó végéhez hozzácsapja (ez egyetlen szemvillanás alatt megvan);
majd rögtön továbbadja a vezérlést a kiíró rutinnak, amelyik a
szövegpuffer legfelső sorát kigörgeti a képre (hacsak nem üres
a puffer éppen akkor); ezt követően a beviteli szubrutin lép színre,
mely pedig egyetlen karaktert beolvas, ha képes; végezetül pedig
vissza az elejére, és ez így megy tovább megállás nélkül… Nem
kis feladat elérni, hogy ez az egész így egyben kellőképp összehangoltan
fusson – különösen, hogyha grafikus képernyőt használunk a megjelenítéshez,
ami már eleve alaposan lelassítja a kiírásokat. (Ajánlott az ASSEMBLY
nyelv használata például.)
Többek
között emiatt is számít, hogy mind az értelmező szubrutin, mind
pedig a program más egyéb végrehajtó eljárásai igen-igen serényen
végezzék a dolgukat – mert miért ne fordulhatna elő, hogy húsz
különböző játékos egyszerre adja ki, egyenként hatszáz betűből
és harminc parancsból álló mondatait? Amiből mindjárt egy másik
szempont is következik, nevezetesen hogy az egyes játékosok teendőit
is hasonló pufferokban kell előzetesen nyilvántartani – elvégre
mindnyájan kiadhatnak több parancsból álló mondatokat is, amiből
egyelőre még csak az első vagy a második hajtódik végre, amelyeket
addig is tárolni kell valahol, de ő gonosz mosollyal az ajkán
tüstént begépeli máris a következő adagot, és lehet, hogy ezt
egyszerre akár többen is megteszik…
És
ne gondoljuk azt sem, hogy mindezek a szörnyűségek kizárólag hálózatos
kalandjátékokban fordulhatnak elő! Ha egy olyan kalandot készítünk,
amiben ugyanazt az egyetlen számítógépet használó játékos több
szereplőt is irányíthat egymással párhuzamosan, mondjuk ESC-pel
vagy TAB-bal kapcsolgatva az egyik vagy a másik között (esetleg
osztott képernyőn megjelenítve egymás mellett egyidejűleg akár
többet is), miközben valós idejű megjelenítést alkalmaztunk benne,
az pontosan ugyanakkora galibákat teremthet, mintha ezren küldözgetnék
az utasításaikat a komputernek egyszerre! De megéri a belefektetett
munkát a dolog, mert rendkívül látványos lesz a végeredmény.
Ezáltal
tehát elértük, hogy – elméletileg – akárhány játékos ténykedését
le tudjuk kezelni egyidejűleg, miközben sem a végrehajtás, sem
a folyamatosan a háttérben zajló szövegkiírás nem zavarja egymást,
és főképpen pedig a bevitel mindeközben zökkenőmentes marad. De
nyitva maradt még egy probléma: a különböző játékosok különböző
sebessége. Ha valaki gépír, mint a villám, annak a parancsai is
fokozott ütemben hajtódnak végre, amire még ráfoghatjuk, hogy
megérdemli ezt a kis előnyt, még ha a többiek nemhogy reagálni
rá, de még csak figyelemmel követni se nagyon bírják közben; de
ha egy több parancsból álló mondatot írt be valaki – erre ugyebár
egy lassúbb illető is képes –, akkor már igazán tisztességtelenül
manőverező vágtázásba kezdenek az utasításai – ennek egymást követő
lépéseit a program mindenfajta késleltetés nélkül, teljesen egybefolyva,
mondhatni szinte egyszerre vágja a többiek képébe. Fokozott mértékben
áll ez a számítógép által irányított szereplőkre (ún. NPC-k: ez
a „Non Player Character” – „nem játékos szereplő” – angol nyelvű
kifejezés rövidítése): őnáluk tudniillik abszolúte semmiféle gépírásról
nincsen szó – az illető fickó cselekvési szándékai a másodperc
törtrésze alatt alakulnak ki egy külön e célra berendezett „műhelyben”.
Ennek eredményeképpen aztán ezek olyan követhetetlen ámokfutásba
kezdenek, hogy szinte látni se nagyon fogják őket a többiek, amint
nagyritkán föltünedeznek egy-két tizedmásodpercre itt-ott… A megoldás
kézenfekvő: minden egyes élőlénynek – függetlenül attól, valódi-e
vagy NPC – osszunk ki valamilyen maximális sebességértéket, amivel
haladhat. Ez pl. úgy néz ki, hogy mindenkihez tartozik egy-egy
számláló, melyeket bizonyos időközönként csökkentünk, s ha nullára
csökkent, akkor következik lépésre az az illető. Ezeket egytől
néhány másodpercig terjedő tartamokra célszerű beállítani, miáltal
mindenkinek személyre szabott sebessége lehet – bizonyos játékosokat
vagy NPC-ket tetszőlegesen fölgyorsíthatunk vagy lelassíthatunk,
amivel a helyzetek nehézségét is lehet némiképpen állítani. Az
említett értékeket akár dinamikusan is változtathatjuk: pl. minél
jobban meg van pakolva súlyos tárgyakkal valaki vagy minél fáradtabb
és kimerültebb, annál jobban lelassul a mozgása stb.