Dejan Katašić: FLASH I ACTIONSCRIPT, diplomski rad

5                  Treći primer: Web memorija

Memory blocks je poznata igra uparivanja slika. Igra se sastoji od delova sa slikama. Svaka slika pojavljuje se tačno na dva dela (tako da je broj delova paran). Na početku igre su svi delovi okrenuti na svoja naličja, tako da se ne zna gde se koja slika nalazi. Potez igrača sastoji se u sukcesivnom okretanju dva dela na lice, po igračevom izboru. U slučaju da se na delovima okrenutim na lice nalazi ista slika, nađen je par, delovi se uklanjaju i to se računa kao pogodak, a u slučaju različitih slika, delovi se ponovo okreću na naličje, što se beleži kao promašaj.

Web memorija je varijanta igre predviđena za dva igrača preko Interneta. Umesto slika koriste se slova engleskog alfabeta. U svakom trenutku na potezu je jedan igrač dok drugi čeka na svoj red. Ako igrač na potezu pogodi par, broji mu se pogodak i ima pravo na sledeći potez, a ako promaši – šansu dobija igrač koji je čekao. Kraj igre je kada se upare svi delovi i tada je pobednik igrač sa više uparivanja, ili ako jedan igrač napusti igru (što se računa kao predaja) kada je pobednik igrač koji je ostao u igri.

Slika prikazuje scenu iz igre. Glavni deo površine zauzimaju delovi koje igrači okreću, a sa desne strane prikazane su korisne informacije za igru: ko je na potezu, broj pogodaka oba igrača i statistika (broj pobeda) igrača. Igrač koji igra upisao se pod imenom Player 1 i ima dve pobede i tri okrenuta para, njegov protivnik se predstavio kao Player 2 i pogodio je dva para, na potezu je Player 1 koji je okrenuo delove sa slovima Z i G, što znači da je promašio i da sledeći potez igra Player 2. U gornjem desnom uglu nalazi se “dugme za predaju”.

5.1          Komunikacija s okruženjem

Flash ima dobre mogućnosti za saradnju sa “spoljnim svetom”. Jedna mogućnost jeste instrukcija loadVarijables koja treba da učita vrednosti promenljivih sa zadate lokacije. Ako se kao lokacija zada neka aktivna strana (PHP, ASP i slično) ona može da prosledi generisan sadržaj koji može da zavisi od primljenih parametara. Elegantnija rešenja postižu se korišćenjem objekata XML i XMLSocket.

5.1.1     Objekat XML

Objekat XML omogućava komunikaciju kroz slanje i prijem poruka definisanim u jeziku XML, opšteprihvaćenom standardu za razmenu podataka. Ulazno-izlazne metode objekta su send (slanje XML poruke na određenu adresu), load (učitavanje sa specifirane adrese) i sendAndLoad (slanje poruke na adresu i učitavanje sa adrese u neki XML objekat). Za indikaciju prijema poruke koristi se logička osobina objekta loaded (samo za čitanje – postavlja se na true po kompletiranju prijema). Takođe se može koristiti hendler onLoad (success), koji program poziva po prijemu podataka, za definisanje akcija u zavisnosti od uspeha prijema poruke (hendleru se dodeljuje potrebna funkcija).

5.1.2     Objekat XMLSocket

Kada aplikacija u Flashu zahteva brzi odziv koristi se objekat XMLSocket koji ostvaruje direktnu vezu sa serverom, što omogućava serveru slanje poruka i bez prijema zahteva za slanje od strane klijenta. Server i klijent tada razmenjuju XML poruke korišćenjem full-duplex TCP/IP protokola. Klijent je na server vezan preko određenog porta. Server mora da ima aktivan program zadužen za ovaj port.

Iz sigurnosnih razloga uvedena su određena ograničenja nad ovim objektom. Objekat se može povezati samo na portove sa brojem većim ili jednakim 1024. Drugo ograničenje je da se objekat može konektovati samo na kompjutere istog poddomena gde se nalazi i Flash film fajl koji traži konekciju.

Objekat se kreira svojim konstruktorom, povezuje sa serverom metodom connect, šalje poruke metodom send, diskonektuje sa close. Obrada događaja obezbeđuje se dodelom potrebnih funkcija hendlerima onConnect (šta raditi nakon uspešne ili neuspešne konekcije), onXML (po prijemu poruke) i onClose (pri diskonekciji).

5.1.3     Server

Klijent je na server vezan preko određenog porta. Server mora da ima aktivan program zadužen za ovaj port. Jedno od pogodnih rešenja za ovu serversku aplikaciju jeste korišćenje programskog jezika Java koji poseduje klasu ServerSocket čiji objekti preuzimaju kontrolu nad određenim portom na računaru, kao i mogućnost rada sa više niti (multithread) što omogućava opsluživanje više klijenata u isto vreme.

Kao serverska aplikacija korištene su (autor Derek Clayton) Java klase CSClient (realizuje opsluživanje klijenta) i CommServer (drži port i stara se o klijentima). Server se podiže pozivanjem klase CommServer i parametrom za broj porta. Server primljenu poruku od nekog klijenta prosleđuje svim svojim klijentima. Pri konekciji ili diskonekciji nekog klijenta, server šalje svim klijentima poruku o trenutnom broju klijenata na serveru.

5.2          Tok programa

Za ostvarivanje veze sa serverom korišćenjem objekta XMLSocket neophodno je znati dva podatka, adresu računara servera (može se koristiti DNS ime ili IP broj) i broj porta koji je rezervisan za serversku aplikaciju. Stoga je zgodno ove podatke setovati kao promenljive (konstantne) na samom početku koda klijentske aplikacije u ActionScriptu. Prilikom razvoja može se koristiti računar na kojem se razvija aplikacija, tada se postavlja host = “localhost”, a nakon uspešne realizacije klijenta, pre postavljanja cele aplikacije na server promeniti vrednost promenljive host na stvarnu adresu servera. Uslov za podizanje serverske aplikacije jeste instalirana Java, kako na razvojnom računaru, tako i na računaru servera.

5.2.1     Povezivanje na server

Da bi igrač mogao da igra igru, potrebno je konektovati se na server. Prva stvar koja se očekuje od korisnika jeste proces logovanja. Korisnik u tekstualno polje unosi ime pod kojim se prijavljuje na server i pritiska dugme za konekciju.

Ako je konekcija uspešna, korisnikov program je ostvario vezu sa serverom. Server šalje poruke o trenutnom broju klijenata svaki put kada dođe do promene ovog broja (kada se prijavi novi korisnik ili neki napusti server). Svi klijenti primaju ovu poruku od strane servera i u zavisnosti od stanja u kom se trenutno nalaze, izvode određene akcije. Ako na serveru nije bilo klijenata, korisnik ostvarenom konekcijom postaje prvi klijent i, pošto je Web memorija igra za dva igrača, prelazi u stanje čekanja na drugog igrača. Ako se korisnik loguje kao drugi klijent, to znači da igra može da počne:  drugi klijent šalje poruku kojom se predstavlja, na šta prvi klijent šalje poruku kojom se takođe predstavlja i izmešani niz karaktera koji odgovara karakterima na delovima igre, na osnovu koje se generiše tabela za igranje i prvi igrač dobija pravo na potez. Logovanje trećeg i ostalih klijenata ih tera na napuštanje igre, prelazak u stanje za rekonekciju, dok igrači ove poruke ignorišu. Ovo zato što nije vršena modifikacija klasa u serverskoj Java aplikaciji, koja je pisana kao opštija i sa mogućnošću opsluživanja većeg broja klijenata.

5.2.2     Igra dva igrača

Igrač na potezu pritiskom na izabrani deo okreće” deo (postaje vidljiv pridružen mu karakter). Klijentska aplikacija šalje poruku o okrenutom delu, pa deo postaje vidljiv kod oba igrača. Igrač je i dalje na potezu jer treba da odredi deo za koji pretpostavlja da je par okrenutom delu. Po pritisku na drugi odabrani deo se takođe šalje poruka, drugi deo postaje vidljiv kod oba igrača pa se, nakon kraće pauze koja daje igračima vreme da uoče karaktere na okrenutim delovima, u zavisnosti od uspeha uparivanja okrenutih delova – ako su delovi upareni na potezu ostaje isti igrač, delovi se gasei broji se pogodak, a ako delovi nisu upareni pravo poteza dobija protivnik, a delovi se okreću na naličje.

5.2.3     Pobeda ili poraz?

Nakon uparivanja poslednjeg preostalog para, igrač sa manjim brojem pogodaka se diskonektuje sa servera, briše mu statistika o broju pobeda i nudi mu se mogućnost rekonekcije, dok pobednik, igrač sa većim brojem uparenih delova, povećava za jedan broj svojih pobeda i prelazi u stanje čekanja na protivnika pošto je sam na serveru.

5.3          Poruke

Tokom programa razmenjuju se XML poruke između servera i klijenata. To su u stvari XML dokumenti. Poruke koje se koriste u programu:

·        <NUMCLIENTS>numOfClients</NUMCLIENTS> – poruka koju server šalje pri konekciji i diskonekciji klijenata

·        <SECOND>playerName</SECOND> – poruka drugog igrača, prosleđuje ime

·        <DEALER>playerName</DEALER><ARRAY>startArray</ARRAY> – odgovor prvog igrača drugom, predstavlja se sa playerName, a startArray predstavlja redosled karaktera koji se dodeljuju delovima

·        <FROM>ordNum</FROM><MOVE>pieceID</MOVE> – regularan potez. Šalje se pri okretanju nekog dela. Vrednost ordNum je redni broj igrača koji je okrenuo deo, a pieceID identifikator okrenutog dela.

XML dokument je skup tagova (oznaka) koji mogu imati svoju vrednost, atribute (osobina taga koja može imati vrednost) i druge tagove. Objekat XML ima za cilj da verno predstavi strukturu XML dokumenta, tako da je sam objekat u stvari kolekcija, odnosno niz childNodes, XML objekata, svi tagovi dokumenta reprezentuju se kao XML objekti u okviru ovog niza.

5.4          Program

Za realizaciju je potreban jedan objekat XMLSocket kojim se ostvaruje veza sa serverom. U promenljivim host i port čuvaju se adresa računara servera i port preko kog se ostvaruje konekcija. Zahtev za logovanje, pritiskom na dugme za konekciju, poziva funkciju connect.

5.4.1     Konekcija

function connect () {

      mySocket = new XMLSocket();

      mySocket.onConnect = handleConnect;

      mySocket.onClose = handleClose;

      mySocket.onXML = handleIncoming;

      if (!mySocket.connect (host, port)) {

            gotoAndStop ("notConnected");

      {

}

Kreira se objekat, određuju njegovi hendleri i uspostavlja se konekcija metodom connect. U slučaju neuspešne konekcije ostaje da se pokuša ponovo ili odustane. Ako je konekcija uspela, može se početi sa prijemom i slanjem XML poruka.

5.4.2     Slanje poruke

function sendMessage (message) {

      var messageObj = new XML();

      messageObj.parseXML (message);

      if (mySocket && mySocket.connected) {

            mySocket.send (messageObj);

      } else {

            quit ();

      }

}

Argument funkcije sendMessage je string, tekst XML poruke. Kreira se XML objekat, parsira poruka i šalje putem mySocket na server.

5.4.3     Prijem poruke

Prijem poruke poziva hendler onXML. Tom prilikom pristiže i XML objekat sa servera, koji hendler treba da obradi.

function handleIncoming (messageObj) {

      var leadNode = messageObj.firstChild.nodeName;

      if (leadNode == "NUMCLIENTS") {

//...

Znači, leadNode je naziv vodećeg taga XML dokumenta koji je primljen i na osnovu njega može se tačno odrediti koji tip poruke je primljen. Dalje se prati opisano ponašanje i izvlače potrebni podaci.

messageObj.childNodes [i].firstChild.nodeValue

Ovo je pristup vrednosti i-tog taga primljenog dokumenta.

5.4.4     Zaključavanje i uslovne promenljive

Pošto se igra odvija tako što je u svakom trenutku na potezu tačno jedan igrač, pri čemu drugi čeka na svoj potez, postoji potreba za inplementacijom mehanizma koji obezbeđuje ovakvo ponašanje. U tom smislu, uvedeno je nekoliko logičkih uslovnih promenljivih čije stanje definiše ponašanje u posmatranom trenutku igre.

var myMove, even, locked;    //    boolean

Promenljiva myMove signalizira da li je igrač na potezu ili ne. U svakom trenutku, jedan od igrača ima vrednost ove promenljive postavljenu na true, dok je kod drugog setovana na false. Inicijalno stanje uspostavlja se pri početku partije kada prvoprijavljeni igrač dobija pravo na potez.

Kako se svaki potez igrača u stvari sastoji od okretanja dva dela, promenljiva even vodi računa o parnosti broja okrenutih delova. Pri okretanju svakog dela ova promenljiva negira svoju vrednost. Sledi kod koji se dodeljuje dugmadima u sastavu delova. Vidi se da se kod nalazi u okviru hendlera on i da se kod izvršava po otpuštanju levog dugmeta miša sa dugmeta dela web memorije. Dugme reaguje samo ako se igrač nalazi na potezu (_root.myMove):

on (release) {

      if (_root.myMove) {

            //    statements

      }

}

Ako je igrač na potezu, ispituje se da li je odigrani broj poteza bio paran ili ne i u zavisnosti od toga izvode određene akcije. (sledeći kod nalazi se na mestu gorenavedenog komentara) Ako je even:

if (_root.even) {

      _root.newMove (this.row, this.column);

      value._visible = true;

      _parent.el = this;

      _root.even = false;

}

Funkcija newMove generiše sadržaj poruke FROM:MOVE i prosleđuje ga na slanje u sendMessage. Naredna linija koda uključuje karakter posmatranog dela tako da igrač vidi koji znak je dodeljen delu. Osobina el objekta memory, koji sadrži sve delove pa se odavde i vidi kao _parent objekat, pokazuje se na posmatrani deo, što će biti od koristi pri okretanju drugog dela i upoređivanja vrednosti dodeljenih im karaktera. Na kraju se even postavlja na false, jer odigran je neparni potez. Ako pak nije even (else grana prethodnog if):

if (!_root.locked && (this != _parent.el)) {

      _root.newMove (this.row, this.column);

      value._visible = true;

      _root.locked = true;

      attachMovie ("waiter", "waiter", 432);

}

Ako nije zaključano (treća uslovna promenljiva, locked, objašnjena je u nastavku) i ako je pritisnuti deo različit od prethodno pritisnutog dela (koji se pamti u promenljivoj el), takođe se šalje poruka i uključuje karakter pritisnutog dela. Potom se vrši zaključavanje setovanjem locked na true i kontrola se predaje movieClipu waiter. Ovaj element pravi vremensku pauzu jer se sastoji od nekoliko praznih frejmova pre frejma u kome se nalazi kod koji izvodi dalja ispitivanja. U ovom trenutku, pri kačenju klipa waiter, onemogućena je svaka interakcija (igrač koji nije na potezu ima myMove na false, igrač na potezu ima even na false i locked na true). Pre nego što vidimo o čemu se stara waiter, da vidimo kako se obrađuju pristigle poruke o odigranom potezu. Kod je iz funkcije handleIncoming koja je pridružena hendleru onXML objekta mySocket. Kada poruka nije ni NUMCLIENTS, ni SECOND, ni DEALER:

if (userOrdNum != messageObj.firstChild.firstChild.nodeValue) {

      //    statements

}

Ako je poruka od protivnika, radi se sledeće (umesto statements):

var el = memory ["e"+messageObj.childNodes[1].firstChild.nodeValue];

el.value._visible = true;

if (even) {

      memory.el = el;

      even = false;

} else {

      locked = true;

      el.attachMovie ("waiter", "waiter", 432);

}

U lokalnu promenljivu el smešta se deo koji odgovara delu koji je protivnik okrenuo (elementi objekta memory nazvani su “e” + vrstaDela + kolonaDela, što određuje poziciju dela na polju igre, imena počinju sa “e” da bi mogli biti identifikatori), njegov karakter postaje vidljiv igraču i, slično kao i kod protivnika, ispituje se da li je broj poteza bio paran ili ne. Takođe i preduzima potpuno iste akcije: ako jeste paran, postavlja memory.el i negira even, a ako nije – zaključava i kači waiter.

Objekat waiter kači se na drugi okrenuti deo jer sadrži u _parent referencu na njega:

var e0 = _parent._parent.el;

var e1 = _parent;

Ispituje se da li je postignut pogodak:

if (e1.value.value == e0.value.value) {

      e1._visible = false;

      e0._visible = false;

      if (_root.myMove) {

            _root.myHit++;

      } else {

            _root.opponentHit++;

      }

      _root.left--;

      if (_root.left == 0) {

            if (_root.myHit < _root.opponentHit) {

                  _root.quit ();

            } else {

                  _root.wins++;

            }

      }

}

Ako su vrednosti karaktera dva okrenuta dela jednaki, postignut je pogodak. Delovi se isključuju i treba brojati pogodak. Ako je igrač bio na potezu, to je njegov pogodak (myHit++), inače je pogodak protivnika (opponentHit++). Promenljiva left drži informaciju o preostalom broju parova koje treba pogoditi i, kako je postignut pogodak, umanjuje se za jedan. Ako je pogođeni par ujedno i poslednji, donosi se odluka o pobedniku (u igri je 30 delova, odnosno 15 parova, što garantuje mogućnost odluke o pobedi igrača sa većim brojem pogodaka). Ako je broj pogodaka igrača manji od pogodaka protivnika, on je izgubio pa se diskonektuje, inače je pobedio i broji pobedu više.

Inače, ako se vrednosti karaktera dva okrenuta dela ne podudaraju:

e1.value._visible = false;

e0.value._visible = false;

_root.myMove = !_root.myMove;

_root.updatePlayer ();

Gase se karakteri okrenutih delova, menja se igrač koji je na potezu, updatePlayer postavlja ispis imena igrača na potezu.

Posle svega, još malo administracije:

_root.even = true;

_root.locked = false;

this.removeMovieClip ();

Odigran je paran broj poteza, uklanja se zaključavanje igre, a waiter je odradio posao i može se ukloniti.


 

vrh strane