Construiește un joc de puzzle bazat pe grilă, cum ar fi Minesweeper în unitate Câștigător

În ultima parte a acestei serii, am pus pasajele finale pe jocul nostru de puzzle Unity bazat pe rețea și l-am făcut să se poată juca. Până la sfârșitul acestei părți, jucătorul va putea câștiga sau pierde jocul.

Acum că ați terminat tutorialele anterioare, jocul nostru poate crea un câmp de gresie și să-i atribuiți alegeri aleatoriu. De asemenea, avem un efect frumos de lumină atunci când jucătorul se deplasează peste o țiglă cu mouse-ul și este posibil să plasați și să eliminați steagurile.

Pe plan intern, fiecare țiglă cunoaște și țiglele vecine și poate deja calcula câte mine sunt în apropiere.

Descoperirea plăcilor

Am adăugat deja abilitatea de a plasa steaguri cu un clic dreapta. Acum, să adăugăm abilitatea de a descoperi plăcile cu un clic stânga.

În OnMouseover () , unde avem codul de recunoaștere a clicurilor, trebuie să recunoaștem un clic stânga. Adaptați funcția astfel încât să arate astfel:

funcția OnMouseOver () if (state == "inactiv") renderer.material = materialLightup; dacă (Input.GetMouseButtonDown (0)) UncoverTile (); dacă (Input.GetMouseButtonDown (1)) SetFlag ();  altfel dacă (state == "marcat") renderer.material = materialLightup; dacă (Input.GetMouseButtonDown (1)) SetFlag (); 

Când este apăsat butonul stâng al mouse - ului, UncoverTile () funcția va fi apelată. Asigurați-vă că creați această funcție, astfel încât aceasta să nu provoace o eroare!

funcția UncoverTile () if (! isMined) state = "descoperit"; displayText.renderer.enabled = true; renderer.material = materialUtilizat;  altfel Explode (); 

Pentru ca acest lucru să funcționeze, trebuie să introducem un material nou.

public var materialUtilizat: material;

Creați ceva care are o culoare diferită de plăcile de bază - deci dacă plăcile dvs. de bază sunt albastre, puteți alege verde pentru neacoperit stat. Dar nu folosiți roșu; mai târziu vom avea nevoie ca să arătăm că am declanșat o mină.

Când apelați această funcție, se întâmplă următoarele: 

  • În primul rând, verificăm dacă țiglele sunt de fapt minate. 
  • Dacă nu, am setat statul la neacoperit, activați afișarea textului care ne arată numărul de mine din apropiere și setați materialul la neacoperit material. 
  • După aceea, piesa nu poate fi repetată și, de asemenea, nu se va aprinde din nou, ceea ce înseamnă că reacția pasivă a plăcilor care reacționează la cursorul mouse-ului se va întâmpla doar pe plăcile pe care le putem face clic.

Înainte de a putea încerca acest lucru, trebuie să ne asigurăm că materialul nu se schimbă atunci când cursorul mouse-ului ieșiri țiglă. Pentru aceasta, adaptați OnMouseExit () funcționează ca atare:

funcția OnMouseExit () if (state == "idle" || state == "marcat") renderer.material = materialIdle; 

În acest fel, culoarea este deconectată numai dacă placa nu a fost încă descoperită.

Încearcă! Ar trebui să puteți descoperi plăcile. Dacă o țiglă este minată, totuși, nu se va întâmpla nimic acum.

Efectuarea plăcilor goale a se descoperi reciproc

Acest lucru va fi un pic dificil. În Minesweeper, când descoperiți o țiglă cu Numine de lângă el, va descoperi toate plăcile adiacente acestuia care nu au de asemenea mină, iar plăcile adiacente lor care nu au mine și așa mai departe.

Luați în considerare acest câmp:

Nu vedem numerele sau minele, doar plăci regulate.

Când o țiglă cu zero minele din apropiere sunt descoperite, toate plăcile de lângă acestea ar trebui descoperite automat. Tigla descoperită descoperă apoi toți vecinii.

Aceste plăci recent dezvăluite vor verifica apoi și vecinii și, dacă nu există mine, să le descoperi și ei.

Acest lucru se va răsfrânge prin câmp până când vom ajunge la țigle care au de fapt mină adiacentă acestora, unde se va opri.

Aceasta creează zone goale putem vedea în Minesweeper.

Pentru a face acest lucru, avem nevoie de două funcții suplimentare, UncoverAdjacentTiles () și UncoverTileExternal ():

funcția privată UncoverAdjacentTiles () pentru (var currentTile: Tile în adiacenteTiles) // descoperă toate nodurile adiacente cu mină adiacentă dacă (! currentTile.isMined && currentTile.state == "idle" && currentTile.adjacentMines == 0) currentTile .UncoverTile (); // descoperiți toate nodurile adiacente cu mai mult de o mină adiacentă, apoi opriți descoperirea altfel dacă (! currentTile.isMined && currentTile.state == "idle" && currentTile.adjacentMines> 0) currentTile.UncoverTileExternal ();  funcția publică UncoverTileExternal () state = "descoperit"; displayText.renderer.enabled = true; renderer.material = materialUtilizat; 

Trebuie, de asemenea, să facem această modificare la UncoverTile () funcţie:

funcția UncoverTile () if (! isMined) state = "descoperit"; displayText.renderer.enabled = true; renderer.material = materialUtilizat; dacă (adjacentMines == 0) UncoverAdjacentTiles (); 

Când descoperim o țiglă și nu există mine lângă ea, numim UncoverAdjacentTiles () funcţie. Aceasta verifică apoi fiecare țiglă vecină pentru a vedea dacă are mine sau nu. de asemenea. Dacă nu există, descoperă și acest cerc și inițiază o nouă rundă de verificare. Dacă în apropiere există mine, acesta descoperă numai țigla în care se află în prezent.

Acum, încercați. Pentru a avea șanse bune de a apărea un câmp gol, creați un câmp destul de mare, cu câteva mine - de exemplu, 81 de plăci, cu nouă plăci pe rând și 10 de mine în total.

De fapt, puteți juca acum acest joc ca joc, cu excepția faptului că nu puteți declanșa încă mine. Vom adăuga acea facilitate în continuare.

Mine de declanșare

Când descoperim o țiglă care este extrasă, jocul se oprește și jucătorul pierde. În plus, toate celelalte plăci minerale devin vizibile. Pentru ca acest lucru să se întâmple, avem nevoie de încă un material pentru grinzile detonate:

public var materialDetonat: material;

Vă sugerez să folosiți ceva roșu pentru asta.

De asemenea, trebuie să adăugăm încă două funcții pentru a face față explodării tuturor minelor:

funcția Explode () state = "detonat"; renderer.material = materialDetonat; pentru (var currentTile: Placă în Grid.tilesMined) currentTile.ExplodeExternal ();  funcția ExplodeExternal () state = "detonat"; renderer.material = materialDetonat; 

Acționăm aceste metode în UncoverTile () funcţie:

funcția UncoverTile () if (! isMined) state = "descoperit"; displayText.renderer.enabled = true; renderer.material = materialUtilizat; dacă (adjacentMines == 0) UncoverAdjacentTiles ();  altfel Explode (); 

Dacă o țiglă este minată, țigla explodează. Exploda() funcția trimite apoi o comandă "explodează" la toate celelalte dale cu mine, dezvăluindu-le pe toate.

Câștigarea jocului

Jocul este câștigat odată ce toate dalele cu mine au fost marcate corect. În acest moment, toate plăcile care nu sunt descoperite sunt descoperite. Deci, cum putem urmări acest lucru?

Să începem prin adăugarea a stat variabilă la Grilă pentru a putea urmări care parte a jocului se află în prezent (încă se joacă, se pierd sau se câștigă).

stare stare statică: String = "inGame";

În timp ce suntem la el, putem începe, de asemenea, să adăugăm o interfață grafică simplă, astfel încât să putem afișa informațiile necesare pe ecran. Unitatea vine cu propriul sistem GUI pe care îl vom folosi pentru acest lucru.

funcția OnGUI () GUI.Box (Rect (10,10,100,50), stat); 

Acest lucru ne va arăta în ce stare suntem în prezent. Vom numi aceste stări în joc, joc încheiat, și gameWon.

De asemenea, putem adăuga cecuri la Tiglă, pentru a ne asigura că putem interacționa numai cu plăcile în timp ce starea curentă a jocului este în joc.

În OnMouseover () și OnMouseExit funcții, mutați tot codul existent într-un dacă bloc care verifică dacă Grid.state este în prezent în joc, ca astfel:

funcția OnMouseOver () if (Grid.state == "inGame") if (state == "inactiv") renderer.material = materialLightup; dacă (Input.GetMouseButtonDown (0)) UncoverTile (); dacă (Input.GetMouseButtonDown (1)) SetFlag ();  altfel dacă (state == "marcat") renderer.material = materialLightup; dacă (Input.GetMouseButtonDown (1)) SetFlag ();  funcția OnMouseExit () if (Grid.state == "inGame") if (state == "idle" || state == "marcat") renderer.material = materialIdle; 

Există de fapt două modalități de a verifica dacă jocul a fost câștigat: putem număra câte minuri au fost marcate corect sau putem verifica dacă toate plăcile care nu sunt minate au fost descoperite. Pentru aceasta, avem nevoie de următoarele variabile; adăugați-le la Grilă clasă:

statice var minesMarkedCrept: int = 0; statice var tilesUncoved: int = 0; statice var minesRemaining: int = 0;

Nu uitați să setați minesRemainingîn Start() funcția pentru a numberOfMines, și celelalte variabile 0. Start() funcția ar trebui să pară acum:

funcția Start () CreateTiles (); minesRemaining = numberOfMines; minesMarkedCorrectly = 0; tigleUncoperite = 0; state = "inGame"; 

Ultima linie stabilește starea jocului. (Acest lucru va fi important atunci când dorim să introducem mai târziu o funcție de "repornire".)

Apoi, verificăm condițiile noastre de joc din Actualizați() funcţie:

funcția Update () if (state == "inGame") if ((minesRemaining == 0 && minesMarkedCorrectly == numberOfMines) || (tilesUncovered == numberOfTiles - numberOfMines)) FinishGame (); 

Finalizăm jocul prin setarea statului gameWon, descoperirea tuturor plăcilor rămase și semnalizarea tuturor resturilor de mine:

funcția FinishGame () state = "gameWon"; // descoperă câmpurile rămase dacă toate nodurile au fost plasate pentru (var currentTile: Tile în tilesAll) dacă (currentTile.state == "idle" &&! currentTile.isMined) currentTile.UncoverTileExternal (); // marchează restul minelor dacă toate nodurile, cu excepția minelor, au fost descoperite pentru (var currentTile: Tile în Grid.tilesMined) dacă (currentTile.state! = "flagged") currentTile.SetFlag (); 

Pentru ca toate acestea să funcționeze efectiv, trebuie să creștem variabilele care urmăresc progresul nostru în locurile potrivite. Adaptați UncoverTile () funcția de a face acest lucru:

funcția UncoverTile () if (! isMined) state = "descoperit"; displayText.renderer.enabled = true; renderer.material = materialUtilizat; Grid.tilesUncovered + = 1; dacă (adjacentMines == 0) UncoverAdjacentTiles ();  altfel Explode (); 

… la fel de bine ca UncoverTileExternal () funcţie:

funcția UncoverTileExternal () state = "descoperit"; displayText.renderer.enabled = true; renderer.material = materialUtilizat; Grid.tilesUncovered + = 1; 

De asemenea, trebuie să creștem și să scădem minesMarkedCorrectly și minesRemaining variabile în funcție de stabilirea unui steag:

funcția SetFlag () if (state == "idle") state = "marcat"; displayFlag.renderer.enabled = true; Grid.minesRemaining - = 1; dacă (isMined) Grid.minesMarkedCorrectly + = 1;  altfel dacă (state == "marcat") state = "inactiv"; displayFlag.renderer.enabled = false; Grid.minesRemaining + = 1; dacă (isMined) Grid.minesMarkedCorrectly - = 1; 

Pierderea jocului

În același mod, trebuie să facem posibil să pierdem un joc. Realizăm acest lucru prin Exploda() funcția în interiorul plăcii. 

Pur și simplu adăugați această linie la Exploda() funcţie:

Grid.state = "gameOver";

Odată ce această linie este executată, starea jocului este schimbată joc încheiat, iar plăcile nu mai pot fi interacționate.

Adăugarea unui GUI mai funcțional

Am folosit GUI Unity cu câțiva pași în urmă pentru a le spune jucătorului despre starea jocului în care se află în prezent. Acum îl vom extinde pentru a afișa câteva mesaje reale.

Cadrul pentru aceasta pare a fi următorul:

funcția OnGUI () if (state == "inGame")  altceva dacă (state == "gameOver")  altceva (state == "gameWon") 

În funcție de stare, în GUI se afișează diferite mesaje. Dacă jocul este pierdut sau câștigat, de exemplu, putem afișa mesaje care spun astfel:

funcția OnGUI () if (state == "inGame")  altceva dacă (state == "gameOver") GUI.Box (Rect (10,10,200,50), "You lose");  altceva dacă (state == "gameWon") GUI.Box (Rect (10,10,200,50), "You rock!"); 

De asemenea, putem arăta numărul de mine găsite până acum sau puteți adăuga un buton care reîncarcă nivelul imediat după terminarea jocului:

funcția OnGUI () if (state == "inGame") GUI.Box (Rect (10,10,200,50), "Mines left:" + MinesRemaining);  altfel dacă (state == "gameOver") GUI.Box (Rect (10,10,200,50), "Pierdeți"); dacă (GUI.Button (Rect (10,70,200,50), "Restart")) Reporniți ();  altceva dacă (state == "gameWon") GUI.Box (Rect (10,10,200,50), "You rock!"); dacă (GUI.Button (Rect (10,70,200,50), "Restart")) Reporniți ();  funcția Restart () state = "loading"; Application.LoadLevel (Application.loadedLevel); 

Puteți încerca totul în această construcție finală!

Concluzie

Asta e! Am creat un joc de puzzle simplu cu Unity, pe care îl puteți folosi ca bază pentru a vă crea propriul. Sper că v-ați bucurat de această serie; vă rugăm să puneți întrebările pe care le aveți în comentarii!