Spânzurătoarea (II)

Am văzut în articolul precedent cum facem un joc. Dar, am arătat doar principiile de bază. De data aceasta vom face în așa fel încât jocul să arate puțin mai bine. Vom trece la o interfață grafică (una simplistă). Nu trebuie să vă așteptați la o "operă de artă"; jocul nostru va arăta cam așa:

Screen Shot 2015-06-25 at 19.35.36E un screenshot pe MacOS. În Windows ar arăta altfel... Nu ne interesează deocamdată foarte mult acest aspect. Vom face ceva de genul: în dreapta avem spânzurătoarea și cuvântul sub ia, iar în dreapta butoane care permit alegerea literelor.

Nu vom mai avea mesaje care să ceară introducerea literei; e destul de evident cum adăugăm o literă. De asemenea, în locul mesajelor care anunță dacă am câștigat sau am pierdut vom avea casete de dialog. Vor arăta cam așa (pe un Mac).

Screen Shot 2015-06-25 at 19.43.15

Reproiectarea

Vrem să avem acum un joc cu grafică. Vom avea nevoie de o fereastră. Prin intermediul acesteia utilizatorul va introduce datele de intrare (apâsând pe litere). Acestea trebuie să ajungă la controller. În fereastră vor fi afișate și informațiile despre starea jocului; view-ul va fi responsabil pentru manipularea acestora.

Fereastra

În Java avem la dispoziția clasa JFrame pentru ferestre (folosim Swing). O vom extinde pentru a crea fereastra pentru jocul nostru. Nu ne interesează deocamdată aspectul estetic, deci vom avea o fereastră simplistă. Dimensiunile sale vor fi alese arbitrar (să arate cât de cât bine), nu va fi redimesionabilă și o vom împărți într-o grilă. Această grilă va conține o imagine pentru spânzurătoare (un JLabel pentru care a fost setat un icon), un text pentru cuvânt (un alt JLabelpentru care este setat un text) și o grilă de butoane (obiecte JButton) pentru alegerea literelor.

Va trebui să putem transmite controller-ului ce butoane au fost apăsate. Ar trebui fie să avem o referință către controller și să îi comunicăm că a fost apăsat un buton când are loc o astfel de acțiune, fie să permitem controller-ului să acceseze informația respectivă comunicând ferestrei că este interesat de ea. Am ales a doua variantă; fereastra are o serie de listener-i care sunt  notificați în momentul în care este apăsat un buton. Vom avea metode pentru a adăuga și elimina listener-i. Controller-ul va fi un astfel de listener. Trebuie să definim o interfață simplă pentru acești listener-i:

Așadar, de fiecare dată când va fi apăsată o literă, va fi apelată metoda letterClicked a controller-ului. Acesta va trebui să implementeze interfața HangmanFrameListener.

Fereastra va trebui să aibă și metoda care permit modificarea imaginii și a cuvîntului. Pentru imagine vom folosi o dimensiune fixă (150 × 150 pixeli). Pentru cuvânt vom încerca să umplem o zonă a ecranului indiferent cât de lung este cuvântul; implementarea va fi simplistă: vom alege cea mai mare mărime de font care "încape". Din nou, din punct de vedere estetic nu este cea mai bună soluție...

Acestea fiind spuse, iată implementarea ferestrei:

În linia 51 suntem "șmecheri"; folosim o expresie lambda (deci jocul nostru are nevoie de Java 8).

Noul controller

Vom crea un nou controller; nu va mai fi bazat pe interacțiunea cu intrarea standard, ci cu fereastra noastră. Va avea așadar o referință spre fereastră.

Controller-ul trebuie să se înregistreze ca listener al ferestrei și va fi informat de fiecare dată când va fi apăsată o literă. Când are loc o astfel de acțiune, va face operațiunile pe care controller-ul vechi le făcea când era introdusă o literă de la tastatură: va cere modelului să adauge litera (și eventual o nouă parte a corpului dacă litera nu există în cuvânt), va cere view-ului să afișeze noua stare a jocului și apoi va cere modelului să verifice dacă s-a încheiat jocul; dacă jocul s-a încheiat, va afișa un mesaj în funcție de cine este câștigătorul și va începe un nou joc. Nu mai este nevoie să întrebăm dacă se dorește începerea unui joc nou; va începe automat altul; dacă utilizatorul nu mai vrea să se joace, poate închide fereastra în orice moment.

Noua implementare este următoarea:

Observăm că lucrează cu aceleași interfețe pentru model și view; implementarea acestora nu este relevantă pentru controller.

Noul model

Nu ne atingem deloc de model. Logica jocului este aceeași...

Noul view

Vom crea un nou view care să lucreze cu fereastra noastră, nu cu ieșirea standard. Pentru afișarea spânzurătorii vom genera numele fișierului care conține imaginea ce corespunde numărului părților corpului care trebuie afișate și vom folosi metoda ferestrei care afișează imaginea.

Pentru cuvânt trebuie doar să apelăm metoda ferestrei care afișează cuvântul.

Pentru un mesaj, generăm o simplă casetă de dialog care afișează mesajul respectiv. Aceasta va fi modală; cum astfel de mesaje sunt afișate doar când se încheie jocul, un joc nou nu va începe decât după ce utilizatorul va apăsa butonul Ok.

Noua implementare este următoarea:

Noul program principal

Vom avea pe lângă model, view și controller o fereastră. Interfețele vor fi aceleași, dar vom alege noile implementări.

Va urma

Am ajuns acum la o variantă grafică a jocului. Dar, parcă am vrea să ne jucăm și pe mobil, nu? În epidosul următor vom trece la varianta Android.