Continuiamo a costruire il nostro puzzle game basato sulla griglia collegando le tessere l'un l'altro, facendole accendere con il cursore del mouse e aggiungendo la possibilità di posizionare le bandiere.
Nell'ultima parte di questo tutorial, abbiamo creato un campo di tessere che costituisce la base del nostro puzzle game. In questa parte, lo renderemo riproducibile. Questo tutorial segue direttamente dall'ultima parte, quindi leggi prima di iniziare.
Quando il mouse si trova su una tessera, vogliamo che si accenda. Questa è una funzione interessante che fornisce anche azioni semplici (come lo spostamento del puntatore del mouse) al feedback istantaneo.
Noi usiamo il OnMouseOver ()
funzione per realizzare questo. Viene chiamato automaticamente ogni volta che il cursore del mouse si sposta sull'oggetto a cui è collegato il codice. Aggiungi queste variabili al Piastrella
script:
public var materialIdle: Material; public var materialLightup: Material;
Quindi assegna il tuo materiale di base alle piastrelle materialIdle
fessura. Abbiamo anche bisogno di un accendere materiale, che dovrebbe avere lo stesso colore, ma usa uno shader diverso. Mentre il materiale di base può avere un diffondere Shader ...
... il materiale di illuminazione potrebbe avere un speculare shader. Molti giochi usano anche uno shader di bordo aggiuntivo per quell'effetto. Quelli non vengono con Unity, ma se riesci a capire come ottenerne uno, puoi usarlo al suo posto!
Non dimenticare di assegnare effettivamente i materiali al Materiale slot sul prefabbricato delle piastrelle, in modo che possano essere utilizzati.
Quindi aggiungi questo OnMouseOver ()
funzione al Piastrella
copione
function OnMouseOver () renderer.material = materialLightup;
Provalo! Quando sposti il cursore del mouse sulle tessere, queste dovrebbero cambiare aspetto.
Quello che potresti aver notato è che le tessere cambiano aspetto dopo che il mouse è sopra di loro, ma in realtà non cambiano indietro. Per questo, abbiamo bisogno di usare il OnMouseExit ()
funzione:
function OnMouseExit () renderer.material = materialIdle;
E voilá; ora abbiamo le tessere che si accendono e rendono il gioco molto più interessante.
Per far comunicare le tessere l'una con l'altra (per scoprire quante mine ci sono nelle vicinanze), ogni tessera deve conoscere le tessere vicine. Un modo per ottenere ciò è usare gli ID, che verranno assegnati ad ogni tessera.
Inizia adattando il codice Tile per includere un ID
variabile. Inoltre, aggiungi una variabile per contenere il numero di tessere per riga, che useremo in questo calcolo:
public var ID: int; public var tilesPerRow: int;
Quindi modificare il comando di istanza nel codice Grid in modo che assomigli al seguente snippet. (La nuova riga assegna gli ID ai riquadri mentre vengono creati).
var newTile = Instantiate (tilePrefab, Vector3 (transform.position.x + xOffset, transform.position.y, transform.position.z + zOffset), transform.rotation); newTile.ID = tilesCreated; newTile.tilesPerRow = tilesPerRow;
La prima tessera riceverà l'ID 0, a quello successivo verrà assegnato l'ID 1, e così via. Puoi controllarli facendo clic sulle tessere durante il runtime e vedendo quale numero sono stati assegnati.
Ora vogliamo che ogni tessera sappia delle sue tessere vicine. Quando eseguiamo un'azione su una tessera (come scoprirla), dobbiamo prendere in considerazione le tessere vicine.
Nel nostro caso, questo significa contare le mine che sono adiacenti alla tessera che abbiamo appena scoperto, e forse scoprire anche altre tessere, ma ci arriveremo più tardi.
Questo può anche essere usato, per esempio, per verificare se tre o più tessere sono l'una accanto all'altra in una partita di combinazione a 3.
Inizia aggiungendo queste variabili allo script Tile:
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;
Questi terranno tutte le tessere vicine. Sono pubblici in modo da poter verificare durante il runtime che siano stati effettivamente assegnati correttamente.
Da adesso ogni tessera ha un ID, il numero di tessere che appaiono in una colonna e l'accesso alla matrice statica che ha tutte le tessere salvate nel Griglia
classe, possiamo calcolare le posizioni delle tessere vicine dopo che sono state create.
Quella parte sembra così:
tileUpper = Grid.tilesAll [ID + tilePerRow]; tileLower = Grid.tilesAll [ID - tilesPerRow]; tileLeft = Grid.tilesAll [ID - 1]; tileRight = Grid.tilesAll [ID + 1]; tileUpperRight = Grid.tilesAll [ID + tilesPerRow + 1]; tileUpperLeft = Grid.tilesAll [ID + tilesPerRow - 1]; tileLowerRight = Grid.tilesAll [ID - tilesPerRow + 1]; tileLowerLeft = Grid.tilesAll [ID - tilesPerRow - 1];
Con gli ID e il numero di tessere per riga, possiamo calcolare quali tessere si trovano nelle vicinanze. Supponiamo che la tessera che esegue i calcoli abbia l'ID 3
, e che ci sono cinque tessere per fila. La tessera sopra avrà l'ID 8
(l'ID della tessera selezionata più il numero di tessere per riga); la tessera sulla destra avrà l'ID 6
(l'ID della tessera selezionata più uno), e così via.
Sfortunatamente, questo non è abbastanza. Il codice controlla correttamente i numeri, ma quando chiede il allTiles
array per restituire le tessere, può richiedere numeri di indice fuori intervallo, producendo un lungo elenco di errori.
Per risolvere questo problema, dobbiamo verificare che l'indice che richiediamo dall'array sia effettivamente valido. Il modo più efficiente per farlo è con un nuovo inbounds ()
funzione. Aggiungilo alla piastrella:
funzione privata inBounds (inputArray: Array, targetID: int): boolean if (targetID < 0 || targetID >= inputArray.length) return false; altrimenti restituisce vero;
Ora dobbiamo controllare che ogni possibile tessera attigua si trovi entro i limiti dell'array che contiene tutte le tessere, prima che proviamo effettivamente ad ottenerle dall'array:
if (inBounds (Grid.tilesAll, ID + tilesPerRow)) tileUpper = Grid.tilesAll [ID + tilePerRow]; if (inBounds (Grid.tilesAll, ID - tilesPerRow)) tileLower = Grid.tilesAll [ID - tilesPerRow]; if (inBounds (Grid.tilesAll, ID - 1) && ID% tilesPerRow! = 0) tileLeft = Grid.tilesAll [ID - 1]; if (inBounds (Grid.tilesAll, ID + 1) && (ID + 1)% tilesPerRow! = 0) tileRight = Grid.tilesAll [ID + 1]; if (inBounds (Grid.tilesAll, ID + tilesPerRow + 1) && (ID + 1)% tilesPerRow! = 0) tileUpperRight = Grid.tilesAll [ID + tilesPerRow + 1]; if (inBounds (Grid.tilesAll, ID + tilesPerRow - 1) && ID% tilesPerRow! = 0) tileUpperLeft = Grid.tilesAll [ID + tilePerRow - 1]; if (inBounds (Grid.tilesAll, ID - tilesPerRow + 1) && (ID + 1)% tilesPerRow! = 0) tileLowerRight = Grid.tilesAll [ID - tilesPerRow + 1]; if (inBounds (Grid.tilesAll, ID - tilesPerRow - 1) && ID% tilesPerRow! = 0) tileLowerLeft = Grid.tilesAll [ID - tilesPerRow - 1];
Quel blocco di codice controlla tutte le possibilità. Controlla anche se una tessera si trova sul bordo del campo. Una tessera sul bordo destro della griglia, dopo tutto, in realtà non ha alcuna tessera destra.
Provalo! Controlla alcune tessere e vedi se hai recuperato tutte le tessere vicine correttamente. Dovrebbe sembrare come questo:
Poiché la tessera in questa schermata è una tessera sul lato destro del campo, non ha vicini a destra, in alto a destra o in basso a destra. Le variabili senza tessere assegnate sono correttamente vuote e un test rivela che anche le rimanenti sono state assegnate correttamente.
Infine, una volta accertato che funzioni, aggiungiamo tutte le tessere adiacenti in una matrice, in modo che possiamo accedervi tutte in una volta. Dobbiamo dichiarare questo array all'inizio:
public var adjacentTiles: Array = new Array ();
È quindi possibile adattare ogni riga dell'algoritmo creato in precedenza per immettere ciascuna tessera adiacente in quella matrice o aggiungere successivamente questo blocco:
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);
Dopo che tutte le tessere sono state create, tutte le mine sono state assegnate, e ogni tessera ha recuperato i suoi vicini, quindi dobbiamo controllare se ognuna di queste tessere vicine è estratta o meno.
Il codice Grid assegna già il numero specificato di mine a tessere scelte a caso. Ora abbiamo solo bisogno di ogni tessera per controllare i suoi vicini. Aggiungi questo codice all'inizio del Piastrella
script, in modo che abbiamo un posto dove memorizzare la quantità di mine:
public var adjacentMines: int = 0;
Per contarli, corriamo attraverso l'array in cui abbiamo precedentemente aggiunto tutti i riquadri adiacenti e controlliamo ciascuna voce a turno per vedere se è stata estratta. Se è così, aumentiamo il valore di adjacentMines
per 1.
function CountMines () adjacentMines = 0; per ciascuna (var currentTile: Tile in adjacentTiles) if (currentTile.isMined) adiacenteMines + = 1; displayText.text = adjacentMines.ToString (); se (adjacentMines <= 0) displayText.text = "";
Questa funzione imposta anche l'elemento di testo della tessera per visualizzare il numero di mine nelle vicinanze. Se non ci sono mine, non visualizza nulla (piuttosto che 0).
Aggiungiamo a stato
a ogni tessera. In questo modo, possiamo tenere traccia dello stato in cui si trova attualmente-inattivo
, scoperto
, o contrassegnato
. A seconda dello stato in cui si trova la tessera, reagirà diversamente. Aggiungilo ora, poiché lo useremo tra un istante.
public var state: String = "idle";
Vogliamo essere in grado di contrassegnare le piastrelle come contrassegnato. Una tessera segnalata ha una piccola bandierina sopra di essa. Se facciamo clic con il tasto destro del mouse sul flag, sparirà di nuovo. Se tutte le tessere estratte sono state segnalate e non rimangono tessere segnalate erroneamente, il gioco è vinto.
Inizia creando un oggetto flag e aggiungilo al riquadro (troverai una mesh flag nei file sorgente).
Abbiamo anche bisogno di una variabile per accedere alla bandiera. Aggiungi questo codice:
public var displayFlag: GameObject;
Ricorda di trascinare la bandiera che fa parte della tessera sul displayFlag fessura.
Inoltre, aggiungi questo al inizio()
funzione della piastrella:
displayFlag.renderer.enabled = false; displayText.renderer.enabled = false;
Questo disabiliterà la bandiera e il testo all'inizio. Più tardi, possiamo allora attivare una bandiera, rendendola nuovamente visibile, mettendola effettivamente lì. In alternativa, se noi scoprire una tessera, rendiamo il testo nuovamente visibile.
Scriveremo una funzione che gestisce sia l'immissione che la rimozione di flag:
function SetFlag () if (state == "idle") state = "flagged"; displayFlag.renderer.enabled = true; else if (state == "flagged") state = "idle"; displayFlag.renderer.enabled = false;
Dopo averlo aggiunto, aggiungi anche il codice per gestire un evento click. Per fare questo, adattare il OnMouseOver ()
funzione dobbiamo già controllare per un clic del mouse:
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 ();
Questo riconoscerà un tasto destro del mouse (pulsante 1
) e attivare il Setflag ()
funzione. Attiva o disattiva la bandiera sul riquadro corrente. Provalo!
Abbiamo esteso il nostro puzzle game con diverse caratteristiche vitali, reso visivamente più interessante e abbiamo dato al giocatore la possibilità di influenzare il campo di gioco.
La prossima volta, aggiungeremo la scoperta delle tessere, creeremo una semplice interfaccia e trasformeremo questo in un gioco appropriato.