Bouw een op rasters gebaseerd puzzelspel zoals Minesweeper in Unity Winning

In het laatste deel van deze serie leggen we de laatste hand aan ons grid-gebaseerde Unity-puzzelspel en maken het speelbaar. Aan het einde van dit deel kan de speler het spel winnen of verliezen.

Nu je de vorige tutorials hebt voltooid, kan ons spel een veld met tegels maken en mijnen willekeurig aan hen toewijzen. We hebben ook een leuk lichteffect wanneer de speler met de muis over een tegel zweeft en het is mogelijk vlaggen te plaatsen en te verwijderen.

Intern weet elke tegel ook over de aangrenzende tegels en kan al berekenen hoeveel mijnen er in de buurt zijn.

Tegels ontdekken

We hebben al de mogelijkheid toegevoegd om vlaggen met een rechtermuisknop te plaatsen. Laten we nu de mogelijkheid toevoegen om tegels te ontdekken met een linkerklik.

In de OnMouseOver () functie, waar we de klikherkenningscode hebben, moeten we een linkerklik herkennen. Pas de functie aan zodat deze er zo uitziet:

function OnMouseOver () if (state == "idle") renderer.material = materialLightup; if (Input.GetMouseButtonDown (0)) UncoverTile (); if (Input.GetMouseButtonDown (1)) SetFlag ();  else if (state == "flagged") renderer.material = materialLightup; if (Input.GetMouseButtonDown (1)) SetFlag (); 

Wanneer de linkermuisknop wordt ingedrukt, de UncoverTile () functie wordt gebeld. Zorg ervoor dat u deze functie maakt, zodat dit geen bug veroorzaakt!

functie UncoverTile () if (! isMined) state = "uncovered"; displayText.renderer.enabled = true; renderer.material = materialUncovered;  else Explode (); 

Om dit te laten werken, moeten we een nieuw materiaal introduceren.

public var materialUncovered: Materiaal;

Maak iets dat een andere kleur heeft dan de standaardtegels. Als uw basistegels dus blauw zijn, kunt u groen kiezen voor de onbedekt staat. Maar gebruik geen rood; we hebben dat later nodig om te laten zien dat we een mijn hebben geactiveerd.

Wanneer deze functie wordt aangeroepen, gebeurt het volgende: 

  • Eerst controleren we of de tegel daadwerkelijk is gedolven. 
  • Als dat niet het geval is, stellen we de status in op onbedekt, activeer de tekstweergave die ons het aantal nabijgelegen mijnen toont en stel het materiaal in op de onbedekt materiaal. 
  • Daarna kan de tegel niet opnieuw worden aangeklikt en zal hij ook niet opnieuw oplichten, wat betekent dat de passieve feedback van tegels die reageren op de muiscursor alleen zal plaatsvinden op tegels waarop we kunnen klikken.

Voordat we dit kunnen uitproberen, moeten we ervoor zorgen dat het materiaal niet wordt gewijzigd als de muisaanwijzer exits de tegel. Pas hiervoor het OnMouseExit () functioneer als volgt:

function OnMouseExit () if (state == "idle" || state == "flagged") renderer.material = materialIdle; 

Op deze manier wordt de kleur alleen teruggeschakeld als de tegel nog niet is ontbloot.

Probeer het! Je zou tegels moeten kunnen vinden. Als een tegel wordt gewonnen, gebeurt er echter op dit moment niets.

Lege tegels maken, elkaar blootleggen

Dit zal een beetje lastig zijn. In Mijnenveger, wanneer je een steen ontdekt met Neemijnen naast, het zal alle tegels ernaast ontdekken die ook geen mijnen hebben, en de tegels ernaast hen die geen mijnen hebben, enzovoort.

Beschouw dit veld:

We zien niet echt de cijfers of mijnen, alleen gewone tegels.

Wanneer een tegel met nul mijnen in de buurt zijn onbedekt, alle tegels ernaast moeten ook automatisch worden ontdekt. De onbedekte tegel onthult vervolgens alle buren.

Deze nieuwe onbedekte tegels zullen dan ook hun buren controleren en, als er geen mijnen zijn, ze ook blootleggen.

Dit zal door het veld rimpelen totdat we tegels bereiken met echte mijnen ernaast, waar het zal stoppen.

Dit maakt de lege gebieden we kunnen zien in Mijnenveger.

Om dit te laten werken, hebben we nog twee functies nodig, UncoverAdjacentTiles () en UncoverTileExternal ():

private functie UncoverAdjacentTiles () for (var currentTile: Tile in adjacentTiles) // ontrafel alle aangrenzende knooppunten met 0 aangrenzende mijnen als (! currentTile.isMined && currentTile.state == "idle" && currentTile.adjacentMines == 0) currentTile .UncoverTile (); // ontdek alle aangrenzende knooppunten met meer dan 1 aangrenzende mijn, stop dan met het ontsluiten van else als (! currentTile.isMined && currentTile.state == "idle" && currentTile.adjacentMines> 0) currentTile.UncoverTileExternal ();  openbare functie UncoverTileExternal () state = "uncovered"; displayText.renderer.enabled = true; renderer.material = materialUncovered; 

We moeten deze wijziging ook aanbrengen in de UncoverTile () functie:

functie UncoverTile () if (! isMined) state = "uncovered"; displayText.renderer.enabled = true; renderer.material = materialUncovered; if (adjacentMines == 0) UncoverAdjacentTiles (); 

Wanneer we een steen blootleggen en er geen mijnen naast staan, noemen we de steen UncoverAdjacentTiles () functie. Dit controleert vervolgens elke aangrenzende tegel om te zien of deze mijnen heeft of niet. te. Als dit niet het geval is, wordt deze tegel ook zichtbaar en wordt er opnieuw een controleronde gestart. Als er mijnen in de buurt zijn, wordt alleen de tegel zichtbaar die deze momenteel heeft.

Nu, probeer het uit. Om een ​​goede kans te krijgen dat een leeg veld verschijnt, maakt u een vrij groot veld met een paar mijnen, bijvoorbeeld 81 tegels, met negen tegels per rij en in totaal 10 mijnen.

Je kunt dit nu echt als een spel spelen, behalve dat je mijnen nog niet kunt activeren. We zullen die functie vervolgens toevoegen.

Mijnen triggeren

Wanneer we een tegel blootleggen die gedolven is, stopt het spel en verliest de speler. Bovendien worden alle andere gedolven tegels zichtbaar. Om dit te laten gebeuren, hebben we nog één materiaal nodig voor de ontplofte mijntegels:

public var materialDetonated: Materiaal;

Ik stel voor hier iets roods voor te gebruiken.

We moeten ook nog twee functies toevoegen om alle mijnen te ontploffen:

function Explode () state = "detonated"; renderer.material = materiaalDetonated; for (var currentTile: Tile in Grid.tilesMined) currentTile.ExplodeExternal ();  function ExplodeExternal () state = "detonated"; renderer.material = materiaalDetonated; 

We triggeren deze methoden in de UncoverTile () functie:

functie UncoverTile () if (! isMined) state = "uncovered"; displayText.renderer.enabled = true; renderer.material = materialUncovered; if (adjacentMines == 0) UncoverAdjacentTiles ();  else Explode (); 

Als een tegel wordt gedolven, explodeert de tegel. De Ontploffen() functie stuurt vervolgens een "explode" -opdracht naar alle andere tegels met mijnen, waardoor ze allemaal zichtbaar worden.

Het spel winnen

De game wordt gewonnen zodra alle tegels met mijnen correct zijn gemarkeerd. Op dit punt worden ook alle niet-onbedekte tegels onthuld. Dus hoe kunnen we dat bijhouden?

Laten we beginnen met het toevoegen van een staat variabele voor de rooster klas, zodat we kunnen volgen in welk deel van het spel we ons bevinden (nog steeds spelen, verloren of gewonnen).

static var state: String = "inGame";

Terwijl we bezig zijn, kunnen we ook beginnen met het toevoegen van een eenvoudige GUI, zodat we de benodigde informatie op het scherm kunnen weergeven. Unity wordt geleverd met een eigen GUI-systeem dat we hiervoor zullen gebruiken.

function OnGUI () GUI.Box (Rect (10,10,100,50), state); 

Dit laat ons zien in welke staat we ons bevinden. We zullen deze staten noemen in het spel, spel is over, en gewonnen.

We kunnen ook controles aan de tegel toevoegen om ervoor te zorgen dat we alleen de tegels kunnen gebruiken terwijl de huidige spelstatus is in het spel.

In de OnMouseOver () en OnMouseExit functies, verplaats alle bestaande code naar een als blok dat controleert of Grid.state is op dit moment in het spel, zoals zo:

function OnMouseOver () if (Grid.state == "inGame") if (state == "idle") renderer.material = materialLightup; if (Input.GetMouseButtonDown (0)) UncoverTile (); if (Input.GetMouseButtonDown (1)) SetFlag ();  else if (state == "flagged") renderer.material = materialLightup; if (Input.GetMouseButtonDown (1)) SetFlag ();  function OnMouseExit () if (Grid.state == "inGame") if (state == "idle" || state == "flagged") renderer.material = materialIdle; 

Er zijn eigenlijk twee manieren om te controleren of het spel is gewonnen: we kunnen tellen hoeveel mijnen correct zijn gemarkeerd, of we kunnen controleren of alle tegels die geen mijnen zijn, zijn ontmaskerd. Daarvoor hebben we de volgende variabelen nodig; voeg ze toe aan de rooster klasse:

static var minesMarkedCorrectly: int = 0; static var tilesUncovered: int = 0; static var minesRemaining: int = 0;

Vergeet niet om in te stellen minesRemainingin de Begin() functie naar numberOfMines, en de andere variabelen om 0. De Begin() functie zou er nu als volgt uit moeten zien:

functie Start () CreateTiles (); minesRemaining = numberOfMines; minesMarkedCorrectly = 0; tilesUncovered = 0; state = "inGame"; 

De laatste regel bepaalt de status voor het spel. (Dit is belangrijk als we later een "herstart" -functie willen introduceren.)

We controleren dan op onze eindspel-voorwaarden in de Bijwerken() functie:

function Update () if (state == "inGame") if ((minesRemaining == 0 && minesMarkedCorrectly == numberOfMines) || (tilesUedcovered == numberOfTiles - numberOfMines)) FinishGame (); 

We eindigen het spel door de status in te stellen gewonnen, het blootleggen van alle resterende tegels en het markeren van alle overgebleven mijnen:

function FinishGame () state = "gameWon"; // ontsluit resterende velden als alle knooppunten zijn geplaatst voor (var currentTile: Tile in tilesAll) if (currentTile.state == "idle" &&! currentTile.isMined) currentTile.UncoverTileExternal (); // markeert de overgebleven mijnen als alle knooppunten behalve de mijnen zijn gevonden voor (var currentTile: Tile in Grid.tilesMined) if (currentTile.state! = "flagged") currentTile.SetFlag (); 

Om dit echt te laten werken, moeten we de variabelen die onze voortgang volgen op de juiste plaats opvoeren. Pas de UncoverTile () functie om dat te doen:

functie UncoverTile () if (! isMined) state = "uncovered"; displayText.renderer.enabled = true; renderer.material = materialUncovered; Grid.tilesUncovered + = 1; if (adjacentMines == 0) UncoverAdjacentTiles ();  else Explode (); 

… net als de UncoverTileExternal () functie:

functie UncoverTileExternal () state = "uncovered"; displayText.renderer.enabled = true; renderer.material = materialUncovered; Grid.tilesUncovered + = 1; 

We moeten ook het. Verhogen en verlagen minesMarkedCorrectly en minesRemaining variabelen afhankelijk van of een vlag is ingesteld:

function SetFlag () if (state == "idle") state = "flagged"; displayFlag.renderer.enabled = true; Grid.minesRemaining - = 1; if (isMined) Grid.minesMarkedCorrectly + = 1;  else if (state == "flagged") state = "idle"; displayFlag.renderer.enabled = false; Grid.minesRemaining + = 1; if (isMined) Grid.minesMarkedCorrectly - = 1; 

Het spel verliezen

Op dezelfde manier moeten we het mogelijk maken om een ​​spel te verliezen. We bereiken dit via de Ontploffen() functie binnen de tegel. 

Voeg eenvoudig deze regel toe aan de Ontploffen() functie:

Grid.state = "gameOver";

Zodra die regel wordt uitgevoerd, wordt de status van het spel omgeschakeld spel is over, en met de tegels kan niet langer worden gecommuniceerd.

Een meer functionele GUI toevoegen

We hebben de Unity GUI een paar stappen geleden gebruikt om de speler te vertellen in welke gamestatus ze zich nu bevinden. Nu breiden we het uit om enkele echte berichten weer te geven.

Het raamwerk hiervoor ziet er als volgt uit:

function OnGUI () if (state == "inGame")  else if (state == "gameOver")  else if (state == "gameWon") 

Afhankelijk van de status worden verschillende berichten weergegeven in de GUI. Als het spel bijvoorbeeld verloren of gewonnen is, kunnen we berichten weergeven die dit zeggen:

function OnGUI () if (state == "inGame")  else if (state == "gameOver") GUI.Box (Rect (10,10,200,50), "You lose");  else if (state == "gameWon") GUI.Box (Rect (10,10,200,50), "You rock!"); 

We kunnen ook het aantal tot nu toe gevonden mijnen laten zien, of een knop toevoegen die het level herlaadt als het spel afgelopen is:

function OnGUI () if (state == "inGame") GUI.Box (Rect (10,10,200,50), "Mines left:" + minesRemaining);  else if (state == "gameOver") GUI.Box (Rect (10,10,200,50), "You lose"); if (GUI.Button (Rect (10,70,200,50), "Restart")) Opnieuw opstarten ();  else if (state == "gameWon") GUI.Box (Rect (10,10,200,50), "You rock!"); if (GUI.Button (Rect (10,70,200,50), "Restart")) Opnieuw opstarten ();  functie Herstarten () state = "laden"; Application.LoadLevel (Application.loadedLevel); 

Je kunt het allemaal uitproberen in deze laatste build!

Conclusie

Dat is het! We hebben een eenvoudig puzzelspel gemaakt met Unity, dat je als basis kunt gebruiken om je eigen puzzelspel te maken. Ik hoop dat je deze serie leuk vond; stel alstublieft alle vragen die u in de opmerkingen hebt!