tinr skripta predavanj 4

20
TINR @ FRI, draft v09, Peter Peer ++ 51 Poglavje #4 (igralnost) (Bojan Klemenc++) Aktualne škatlice: oblikovanje igralnosti o igralnost poudarek predvsem na tem delu, ker o splošnih zadevah glede programiranja igralnosti ni veliko za povedat (razen kar je o sami arhitekturi objektov igre) arhitektura igre o arhitektura objektov igre 1. pravila igre o splošna pravila igre o pravila integrirana v objekte igre shranjevanje (str. 118119, 304307) o serializacija 1. Igralnost Poskus opredelitve s sintezo opredelitev različnih avtorjev: Igralnost (gameplay, playability) je interakcija z igro preko njenih pravil, povezava med igralci znotraj igre, izzivi in reševanje leteh, zgodba in vpletenost igralca v zgodbo [Wikipedia10]. Igralnost je subjektivna in odraža doživljanje igre. Pravila igre in mehanika igre (game mechanics): Mehanika igre je potek igre iz nekega začetnega stanja z interakcijo med pravili igre in akcijami igralca. Posledično igralec doživlja igro: igralnost. [de.wikipedia10]

Upload: luka-kozuh

Post on 14-Apr-2016

231 views

Category:

Documents


2 download

DESCRIPTION

games

TRANSCRIPT

Page 1: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   51  

Poglavje #4 (igralnost)                         

(Bojan Klemenc++) 

Aktualne škatlice: 

 

• oblikovanje igralnosti  o igralnost  

poudarek predvsem na tem delu, ker o splošnih zadevah glede programiranja igralnosti ni veliko za povedat (razen kar je o sami arhitekturi objektov igre) 

• arhitektura igre o arhitektura objektov igre 

1. pravila igre o splošna pravila igre  o pravila integrirana v objekte igre 

• shranjevanje  (str. 118‐119, 304‐307) o serializacija 

1. Igralnost 

Poskus opredelitve s sintezo opredelitev različnih avtorjev:  Igralnost (gameplay, playability) je interakcija z igro preko njenih pravil, povezava med igralci znotraj igre, izzivi in reševanje le‐teh, zgodba in vpletenost igralca v zgodbo [Wikipedia10]. Igralnost je subjektivna in odraža doživljanje igre.  Pravila igre in mehanika igre (game mechanics): Mehanika igre je potek igre iz nekega začetnega stanja z interakcijo med pravili igre in akcijami igralca. Posledično igralec doživlja igro: igralnost. [de.wikipedia10]  

Page 2: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   52  

Primer: (Gospodarska) simulacija (Construction and management simulation) obravnava neko gospodarsko panogo z določenega zgodovinskega ozadja, iz česar izhaja začetno stanje igre (Transport Tycoon: simulacija logističnega podjetja od začetka 20. st. naprej). Pravila igre simulirajo gospodarstvo – proizvodnja, ponudba, povpraševanje, finančni tokovi. Te vrednosti se spreminjajo v odvisnosti od časa, morebitni računalniško vodeni nasprotniki reagirajo na delovanje igralca – to je mehanika igre v ožjem smislu. Igralnost pa je poleg mehanike še subjektivno ovrednotena izkušnja, ki jo ima igralec med igranjem – sámo zaznavanje poteka igre, pogled na zemljevid in statistike, ki so rezultat njegovega igranja.  Še dve zanimivi opredelitvi: »Igra je zaporedje zanimivih izbir« (»a series of interesting choices«) ‐ Sid Meier »Igralnost je eden ali več zaporednih vzročno‐posledičnih izzivov v simuliranem okolju« (»One or more causally linked series of challenges in a simulated environment«) ‐ International Game Developers Association  Igralnost lahko opazujemo (in merimo) z več vidikov [González10]:  Zadovoljstvo: stopnja zadovoljstva označuje igralčev občutek užitka pri igranju igre v celoti ali kakšnega njenega dela: mehanika, grafika, zgodba ipd. Zadovoljstvo je precej subjektivno in težko merljivo.  Učenje: omogoča razumeti in obvladovati sistem in mehaniko igre. V primerjavi z uporabniškimi programi, kjer si želimo čim manj učenja in čim bolj preprosto uporabo, je lahko pri igrah količina in hitrost učenja odvisna od vrste igre: igralca lahko že na začetku igre z intenzivnim treniranjem naučimo obvladovati igro ali pa ga postopoma naučimo po potrebi. Lahko tudi predpostavimo, da ima igralec določene sposobnosti še preden je začel igrati igro.  Učinkovitost: Kako dobro igra izkoristi čas in vire, da se igralec zabava medtem, ko poskuša doseči cilje igre – kako dobro igra pritegne pozornost igralca od začetka do konca. Zato moramo dobro porazdeliti naloge (oziroma izzive).  Zatopljenost: Kako dobro se igralec zatopi v navidezni svet. Igralec postane del navideznega sveta. Da igralec ostane zatopljen v igro, je potrebno poskrbeti za dobro razmerje med izzivi in veščinami, ki so potrebne, da premaga izzive.  Kako povečati zatopljenost: 

• izkoristi radovednost igralca • nekateri odgovori na skrivnosti so lahko dvoumni • igralcu ponudi različne možnosti in nove svetove • igralcu pokaži kuliso (in ne tistega, ki jo upravlja) • igralcu pokaži stvari, ki ga zanimajo in tako ohrani zanimanje do igre • ustvari ritem • princip nedovršenosti, um naj zapolni manjkajoče • prikaži stvari v večjem ali manjšem obsegu od pričakovanega • strahospoštovanje • domišljija • zanimive zgodbe in liki • zvok (nevidenega). 

 

Page 3: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   53  

Motivacija: Kako pripraviti igralca, da izvrši neka dejanja, oziroma vztraja pri njihovem izvajanju dokler je potrebno. Tako je potrebno poskrbeti, da se igralec osredotoči na izzive, naloge morajo imeti smisel, nagraditi je potrebno napredek znotraj igre.  Sistem nagrad: 

• nagrade za dosežke • nagrade prihajajo v ciklih po 15 min • nagrade lahko ustvarijo ritem • nagrade se dopolnjujejo • nagrade za naloge, ki naj jih igralec izkusi • zbirke manjših nagrad vodijo do velikih nagrad. 

 Čustva: pomagajo, da ime igralec čim boljše izkušnje pri igranju igre. Z uporabo izzivov, zgodbe, situacij, estetskega izgleda, glasbe itd. lahko izzovemo različna čustvena stanja, kot na primer veselje, strah, radovednost, žalost. V kratkem času lahko igranje vzbudi različna čustva, ki se v resničnem življenju ali ne bi pojavila ali pa pojavila v tako kratkem času.  Družabnost: V kakšnem obsegu igra spodbuja družabnost (pri igranju) in njen vpliv na doživljanje igre. Igralci lahko rešujejo naloge skupaj ali pa tekmujejo, pri tem se tvorijo nova razmerja, lahko pa se tudi obstoječa razmerja v resničnem svetu preslikajo v svet igre. Za spodbujanje družabnosti lahko v igro vključimo izzive, ki so skupni več igralcem, jih tako združujejo in motivirajo, da skupaj premagajo izzive.  Družabno doživetje: družabne igre → igre na osebnih računalnikih → splet Za družabno doživetje fizična prisotnost v okolici ni več nujno potrebna (povezava je lahko tudi samo preko točkovanja izidov – lestvica).  Smernice: 

• ustvari priložnosti za tekmovanje • ustvari priložnosti za sodelovanje • omogoči igralcem pokazati svoje spretnosti • omogoči igralcem individualnost in razkazovanje značaja • dodaj orodja za komunikacijo in izmenjavo • omogoči občutek mentorstva in občutka zadovoljstva ob uspehu drugih • omogoči ljudem, da gledajo. 

 Pregled pojmov, ki so pomembni za igralnost in na kaj moramo biti pozorni:  Cilji/naloge (objectives): Cilji znotraj igre so zahteve, ki jih je potrebno izpolniti, da dosežemo določen izid. Vzpostavljajo konflikte in izzive, motivirajo igralca s končnim naborom rešljivih problemov. V osnovi dajo igralcu možnost, da nekaj počne. Pri igrah je pomembna pot do cilja oziroma, da se igralec ob tem zabava.  Izidi (outcomes): Igre imajo množico možnih izidov (konec igre), ki so posledica igralčevih dejanj (interakcije z igralcem). Biti morajo merljivi – igralec ugotavlja svojo uspešnost na podlagi merljivih izidov. Nekateri izidi so boljši, nekateri slabši; brez merjenja izidov lahko pridemo do situacije, ko ne vemo, kdo je zmagal. Najbolj osnovna izida sta zmaga in poraz, tretji pogost izid pa je neodločeno.  Negotovost (uncertainty): 

Page 4: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   54  

Od igre pričakujemo, da so izidi negotovi. V nasprotnem primeru igramo igro, v kateri je izid vnaprej določen ne glede na to, kako igramo, in igra postane nezanimiva. Igralci želijo v igri nekaj doseči, vendar pa mora obstajati možnost, da jim ne bo uspelo.  Ciljna publika (target audience): Glavno pravilo: nikoli ne poskušaj zadovoljiti vseh igralcev, ker z veliko verjetnostjo ne boš zadovoljil nobenega. Potrebno je identificirati ciljno skupino in ugotoviti njihove želje. Pri tem si lahko pomagamo tako, da si zamislimo in na kratko opišemo profile tipičnih uporabnikov ciljne skupine (tipični igralni čas, obstoječe igre, ki jih igrajo ipd.).  Obvladovanje igre:  Za večino igralcev je smisel igre uporabiti spretnosti in znanje, da zmagamo (»obrnemo«) igro. Poleg same želje po zmagi je pomemben občutek »kako dobri smo« (in, da to lahko pokažemo drugim). Poraz je v tem primeru sprejemljiv, saj nam je zmaga izziv. Pomemben je občutek nadzora – če nimamo nadzora in izgubimo ali zmagamo, ni zabavno.  Nekaj nasvetov: 

• igra naj preizkusi sposobnosti igralca • preizkusimo lahko več sposobnosti naenkrat • zmanjšati je potrebno vpliv sreče • ponuditi igralcem vabljive nagrade (v igri seveda) • pred pomembne cilje je potrebno postaviti večje ovire • ponuditi igralcem možnost, da tvegajo • ponuditi več ciljev naenkrat, da lahko igralec izoblikuje strategijo obdelovanja ciljev • ponuditi igralcu, da izbere stopnjo tveganja • če je možno, navidezno povečati občutek težavnosti (dejanska težavnost je lahko manjša). 

 Princip »škarje – kamen – papir«: Cilj: preprečiti, da igralec z eno strategijo dominira igro ter s tem povzroči, da so vse izbire trivialne. Primer: pogosto se uporablja v strategijah – pešak s kopjem premaga konjenika, konjenik premaga lokostrelca, lokostrelec premaga pešaka.   Težava: kako narediti, da ovire ne bodo pretežke in povzročale frustracije? 

 Ostati moramo v idealnem področju.  Težavnost: 

Page 5: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   55  

Igra ne sme biti pretežka ali prelahka. Ker vsi igralci niso enako spretni, jim lahko ponudimo možnost, da sami izberejo tako težavnost, da lahko še vedno uživajo v igri. Dejavniki, ki jih lahko spreminjamo: 

• čas (hitrejši ali počasnejši tek igre) • število nasprotnikov • značilnosti nasprotnikov (počasnejši, šibkejši nasprotniki, omejena UI) • količina virov • porazdelitev in gostota objektov za povrnitev ali nadgradnjo moči (power‐ups). 

Dejavniki se lahko spreminjajo tudi samodejno – tako se težavnost lahko prilagaja igralčevim sposobnostim.  Informacije: Koliko informacij damo igralcu oziroma koliko jih skrijemo. Informacije zmanjšujejo negotovost. Igre uporabljajo pristope glede na to, koliko informacij nudijo igralcu (poker, šah). Kadar je igra preveč predvidljiva, bi lahko po nepotrebnem (...???) dodali naključje, vendar pa v večini primerov zadošča, da igralcu ne ponudimo določenih informacij (omejeno vidno polje pri strategijah (fog of war)).   Kratkoročni spomin igralca: Kratkoročni spomin lahko naenkrat hrani (in hkrati obdeluje) 7±2 podatka. Če želimo, da si igralec nekaj zapomni, potem se držimo spodnje meje, če želimo, da pozabi, potem gremo višje od te meje. Vendar pa moramo paziti, da igralca ne zasujemo s podatki, saj lahko povzročimo frustracije.   2. Arhitektura objektov igre  Kompozicija in agregacija (2. poglavje / 3. predavanje):   je to ponovitev ...??? dodatek z več primeri  Problemi hierarhije objektov [Chandy09]: Imamo hierarhijo razredov: 

 Želimo dodati možnost animiranja. Nekateri objekti se bodo samo izrisali, nekateri bodo tudi animirani, nekateri bodo simulirani in animirani, nekateri samo simulirani:  

Page 6: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   56  

 Možna rešitev z večkratnim dedovanjem: 

 Kaj pa, če se pojavi zahteva, da se območje prožilca (trigger) izriše na ekranu (drawable)?  Še en primer iz konkretne igre: »Redeemer« iz Unreal Tournamenta – kako realizirati: kot izstrelek, ki ga igralec lahko krmili; kot kamero, ki lahko »ustreli« (ubije) nasprotnika?  Rešitev: kompozicija  Uporabnost kompozicije: Primer naknadne rekonstrukcije hierarhije razredov (z večkratnim dedovanjem) iz igre, kjer se je uporabil model kompozicije: 

Page 7: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   57  

 Iz slike je razvidno, da je uporaba kompozicije omogoča precej veliko kompleksnost – če bi modelirali s hierarhijo razredov, postane model precej nepregleden, težaven za spreminjanje in dopolnjevanje.  Zgradba objektov s pomočjo komponent:  

 

Page 8: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   58  

Predstavitev v obliki mreže: objekti so vodoravno (na primer igralec), navpično komponente (igralec ima komponente pozicija, premikanje, izris, skript in fiziko).  obrazložitev ...???  Vzorci načrtovanja objektov (design patterns):  Vzorci načrtovanja objektov podajajo rešitve problemov in določenih situacij, ki se pogosto pojavijo pri razvoju programske opreme. Ker rešujejo probleme na višjem abstraktnem nivoju, se jih navadno ne da dobiti v obliki knjižnic. Oglejmo si nekaj najpomembnejših vzorcev z vidika iger:  Edinec (Singleton):  Problem: Potrebujemo natanko eno instanco razreda, ki je dostopna celotnemu programu, globalno. Rešitev (predstavljena z UML razrednim diagramom):  

 Implementacija ima navadno eno točko za ustvarjanje in dostop, kar nam daje potrebno kontrolo nad instanco znotraj celotnega programa. Uporaba v igrah: Predvsem strojni viri ‐ grafične naprave, zvočne naprave, upravljalci pomnilnika, upravljalci datotek, mrežni vmesniki. Vendar pa se določene naprave lahko pojavijo večkrat, npr. igralne palice, pomnilniške kartice. Slednje implementiramo kot navadne objekte. Poleg strojnih virov lahko ta vzorec uporabimo recimo za dnevnik, opozorila, čakalno vrsto za sporočila, ali celo objekt, ki predstavlja samo igro. Posledice: Lahko se pojavi veliko globalnih spremenljivk (objektov; z njimi imamo le boljši nadzor nad ustvarjanjem in uničenjem le‐teh v primerjavi s spremenljivkami), modularnost in preglednost kode je manjša, zato, če se le da, se poskušamo temu vzorcu izogniti.  Primer: public class MemoryManager { private static final MemoryManager INSTANCE = new MemoryManager(); // Private constructor prevents instantiation from other classes private MemoryManager() { } public static MemoryManager getInstance() { return INSTANCE; } } Uporaba: MemoryManager mm = MemoryManager.getInstance(); (Lepše bi bilo, če bi iz imena razreda bilo jasno, da gre za Singleton.)  XNI primer: .h: @interface TouchPanel : NSObject {     ... } ... 

Page 9: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   59  

+ (TouchPanel*) getInstance; @end  .m: @implementation TouchPanel static TouchPanel *instance; ‐ (id) init {   self = [super init];   if (self != nil) {     ...    }   return self; } + (void) initialize {   instance = [[TouchPanel alloc] init]; } + (TouchPanel*) getInstance {     return instance; } @end 

Tovarna objektov (Object Factory):  Problem: Včasih želimo ustvariti objekt razreda, ki ga (torej njegovega tipa) še ne poznamo v trenutku prevajanja programa. Tip določimo šele v času izvajanja. Rešitev:  

 Namesto ustvarjanja instanc z »new«, imamo tovarno objektov, ki ji sporočimo preko klica metode z ustreznim parametrom, kateri objekt naj ustvari in tovarna ustvari in vrne ta objekt. Uporaba v igrah: Tovarna objektov ima ključno vlogo pri podatkovno vodenem razvoju. Npr. pri nalaganju stopnje z vsemi objekti nimamo metode, ki eksplicitno pokliče »new« za vsak objekt, ampak preberemo datoteko, ki vsebuje seznam vseh potrebnih objektov, jih identificiramo in pokličemo tovarno, da ustvari objekt tega tipa.  Posledice: Primer #1: enum GameObjectType { PLAYER, ENEMY,

Page 10: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   60  

POWERUP, WEAPON, // ... rest of the objects... } GameObject * ObjectFactory::Create(GameObjectType type) { switch (type) {

case PLAYER: return new Player(); case ENEMY: return new Enemy(); case POWERUP: return new PowerupO; case WEAPON: return new Weapon();

} return NULL; }  Pomanjkljivost takšne tovarne je, da mora vedeti vnaprej, kakšne objekte sploh lahko ustvari. To lahko odpravimo tako, da se vsak razred, ki se želi ustvariti s pomočjo tovarne, registrira. Registracija vsebuje unikatno identifikacijsko številko in specificiran način za ustvarjanje takšnega objekta.  Primer #2 (ima omenjeno pomanjkljivost?): public class ImageReaderFactory { public static ImageReader getImageReader(InputStream is) { int imageType = determineImageType(is); switch(imageType) { case ImageReaderFactory.GIF: return new GifReader(is); case ImageReaderFactory.JPEG: return new JpegReader(is); // etc. } } }  Ta vzorec je zelo uporaben tudi za produciranje nove vsebine za igre, ki so že na prodajnih policah.  Opazovalec (Observer):  Problem: Objekti (različnih tipov) želijo vedeti, ko se nekaj zgodi, ali ko se spremeni stanje drugih objektov (pri čemer so objekti čim manj soodvisni (coupling)). Rešitev: Imamo opazovanca in več opazovalcev. Opazovalci se registrirajo pri opazovancu, ki jih doda v seznam. Ob primernem dogodku opazovanec kliče metodo notify() (ali update()) pri vseh opazovalcih (message passing, event driven interaction ...???). 

 

Page 11: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   61  

Uporaba v igrah: Interakcija med objekti. Vrata recimo želijo biti obveščena, da je igralec pritisnil primeren gumb in se posledično lahko odprejo. Obratno bi lahko gumb eksplicitno klical funkcijo za odpiranje vrat, vendar je potem vedno potrebno razmišljati, kaj vse je potrebno poklicati, ko se pritisne gumb. V drugi smeri se kot opazovalci gumba prijavijo samo tisti objekti, ki se jih sprememba stanja gumba tiče. Še dva primera: Kadar je določen objekt uničen, morajo vsi nasprotniki, ki so imeli ta objekt naciljan, spremeniti cilj, saj objekt ne obstaja več. Če recimo na grafični kartici spremenimo ločljivost, potem je smiselno o tem obvestiti vse objekte, ki imajo nek grafični pomnilniški vmesnik (buffer), da se ustrezno posodobijo. Posledice: Ta vzorec je dobra rešitev za večino interakcij med objekti ob praktično ničelni stopnji odvisnosti med objekti, vendar je potrebna previdnost pri učinkovitosti in porabi spomina. Če se dogodki pojavljajo večkrat na sličico (frame) ali pa je seznam opazovalcev dolg, lahko hitro pridemo so slabše učinkovitosti. Dostop do nepovezanih objektov pa je dodatno še nenaklonjen do zapisa v predpomnilniku (cache). Problem pri dolgih seznamih lahko rešimo tako, da se vsak opazovalec registrira samo za določen dogodek opazovanca (eden recimo za vsak gib, drugi pa le za uničenje). Če imamo na tisoče opazovancev in pravtako opazovalcev, potem lahko zahteva po spominu eskalira preko razumnih meja, saj za vsako vozlišče v kazalčnem seznamu potrebujemo dva kazalca (na naslednje vozlišče in na opazovalca ...???).  XNI primer: Event *orientationChanged;…  (Pri Observerju je treba še definirati linked list nad opazovancem (Subject), ki potem ob dogodku kliče opazovalca (Observer) oz. njegovo update/notify metodo. To že imaš implementirano? Dogodek je le odskočna deska za ta design pattern. …???)  V sami igri sicer zaenkrat še nimamo tega (implementation under way ;‐). Podoben primer imamo ob dogodku positionChanged, ki se sproži vsakič, ko se spremeni vektor lokacije na objektu. To recimo posluša ABT drevo in po potrebi premakne objekt iz ene celice v drugo. 

Kompozitum (Composite):  Problem: Skupino objektov želimo obravnavati kot en objekt. Rešitev: Lahko naredimo nov objekt, ki je zbirka objektov (združi objekte). Nov objekt vsebuje vse metode, ki jih vsebuje navaden objekt (ni obravnavan kot poseben primer).  

 (Leaf je le specifična komponenta.) 

Page 12: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   62  

Uporaba v igrah: Pogosto, na primer v meniju imamo gumbe z napisi, vsakega lahko premikamo po ekranu, lahko pa jih premikamo kot skupino. Pri tem omenimo, da imamo recimo v meniju tudi tekstovne panele in slike, ki skupaj z gumbi gradijo uporabniški vmesnik (naš Component na zgornji sliki), vendar v tem primeru ne tvorijo kompozituma z gumbi. Kompozitum se v veliki meri uporablja tudi nad entitetami igre ter vozlišči grafa scene. Posledice: Za vse metode v dedovanem razredu (slednje morajo biti virtualne) moraš ročno spisati kodo in narediti novo implementacijo, ki bo iterirala čez vse združene objekte, nad katerimi bo uveljavljala izbrano operacijo.  Primer: /** "Component" */ interface Graphic { //Prints the graphic. public void print(); } /** "Composite" */ class CompositeGraphic implements Graphic { //Collection of child graphics. private List<Graphic> mChildGraphics = new ArrayList<Graphic>(); //Prints the graphic. public void print() { for (Graphic graphic : mChildGraphics) { graphic.print(); } } //Adds the graphic to the composition. public void add(Graphic graphic) { mChildGraphics.add(graphic); } //Removes the graphic from the composition. public void remove(Graphic graphic) { mChildGraphics.remove(graphic); } } /** "Leaf" */ class Ellipse implements Graphic { //Prints the graphic. public void print() { System.out.println("Ellipse"); } } /** Client */ public class Program { public static void main(String[] args) { //Initialize four ellipses Ellipse ellipse1 = new Ellipse(); Ellipse ellipse2 = new Ellipse(); Ellipse ellipse3 = new Ellipse(); Ellipse ellipse4 = new Ellipse(); //Initialize three composite graphics CompositeGraphic graphic = new CompositeGraphic(); CompositeGraphic graphic1 = new CompositeGraphic(); CompositeGraphic graphic2 = new CompositeGraphic(); //Composes the graphics graphic1.add(ellipse1); graphic1.add(ellipse2); graphic1.add(ellipse3);

Page 13: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   63  

graphic2.add(ellipse4); graphic.add(graphic1); graphic.add(graphic2); //Prints the complete graphic (four times the string "Ellipse"). graphic.print(); } }  Dodatni popularni vzorci:  Uporaba spodaj naštetih vzorcev načeloma ni splošno aplikativna na vseh področjih. Nekateri vzorci so tako bolj primerni za višje nivoje kode igre kot za pogone, nekateri so uporabni pri izračunih fizike, a ne pri UI.  Dekorater (decorator):   Omogoča dodajanje nove funkcionalnosti obstoječim objektom med izvajanjem programa (brez, da bi spremenili druge objekte).  Primer: 

  Začnemo z navadnim oknom, ki mu želimo dodati drsnike. Razred Window nima te možnosti zato lahko dedujemo nov razred (ScrollingWindow), ki mu dodamo to funkcionalnost. Druga možnost je, da naredimo ScrollingWindowDecorator, ki doda to funkcionalnost obstoječim objektom tipa Window. Obe rešitvi sta v tem trenutku enakovredni.  Če bi oknu želeli dodati še obrobo, bi morali narediti dva nova podrazreda, enega, ki deduje od Window in enega, ki deduje od ScrollingWindow. Za vsako funkcionalnost se ta problem še poslabša. Če pa uporabimo dekorator, samo ustvarimo BorderedWindowDecorator in dekoriramo obstoječa okna z BorderedWindowDecorator ali ScrollingWindowDecorator, obema skupaj ali nobenim.  Fasada (facade):   Združi kompleksno množico vmesnikov z veliko objekti in funkcijami v en enotni vmesnik.  slika ...???  Primer [wikipedia10: Facade_pattern]: zagon računalnika (ni potrebno vedeti, kako se posamezna komponenta zažene). /* Complex parts */ class CPU {

Page 14: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   64  

public void freeze() { ... } public void jump(long position) { ... } public void execute() { ... } } class Memory { public void load(long position, byte[] data) { ... } } class HardDrive { public byte[] read(long lba, int size) { ... } } /* Facade */ class Computer { private CPU cpu; private Memory memory; private HardDrive hardDrive; public Computer() { this.cpu = new CPU(); this.memory = new Memory(); this.hardDrive = new HardDrive(); } public void startComputer() { cpu.freeze(); memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE)); cpu.jump(BOOT_ADDRESS); cpu.execute(); } } /* Client */ class You { public static void main(String[] args) { Computer facade = new Computer(); facade.startComputer(); } }  Obiskovalec (visitor):   Loči algoritem (operacijo) od množice podatkov, ki jih obdeluje. Tako omogoča nove operacije nad množico objektov, brez, da spremenimo obstoječo kodo (samo napišemo novega obiskovalca). V obiskovanih objektih je potreben povraten klic na obiskovalca.  Primer: 

Page 15: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   65  

  class CarElementPrintVisitor implements CarElementVisitor { public void visit(Wheel wheel) { System.out.println("Visiting "+ wheel.getName() + " wheel"); } public void visit(Engine engine) { System.out.println("Visiting engine"); } public void visit(Body body) { System.out.println("Visiting body"); } public void visit(Car car) { System.out.println("Visiting car"); } } class CarElementDoVisitor implements CarElementVisitor { public void visit(Wheel wheel) { System.out.println("Kicking my "+ wheel.getName() + " wheel"); } public void visit(Engine engine) { System.out.println("Starting my engine"); } public void visit(Body body) { System.out.println("Moving my body"); } public void visit(Car car) { System.out.println("Starting my car"); } } public class VisitorDemo { static public void main(String[] args){ Car car = new Car(); car.accept(new CarElementPrintVisitor()); car.accept(new CarElementDoVisitor()); } }  

Page 16: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   66  

Adapter:   Omogoča, da razredi, ki drugače ne morejo komunicirati med sabo, lahko komunicirajo.  Primer: 

  "Client" kliče "methodA()" na "Adaptee", vendar "Adaptee" te metode nima, zato naredimo vmesni razred "Adaptor", ki ima metodo "methodA()", ki jo lahko kliče "Client" in znotraj te metode pokliče "methodB()", ki jo ima "Adaptee".  Lahki objekti (Flyweight):   Omogočajo deljenje pomnilnika pri večjem številu manjših objektov.  Primer: Grafična predstavitev črke – vsaka črka lahko vsebuje podatke o njenih merah, pisavi, barvi itd. Vendar imajo v sestavku besedila posamezne črke pogosto vse naštete parametre enake, zato jih lahko hranimo samo enkrat na črko, za posamezno črko iz besedila pa hranimo samo še na primer pozicijo.  Ukaz (command):  Vzorec enkapsulira (učinkovito povzame in zapiše) zahtevo ali spročilo, ki ga lahko nato shranimo, posredujemo med objekti ipd.  Primer: ...???  Vzorci ustvarjanja (creation ...???): tovarna objektov, edinec.  Vzorci strukture (structure ...???): kompozitum, adapter, dekorater, fasada, lahki objekti.  Vzorci obnašanja (delovanja) (behaviour ...???): ukaz, opazovalec, obiskovalec.  3. Pravila igre  

• splošna pravila igre  o začetno stanje  

Page 17: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   67  

o končni pogoji  • pravila integrirana v objekte igre  

o razdelitev pravil, ki se neodvisno tičejo določenih objektov igre  

Začetno stanje igre (initial state) se vzpostavi ob zagonu instance igre (npr. 0 točk, vsa življenja ipd.). Iz tega stanja se (ob interakciji uporabnika) s pomočjo pravil spreminja stanje igre.  Končni pogoji (end conditions) določajo stanje igre, ki pomeni spremembo igralnega načina, dosežen cilj (ali nezmožnost doseči cilj), konec instance igre ali seanse [Björk05]. Tipičen primer doseženega končnega pogoja je, ko igralec izgubi vsa življenja. Pobrati določeno število zastav je končni pogoj igralnega načina, kjer igralci zbirajo zastave nasprotnikov.  Pravila integriramo v objekte s pomočjo agregacije (glej arhitekturo objektov igre zgoraj).  Splošna pravila igre (game rules) in pravila objektov igre (item rules):  Pravila objektov se nanašajo na posamezen objekt igre. Pri njihovem obravnavanju se sprehodimo čez vse objekte in za vsakega posebej obravnavamo pravila. Koda za izvajanje pravila je lahko integrirana v objekt kot metoda komponente.  Simulator v enem obhodu zanke izvede iteracijo čez vse objekte igre. Na vsakem objektu se obdelajo pravila objektov.   Splošna pravila igre se preverjajo enkrat na simulacijski korak (oziroma enkrat za vsako stanje igre).  

   Primer obravnavanja pravil za igro Pong [Jan09?]:  public class PongRules : Part {   int itemUpdateOrder;   int globalUpdateOrder;      public PongRules(int itemUpdateOrder, int globalUpdateOrder) { 

Page 18: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   68  

    this.itemUpdateOrder = itemUpdateOrder;     this.globalUpdateOrder = globalUpdateOrder;   }      public override void Install(Item parent) {     base.Install(parent);     parent.As<ISceneIterator>().RegisterUpdateItemMethod(itemUpdateOrder, ItemRules);     parent.As<Simulator>().RegisterUpdateMethod(globalUpdateOrder, GlobalRules);   }   // ITEM RULES   void ItemRules(float dt, Item item) {     // Ball     Ball b = item.As<Ball>();     if (b != null) {       Particle p = b.As<Particle>();       if (Math.Abs(p.Position.X) > 450) {         int winner = (int)(‐Math.Sign(p.Position.X) * 0.5f + 0.5f);         Pong.Score[winner]++;         b.Reset();       }     }   }   // GLOBAL RULES   void GlobalRules(float dt) {     if (Pong.Score[0] > 9 || Pong.Score[1] > 9){       Pong.Score[0] = 0;       Pong.Score[1] = 0;     }   } }  ...??? razlaga  4. Shranjevanje  Shranjevanje oziroma posnetek trenutnega stanja igre. Glavni razlogi za uporabo shranjevanja so: 

• omogočajo prekinitev igranja in kasnejše nadaljevanje (tudi igričarji imajo življenje izven igre) • zavarujejo trenutno stanje igre (in napredovanje igralca) pred morebitnim kasnejšim 

sesutjem igre • omogočajo preigravanje različnih potekov igre na točkah, kjer se igralcu ponudi več 

alternativnih poti/možnosti.  Iz igralskega vidika ločimo naslednje načine shranjevanja: 

• omejeno shranjevanje: končno število shranjevanj ali pa posebne lokacije shranjevanja (npr. Final Fantasy) 

• kontrolne točke (checkpoints): avtomatsko shranjevanje na ključnih točkah v okolju • neomejeno: kjerkoli, kadarkoli, kolikokratkoli (npr. Civilization) • samodejno shranjevanje (autosave): periodično ali po ključnih dogodkih (primerjaj: 

kontrolne točke) • vezano na profil: samodejno shranjevanje vezano na profil igralca (npr. Diablo). 

 Neomejeno shranjevanje lahko spremeni težavnost igre. Pri samodejnem shranjevanju in kontrolnih točkah je pomembno, da ne shranimo igralca v slabem stanju oziroma brezizhodni situaciji.  

Page 19: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   69  

Predvsem pri neomejenem shranjevanju (pri strateških igrah) lahko igralec zlorabi sistem shranjevanja tako, da shranjuje in nalaga igro dokler rezultat, ki je odvisen od naključja, ni v igralčevo korist. Problem se lahko reši z determinističnem obnašanju po nalaganju igre ‐ npr. vsakič, ko naložimo igro bo rezultat boja enak (razen, če ne spremenimo strategije).  Kako spraviti stanje igre v obliko, ki jo lahko shranimo na disk? Potrebno je shraniti trenutno stanje objektov v zaporedno obliko (zaporedje bajtov), ki jo lahko zapišemo na disk ‐ serializacija. Najboljše je, da vsak objekt sam poskrbi za svojo serializacijo. Tako se v trenutku, ko želimo igro shraniti, samo sprehodimo preko objektov, ki jih želimo shraniti in pokličemo njihove metode za serializiranje (podamo jim tudi podatkovni tok, kamor bodo shranili svoje stanje).  Vrste serializacije: 

• tekstovna  • binarna serializacija  • xml serializacija. 

 ...??? serializacija / deserializacija – shranjevanje / nalaganje  Primer serializacije kamere:  bool GameCamera::Write(IStream &stream) const {   //očetovski razred poskrbi za serializacijo pozicije, rotacije,…   bool bSuccess = GameEntity::Write(stream);   //serializacija osnovnih podatkovnih tipov   bSuccess &= WriteFloat(stream, m_FOV);   bSuccess &= WriteFloat(stream, m_NearPlane);   bSuccess &= WriteFloat(stream, m_FarPlane);   //serializacija objekta   bSuccess &= m_lens.Write(stream); }  Osnovne podatkovne tipe shranimo neposredno v podatkovnih tok, na objektih pa samo kličemo njihove metode za serializacijo. Na tak način zapišemo samo podatke brez metapodatkov, kar je hitro, vendar pa vsaka manjša sprememba v strukturi razreda lahko povzroči, da se shranjene igre ne morejo več naložiti. Zato lahko implementiramo še počasnejšo metodo, ki shrani podatke v tekstovni obliki in opremi z metapodatki.  Problem kazalcev: Zaradi dinamičnega dodeljevanja pomnilnika kazalci ob naslednjem nalaganju igre ne bodo kazali na prave pomnilniške lokacije. Težavo lahko rešimo, da se na druge objekte sklicujemo preko njihovih enoličnih identifikatorjev (UID ...???). Lahko tudi neposredno shranimo kazalce, vendar mora potem tudi vsak objekt shraniti svoj naslov. Pri nalaganju se objekti ponovno kreirajo na novih naslov in ustvari se translacijska tabela, ki vsebuje star in nov naslov. Ko so vsi objekti naloženi, se vsi kazalci naenkrat popravijo.  Dodatni viri:  [Chandy09] Marcin Chady. Theory and Practice of Game Object Component Architecture. Game Developers Conference Canada. 2009.  

Page 20: TINR Skripta Predavanj 4

TINR @ FRI, draft v09, Peter Peer ++   70  

[González10] González Sánchez, J. L. Jugabilidad. Caracterización de la Experiencia del Jugador en Videojuegos. PhD Thesis (2010)   [Wikipedia10] ...???   [de.wikipedia10] ...???  [wikipedia10] ...??? #2  [wikipedia10: Facade_pattern] ...???  [Björk05] Staffan Björk, Jussi Holopainen. Patterns in game design, Cengage Learning, 2005.  [Jan09?] Matej Jan. Tečaj XNA, 2009.  Naloga na vajah:  

• Delo na elementih igralnosti: Specializacija objektov igre, da se obnašajo po pravilih igre, ter preverjanje pravil na nivoju stopnje in seje igre (gameplay session). Glede na spisana pravila v konceptu igralnosti je potrebno naredit, da se vse dogaja kot je predvideno (razen UI elementov seveda): vpeljava končnih pogojev igre, dodajanje atributov igre (npr. rezultat, življenja).  (Lestvica najboljših in shranjevanje bo šele, ko pridemo do menijev po UI. Enako velja za nalaganje stopnje iz datotek.)