We blijven ons op rasters gebaseerde puzzelspel bouwen door de tegels met elkaar te verbinden, ze op te laten lichten met de muiscursor en de mogelijkheid toe te voegen vlaggen te plaatsen.
In het laatste deel van deze tutorial hebben we een veld met tegels gemaakt dat de basis vormt voor ons puzzelspel. In dit deel maken we het speelbaar. Deze tutorial volgt direct vanaf het laatste deel, dus lees dat voordat je hiermee begint.
Als de muis zich boven een tegel bevindt, willen we dat hij oplicht. Dit is een coole functie die zelfs eenvoudige acties (zoals het bewegen van uw muisaanwijzer) onmiddellijke feedback geeft.
Wij gebruiken de OnMouseOver ()
functie om dit te bereiken. Het wordt automatisch opgeroepen wanneer de muiscursor zich verplaatst over het object waaraan de code is gekoppeld. Voeg deze variabelen toe aan de Tegel
script:
public var materialIdle: Materiaal; public var materialLicht-up: materiaal;
Ken vervolgens uw basistegelmateriaal toe aan de materialIdle
sleuf. We hebben ook een oplichten materiaal dat dezelfde kleur zou moeten hebben, maar een andere arcering gebruikt. Hoewel het basismateriaal een diffuus shader ...
... het lightup-materiaal kan een spiegelende shader. Veel spellen gebruiken ook een extra velghuls voor dat effect. Die komen niet met Eenheid, maar als je erachter kunt komen hoe je er een kunt krijgen, kun je dat in plaats daarvan gebruiken!
Vergeet niet om de materialen daadwerkelijk toe te wijzen aan de Materiaal slots op de tegel prefab, zodat ze kunnen worden gebruikt.
Voeg dit toe OnMouseOver ()
functie naar de Tegel
script
function OnMouseOver () renderer.material = materialLightup;
Probeer het! Als u de muiscursor over de tegels beweegt, moeten deze hun uiterlijk wijzigen.
Wat je misschien wel gemerkt hebt, is dat de tegels hun uiterlijk veranderen als de muis er eenmaal op staat, maar niet echt verandert terug. Daarvoor moeten we de OnMouseExit ()
functie:
function OnMouseExit () renderer.material = materialIdle;
En voilá; we hebben nu tegels die oplichten en maken het spel veel interessanter.
Om de tegels met elkaar te laten communiceren (om erachter te komen hoeveel mijnen er in de buurt zijn), moet elke tegel de aangrenzende tegels kennen. Een manier om dat te bereiken, is het gebruik van ID's, waaraan elke tegel wordt toegewezen.
Begin met het aanpassen van de tegelcode om er een op te nemen ID kaart
variabel. Voeg ook een variabele toe om het aantal tegels per rij te bevatten, die we in deze berekening gebruiken:
public var ID: int; openbare var tilesPerRow: int;
Wijzig vervolgens de instantiate-opdracht in de rastercode om er als volgt uit te zien als het volgende fragment. (De nieuwe regel wijst ID's toe aan de tegels terwijl ze worden gemaakt.)
var newTile = Instantiate (tilePrefab, Vector3 (transform.position.x + xOffset, transform.position.y, transform.position.z + zOffset), transform.rotation); newTile.ID = tilesCreated; newTile.tilesPerRow = tilesPerRow;
De eerste tegel krijgt de ID 0, de volgende krijgt de ID toegewezen 1, enzovoorts. U kunt ze controleren door tijdens runtime op de tegels te klikken en te zien welk nummer aan hen is toegewezen.
Nu willen we dat elke tegel op de hoogte is van de aangrenzende tegels. Wanneer we een actie uitvoeren op een tegel (zoals het ontrafelen), moeten we rekening houden met de aangrenzende tegels.
In ons geval betekent dit het tellen van de mijnen die grenzen aan de tegel die we zojuist hebben blootgelegd, en mogelijk ook andere tegels blootleggen, maar daar komen we later op terug.
Dit kan ook worden gebruikt om bijvoorbeeld te controleren of drie of meer tegels naast elkaar staan in een Match-3-game.
Begin met het toevoegen van deze variabelen aan het Tile-script:
public var tileUpper: Tile; public var tileLower: Tile; public var tileLeft: Tile; public var tileRight: Tile; public var tileUpperRight: Tile; public var tileUpperLeft: Tile; public var tileLowerRight: Tile; public var tileLowerLeft: Tile;
Deze bevatten alle aangrenzende tegels. Ze zijn openbaar, zodat we tijdens runtime kunnen controleren of ze daadwerkelijk correct zijn toegewezen.
Sindsdien heeft elke Tile een ID, het aantal tegels dat in een kolom wordt weergegeven en toegang tot de statische array waarin alle tegels zijn opgeslagen in de kolom rooster
klasse, kunnen we de posities van de naburige tegels berekenen nadat ze zijn gemaakt.
Dat deel ziet er zo uit:
tileUpper = Grid.tilesAlle [ID + tilesPerRow]; tileLower = Grid.tilesAlle [ID - tilesPerRow]; tileLeft = Grid.tilesAlle [ID - 1]; tileRight = Grid.tilesAlle [ID + 1]; tileUpperRight = Grid.tilesAlle [ID + tilesPerRow + 1]; tileUpperLeft = Grid.tilesAlle [ID + tilesPerRow - 1]; tileLowerRight = Grid.tilesAlle [ID - tilesPerRow + 1]; tileLowerLeft = Grid.tilesAlle [ID - tilesPerRow - 1];
Met de ID's en het aantal tegels per rij kunnen we berekenen welke tegels in de buurt zijn. Stel dat de tegel die de berekeningen uitvoert, de ID heeft 3
, en dat er vijf tegels per rij zijn. De tegel erboven heeft de ID 8
(de ID van de geselecteerde tegel plus het aantal tegels per rij); de tegel rechts heeft de ID 6
(de ID van de geselecteerde tegel plus één), enzovoort.
Helaas is dit niet voldoende. De code controleert de cijfers correct, maar wanneer deze wordt gevraagd allTiles
array om de tegels terug te zetten, kan het indexnummers opvragen die buiten het bereik liggen, waardoor een lange lijst met fouten wordt geproduceerd.
Om dit op te lossen, moeten we controleren of de index die we van de array vragen, ook geldig is. De meest efficiënte manier om dit te doen is met een nieuwe inbounds ()
functie. Voeg het toe aan de tegel:
private function inBounds (inputArray: Array, targetID: int): boolean if (targetID < 0 || targetID >= inputArray.length) return false; anders ga terug naar waar;
Nu moeten we controleren of elke mogelijke tegel in de buurt valt binnen de grenzen van de array die alle tegels bevat, voordat we deze daadwerkelijk uit de array proberen te krijgen:
if (inBounds (Grid.tilesAll, ID + tilesPerRow)) tileUpper = Grid.tilesAlle [ID + tilesPerRow]; if (inBounds (Grid.tilesAll, ID - tilesPerRow)) tileLower = Grid.tilesAlle [ID - tilesPerRow]; if (inBounds (Grid.tilesAll, ID - 1) && ID% tilesPerRow! = 0) tileLeft = Grid.tilesAlle [ID - 1]; if (inBounds (Grid.tilesAll, ID + 1) && (ID + 1)% tilesPerRow! = 0) tileRight = Grid.tilesAlle [ID + 1]; if (inBounds (Grid.tilesAll, ID + tilesPerRow + 1) && (ID + 1)% tilesPerRow! = 0) tileUpperRight = Grid.tilesAlle [ID + tilesPerRow + 1]; if (inBounds (Grid.tilesAll, ID + tilesPerRow - 1) && ID% tilesPerRow! = 0) tileUpperLeft = Grid.tilesAlle [ID + tilesPerRow - 1]; if (inBounds (Grid.tilesAll, ID - tilesPerRow + 1) && (ID + 1)% tilesPerRow! = 0) tileLowerRight = Grid.tilesAlle [ID - tilesPerRow + 1]; if (inBounds (Grid.tilesAll, ID - tilesPerRow - 1) && ID% tilesPerRow! = 0) tileLowerLeft = Grid.tilesAlle [ID - tilesPerRow - 1];
Dat codeblok controleert alle mogelijkheden. Het controleert ook of een tegel zich aan de rand van het veld bevindt. Een tegel aan de rechterkant van het raster heeft tenslotte eigenlijk geen tegels rechts.
Probeer het! Controleer een paar tegels en kijk of je alle aangrenzende tegels correct hebt opgehaald. Het zou er zo uit moeten zien:
Omdat de tegel in deze schermafbeelding een tegel is aan de rechterkant van het veld, heeft deze geen buren rechts, rechtsboven of rechtsonder. De variabelen zonder toegewezen tegels zijn correct leeg en een test laat zien dat de resterende tegels ook correct zijn toegewezen.
Eindelijk, zodra we ervoor hebben gezorgd dat dit werkt, voegen we alle aangrenzende tegels in een array toe, zodat we ze later allemaal tegelijk kunnen openen. We moeten dit array aan het begin declareren:
public var adjacentTiles: Array = new Array ();
U kunt vervolgens elke regel van het algoritme aanpassen die we hierboven hebben gemaakt om elke aangrenzende tegel in die array in te voeren, of dit blok achteraf toevoegen:
if (tileUpper) adjacentTiles.Push (tileUpper); if (tileLower) adjacentTiles.Push (tileLower); if (tileLeft) adjacentTiles.Push (tileLeft); if (tileRight) adjacentTiles.Push (tileRight); if (tileUpperRight) adjacentTiles.Push (tileUpperRight); if (tileUpperLeft) adjacentTiles.Push (tileUpperLeft); if (tileLowerRight) adjacentTiles.Push (tileLowerRight); if (tileLowerLeft) adjacentTiles.Push (tileLowerLeft);
Nadat alle tegels zijn gemaakt, alle mijnen zijn toegewezen en elke tegel zijn buren heeft opgehaald, moeten we controleren of al deze aangrenzende tegels worden gedolven of niet.
De Grid-code wijst het opgegeven aantal mijnen al toe aan willekeurig gekozen tegels. Nu hebben we alleen elke tegel nodig om zijn buren te controleren. Voeg deze code toe aan het begin van de Tegel
script, zodat we een plaats hebben om het aantal mijnen op te slaan:
public var adjacentMines: int = 0;
Om ze te tellen, rennen we door de array waarin we eerder alle aangrenzende tegels hebben toegevoegd en controleren we elk item om beurten om te zien of het is gedolven. Als dat het geval is, verhogen we de waarde van adjacentMines
door 1.
function CountMines () adjacentMines = 0; voor elke (var currentTile: Tile in adjacentTiles) if (currentTile.isMined) adjacentMines + = 1; displayText.text = adjacentMines.ToString (); if (adjacentMines <= 0) displayText.text = "";
Met deze functie wordt ook het tekstelement van de tegel ingesteld om het aantal mijnen in de buurt weer te geven. Als er geen mijnen zijn, geeft deze niets weer (in plaats van 0).
Laten we er een toevoegen staat
op elke tegel. Op deze manier kunnen we bijhouden in welke staat het zich momenteel bevindt-nutteloos
, onbedekt
, of markeerde
. Afhankelijk van de staat waarin de tegel zich bevindt, reageert deze anders. Voeg het nu toe, we zullen het in een oogwenk gebruiken.
public var state: String = "idle";
We willen tegels kunnen markeren als markeerde. Een gemarkeerde tegel heeft een klein vlaggetje erop. Als we met de rechtermuisknop op de vlag klikken, verdwijnt deze weer. Als alle gedolven tegels zijn gemarkeerd en er geen verkeerd gemarkeerde tegels overblijven, wordt het spel gewonnen.
Begin met het maken van een vlagobject en voeg het toe aan de tegel (u vindt een vlaggaas in de bronbestanden).
We hebben ook een variabele nodig om toegang te krijgen tot de vlag. Voeg deze code toe:
public var displayFlag: GameObject;
Vergeet niet om de vlag die deel uitmaakt van de tegel te slepen naar de displayFlag sleuf.
Voeg dit ook toe aan de begin()
functie van de tegel:
displayFlag.renderer.enabled = false; displayText.renderer.enabled = false;
Hierdoor worden de vlag en tekst aan het begin uitgeschakeld. Later kunnen we dat dan activeren een vlag, die hem weer zichtbaar maakt, hem er effectief neerzet. Als alternatief, als wij ontdekken een tegel, we maken de tekst opnieuw zichtbaar.
We zullen een functie schrijven die zowel het plaatsen als verwijderen van vlaggen behandelt:
function SetFlag () if (state == "idle") state = "flagged"; displayFlag.renderer.enabled = true; else if (state == "flagged") state = "idle"; displayFlag.renderer.enabled = false;
Voeg, nadat je dat hebt toegevoegd, de code toe om een klikgebeurtenis af te handelen. Om dit te doen, past u de OnMouseOver ()
functie die we al moeten controleren op een muisklik:
function OnMouseOver () if (state == "idle") renderer.material = materialLightup; if (Input.GetMouseButtonDown (1)) SetFlag (); else if (state == "flagged") renderer.material = materialLightup; if (Input.GetMouseButtonDown (1)) SetFlag ();
Hiermee wordt een rechterklik herkend (knop 1
) en activeer de Zet vlag()
functie. Vervolgens wordt de vlag op de huidige tegel geactiveerd of gedeactiveerd. Probeer het!
We hebben ons puzzelspel uitgebreid met verschillende vitale functies, het visueel interessanter gemaakt en de speler de mogelijkheid gegeven om het speelveld te beïnvloeden.
De volgende keer voegen we tegels toe, maken we een eenvoudige interface en maken we er een echt spel van.