Joc de labirint Tutorial

  



    Lecția de azi e una specială, căci vei învăța să faci singur un joc. Un joc întreg, de la cap la coadă, pornind de la zero.

    Vei vedea în lecția de azi pas cu pas cum am gândit jocul și cum l-am realizat.

    În esență sunt necesari doar doi pași și trei mișcări. Serios. 

   Voi detalia toate aceste etape pe un joc pe care îl vom aduce împreună de la stadiul de idee la stadiul de realizare practică. Este vorba de un joc de labirint, în care scopul va fi să deplasezi (cu ajutorul unor taste) un omuleț printr-un spațiu cu trasee libere și pereți până la o anumită destinație.

    Am zis că pentru a realiza acest joc e nevoie de doar doi pași și trei mișcări. Și am vorbit serios. Hai să vedem care ar fi aceștia.

    În primul rând, cei doi pași.


        Pasul 1 — proiectarea


    Primul pas în realizarea unui joc este proiectarea lui. Nu are sens să te apuci direct de partea de implementare atâta timp cât nu știi foarte clar ce vrei să obții și cum anume.

    Imaginea aceea de programator care se așazăla calculator și se apucă direct să programeze e doar de spectacol.    

    Toți programatorii (buni) își fac înainte un plan. (Chiar dacă, poate, în unele cazuri acest plan este făcut doar mental și cu o viteză atât de mare încât pare că omul acela scrie direct programul fără să-l fi gândit în prealabil.)

    În cadrul acestui prim pas (care este extrem de important) voi detalia ulterior cele trei mișcări esențiale pentru realizarea lui.


 


        Pasul 2 — programarea


    Abia după ce ai dedicat programului o bună cantitate de gândire și ai îndeplinit cu succes cele trei mișcări din cadrul pasului de proiectare e cazul să te apuci de programarea propriu-zisă.

    În funcție de cât de bine ai îndeplinit primul pas (pasul de proiectare), pasul al doilea (pasul de programare) va fi mai mult sau mai puțîn o formalitate. Scrierea unui program bine gândit în prealabil este o sarcina foarte ușoară.

    Nu este exclus, însă, nici că în cadrul acestei etape să iasă la iveală unele lucruri care au scăpat în faza de proiect, caz în care va trebui să revii la pasul anterior și să regândești programul ținând cont de noile date dobândite.

    Prin urmare, pasul de proiectare este unul de o importanță covârșitoare în realizarea unui program de calculator (în general, și a unui joc, în particular). El te va urma într-o formă sau altă pe toată perioada realizării jocului.

    Așa că haide să detaliem pasul de proiectare a jocului și să vedem care sunt cele trei mișcări necesare pentru realizarea lui. Le voi și exemplifica practic pe jocul nostru de labirint.


    Mișcarea 1 — specificațiile



    Bun, vreau să fac un joc. De unde încep?

    În primul rând trebuie să stabilesc cât mai clar cu putință ce vreau să fac. Mai exact, trebuie să definesc interfață grafică și comportamentul programului.

    Așa că primul lucru pe care îl fac atunci când mă apuc să construiesc un joc este să îi desenez pe o foaie interfața, adică modul cum vreau să arate el pe ecran.

    De asemenea, pe lângă această interfață trebuie să specific comportamentul, și în special modul cum vreau că interfața grafică să răspundă la comenzile utilizatorului (adică la apăsarea unor taste).

    În cazul jocului nostru de labirint interfață dorită este cea din imaginea alăturată. În această imagine punctul verde este omulețul (sau marioneta pe care o vom plimba prin labirint), punctele albe sunt traseele libere pe care omulețul se poate deplasa, punctele negre sunt pereții labirintului, iar punctul roșu este destinația unde omulețul trebuie să ajungă.

    Cât privește comportamentul programului, acesta este următorul. La pornirea jocului se va desena interfața grafică, adică se vor desena pereții și se vor plasa omulețul și destinația așa cum am arătat în imagine. Apoi, la fiecare apăsare a vreuneia din tastele 'a' (stânga), 's' (jos), 'd' (dreapta) sau 'w' (sus) omulețul se va deplasa cu o poziție în direcția indicată. Deplasarea aceasta se va face doar dacă în direcția respectivă nu este perete sau marginea ecranului nostru de 10×10 puncte. În momentul în care omulețul ajunge la destinație jocul se va încheia (adică programul nu va mai răspunde la apăsarea tastelor, ecranul se va șterge și se va afișa un zâmbet albastru (și anume cel pe care l-ai văzut reprezentat în prima imagine din acest articol)).


    Mișcarea 2 — datele


    Odată ce am realizat mișcarea anterioară (deci odată ce știu cum vreau ca jocul să arate și să se comporte), următoarea etapă în realizarea lui constă în a stabili ce variabile îmi trebuiesc în spatele lui.

    Cu alte cuvinte, trebuie să analizez rezultatele etapei anterioare și să decid ce elemente din interfață și din comportamentul programului necesită memorarea unor date (care eventual trebuie să poată fi modificate pe parcursul derulării jocului).

    Aceasta este o etapă foarte importantă (și pe care cel mai probabil va trebui să o antrenezi o vreme pentru a ajunge să o realizezi cu ușurință), căci în cadrul acestei etape practic abstractizezi jocul. Pornești de la concret, de la cum vrei să arate și să se comporte jocul, și ajungi să scoți din el niște numere (fixe (constante) sau variabile) care îi modelează complet atât felul cum arată pe ecran, cât și modul în care se comportă.

    Dacă mișcarea anterioară a presupus desene și text, în cadrul mișcării curente cuvântul cheie este numărul. (Pitagora spunea că orice lucru poate fi redus la un număr. Dacă te uiți la muzică ta (șiruri de numere în format MP3) sau la cărțile tale (șiruri de numere în format PDF) cu siguranță vei fi tentat să îi dai dreptate. Dacă te vei uită, însă, la inteligență și la procesele complexe ce se produc în creierul tău vei realiza că dezbaterea asta e departe de a fi încheiată.)

    Practic, pentru că un joc să poată fi realizat pe calculator trebuie să îndeplinească condiția de a putea fi modelat în întregime cu ajutorul numerelor. Iar etapa curentă are rolul de a obține tocmai acest model pe baza de numere al jocului.

    Hai să vedem concret prin ce numere am putea să modelăm jocul nostru de labirint.

    Avem în joc un omuleț care pe parcursul jocului se poate găși în diverse poziții din labirint. Prin urmare, trebuie să avem niște variabile în care să îi memorăm poziția curentă. Să denumim aceste variabile ox și oy. Așadar, (ox, oy) vor reprezenta coordonatele omulețului pe ecranul de 10×10 puncte.

    De asemenea, avem în labirint o poziție specială — destinația unde trebuie să ajungă omulețul. O putem memora în alte două variabile, dx și dy. (De remarcat aici faptul că această destinație nu se schimbă pe parcursul jocului (așa că dx și dy vor fi utilizate pe post de constante (și nu de variabile, așa cum le zice numele)).)

    În plus, pereții și traseele au o configurație specială, și anume cea arătată în figura de la etapă anterioară. Este necesar să avem un mijloc de a putea spune dacă un anumit punct de pe ecran este traseu liber sau este perete al labirintului. Putem face asta cel mai facil cu ajutorul unei matrici. În această matrice (să o denumim, de exemplu, mp (de la matricea pereților)) va trebui să avem elemente pentru fiecare punct de pe ecran și în fiecare astfel de element în parte vom putea memora valoarea 1 pentru elementele ce corespund unor puncte unde se găsesc pereți, și valoarea 0 pentru elemente ce corespund unor puncte unde se găsește traseu liber.

    În mod normal ar fi suficientă în acest sens o matrice, mp, de 10 linii și 10 coloane. Cum, însă, numerotarea liniilor și a coloanelor începe nu de la 1, ci de la 0, am ales să folosesc (pentru sporirea clarității programului rezultat) o matrice de 11×11 în care prima linie (linia cu indicele 0) și prima coloană (coloana 0) le voi lăsa neutilizate. (Da, voi face risipă de memorie în scop didactic .)


    OK, deci am o matrice mp în care elementul mp[i][j] este 1 pentru perete și 0 pentru traseu liber. Dar i și j de aici sunt indici de linie și respectiv de coloană, și nu coordonate pe ecran. Reamintesc faptul că originea sistemului de coordonate ale punctelor de pe ecran este în colțul stânga jos (cu x crescând de la 1 cu câte o unitate spre dreapta, și cu y crescând de la 1 cu câte o unitate în sus), iar originea indicilor de linie și de coloana pentru o matrice este în colțul din stânga sus (cu indicele de linie (i) crescând cu câte o unitate în jos, și cu indicele de coloană (j) crescând cu câte o unitate la dreapta).

    Prin urmare, în cadrul programului va trebui să pot face ușor translatarea de la coordonatele de pe ecran la coordonatele din matrice. De exemplu, punctului (1, 1) de pe ecran îi corespunde elementul [10][1] din matrice (în condițiile în care, așa cum am spus, linia 0 și coloana 0 din matrice le las neutilizate). Similar, elementul [2][9] din matrice corespunde punctului (9, 9) de pe ecran.

    Cum pot face translatarea aceasta? Pentru coordonată de pe axa x e simplu, căci această practic va coincide cu indicele de coloană din matricea mp. Pentru coordonata de pe axa y este un pic mai dificil, căci y variază de jos în sus, în vreme ce indicele de linie, i, variază de sus în jos. Acest sens de variație diferit îmi spune că în expresia lui i y va apărea cu semnul minus (și, de asemenea, în expresia lui y i va apărea cu semnul minus).

    Hai să pornim de la punctul (1, 1) de pe ecran. Pentru acest punct, y = 1. Lui îi corespunde din matricea mp elementul [10][1]. Deci i = 10.

    Mai întâi vreau să obțin expresia lui y în funcție de i. Știu până aici doar că în această expresie apare termenul -i. Adică până aici am doar y = -i… . Adică 1 = -10… . Este clar, deci, cum trebuie să completez expresia pentru a avea egalitate. 1 = -10+11. Adică y = 11-i. Expresia se verifică pentru toate punctele de la (1, 1) la (1, 10).

    Pot obține expresia lui i în funcție de y rearanjând expresia anterioară. Rezultatul va fi i = 11-y.

    Așadar, dacă vreau să aprind pe ecran punctul ce corespunde elementului mp[i][j], voi aprinde punctul (j, 11-i). Iar dacă vreau să verific dacă e perete în punctul (x, y), voi testa valoarea elementului mp[11-y][x].

    În plus față de variabilele discutate până aici,  vom avea nevoie și de niște variabile pe care să le folosim pe post de indici cu care să parcurgem matricea mp. Aceste variabile nu fac parte efectiv din modelul jocului, ci sunt variabile auxiliare, ce ne sunt necesare că instrumente în implementare. Le voi denumi l (de la linie) și c (de la coloană). (Ele vor corespunde lui i și j din discuția anterioară.)


    Mișcarea 3 — schița


    Odată ajunși în punctul acesta, suntem gata să începem schițarea programului. Scopul nu este să obținem direct codul sursă pe care îl vom scrie în simulator, ci o schița generală, care să ne arate într-un limbaj natural ce va conține programul.

    Pentru jocul nostru de labirint schița aceasta ar putea fi următoarea:

        -> Definire variabile (ox, oy, dx, dy, mp, l, c)

        -> Inițializări (definire trasee și pereți (mp), poziție destinație (dx, dy), poziție omuleț (ox, oy))

        -> Desenare interfață (desenare pereți, desenare destinație, desenare omuleț)

        -> Definire funcție taste

    Dacă la primele trei puncte din această schiță cred că este destul de clar ce e de făcut, ultimul dintre ele necesită detaliere. Asta voi face în continuare.

        -> Definire funcție taste

        –> dacă s-a apăsat vreo tasta de direcție, atunci încearcă deplasarea în direcția aia (folosind două noi variabile auxiliare locale, oxnou și oynou, și având grijă să nu se iasă în afară ecranului)

        –> dacă la noua poziție nu e perete, atunci deplasează omulețul acolo

        –> dacă omulețul e la destinație, atunci încheie jocul și afișează zâmbet albastru

    Gata! Abia după ce am ajuns în punctul asta e cazul să ne punem problema scrierii codului sursă al programului. Ce urmează nu este chiar departe de a fi o simplă formalitate odată ce ajungi să stăpânești bine fundamentele programării. Dificultatea semnificativă în constuirea unui joc o constituie îndeplinirea cu succes a acestor trei mișcări pe care le-am discutat aici.


    Mai jos voi prezenta programul pentru jocul de labirint, așa cum a rezultat în urmă etapelor exemplificate în acest articol. Ți-aș recomanda ca înainte de a-l citi să îți scrii tu propriul program și doar atunci când întâmpini dificultăți să te inspiri din exemplul meu. În multe locuri din acest program puteam să fac lucrurile în alt mod (și uneori chiar mai eficient decât am făcut-o). Așa că te-aș ruga să nu citești programul de mai jos cu evlavia datorată unui text sfânt, ci să-l folosești doar ca termen de comparație pentru programul tău :-). Nu va fi un exercițiu ușor pentru ține, dar rezultatele vor fi spectaculoase și îți vei mulțumi că ai făcut-o.


// ——————–

// | Joc de labirint |

// ——————–


// –> Definire variabile:


// omulețul

var ox

var oy


// destinația

var dx

var dy


// traseul și pereții

var mp = Matrice(11, 11)


// variabile ajutătoare

var l

var c


// –> Inițializări:


// omulețul

ox = 1

oy = 1


// destinația

dx = 10

dy = 10


// traseul și pereții

l = 1

while (l<=10)

{

  c = 1

  while (c<=10)

  {

    mp[l][c] = 0

    c = c+1

  }

  l = l+1

}

mp[1][1]=1; mp[2][1]=1; mp[3][1]=1; mp[4][1]=1;

mp[5][1]=1; mp[6][1]=1; mp[7][1]=1; mp[8][1]=1;

mp[9][1]=1; mp[2][2]=1; mp[6][2]=1; mp[9][2]=1;

mp[2][3]=1; mp[4][3]=1; mp[6][3]=1; mp[8][3]=1;

mp[9][3]=1; mp[4][4]=1; mp[8][4]=1; mp[9][2]=1;

mp[2][5]=1; mp[3][5]=1; mp[4][5]=1; mp[5][5]=1;

mp[6][5]=1; mp[8][5]=1; mp[10][5]=1; mp[2][6]=1;

mp[4][6]=1; mp[8][6]=1; mp[2][7]=1; mp[4][7]=1;

mp[5][7]=1; mp[6][7]=1; mp[8][7]=1; mp[9][7]=1;

mp[2][8]=1; mp[9][8]=1; mp[2][9]=1; mp[4][9]=1;

mp[5][9]=1; mp[6][9]=1; mp[7][9]=1; mp[8][9]=1;

mp[9][9]=1; mp[2][10]=1;


// –> Desenare interfață:


// desenare pereți

l = 1

while (l<=10)

{

  c = 1

  while (c <= 10)

  {

    if (mp[l][c] == 0)

    {

      Stinge(c, 11-l)

    }

    else

    {

      Aprinde(c, 11-l, NEGRU)

    }

    c = c+1

  }

  l = l+1

}


// desenare destinație

Aprinde(dx, dy, ROȘU)


// desenare omuleț

Aprinde(ox, oy, VERDE)


// –> Definire funcție taste:


function FunctieTaste(ev)

{

  var tasta = TastaApasata(ev)

  // dacă s-a apăsat vreo tasta de direcție,

  // atunci încearcă deplasarea în direcția aia

  var oxnou = ox

  var oynou = oy

  if ( (tasta == 'a') && (ox > 1) )

  {

    oxnou = ox-1

  }

  if ( (tasta == 'd') && (ox < 10) )

  {

    oxnou = ox+1

  }

  if ( (tasta == 's') && (oy > 1) )

  {

    oynou = oy-1

  }

  if ( (tasta == 'w') && (oy < 10) )

  {

    oynou = oy+1

  }

  Stinge(ox, oy)

  // dacă la noua poziție nu e perete,

  // atunci deplasează omulețul acolo

  if (mp[11-oynou][oxnou] != 1)

  {

    ox = oxnou

    oy = oynou

  }

  Aprinde(ox, oy, VERDE)

  // dacă omulețul e la destinație,

  // atunci încheie jocul și afișează zâmbet

  if ( (ox == dx) && (oy == dy) )

  {

    //încetează ascultarea tastelor

    AscultaTaste()

    // șterge ecranul

    l = 1

    while (l <= 10)

    {

      c = 1

      while (c <= 10)

      {

        Stinge(c, 11-l)

        c = c+1

      }

      l = l+1

    }

    // afișează zâmbet

    Aprinde(3, 4, ALBASTRU)

    Aprinde(4, 3, ALBASTRU)

    Aprinde(5, 3, ALBASTRU)

    Aprinde(6, 3, ALBASTRU)

    Aprinde(7, 3, ALBASTRU)

    Aprinde(8, 4, ALBASTRU)

    Aprinde(4, 7, ALBASTRU)

    Aprinde(7, 7, ALBASTRU)

  }

}


AscultaTaste(FunctieTaste)


    Îți urez distracție plăcută în construirea și jucarea acestui joc de labirint.

Link articol: https://igotopia.ro/cum-sa-programezi-cu-vectori-si-matrici-fara-sa-fii-as-la-matematica/

Mulțumim, Florin Bîrleanu

Bucuresti, timisoara, arad




x
Acest website utilizează cookie-uri pentru a creea o experiență cât mai plăcută. Învață mai multe Acceptă