Construye un juego de rompecabezas basado en cuadrícula como el buscaminas en Unity Winning

En la parte final de esta serie, damos los toques finales a nuestro juego de rompecabezas Unity basado en cuadrícula y lo hacemos jugable. Al final de esta parte, el jugador podrá ganar o perder el juego..

Ahora que has completado los tutoriales anteriores, nuestro juego puede crear un campo de fichas y asignarles minas al azar. También tenemos un bonito efecto de iluminación cuando el jugador se desplaza sobre una baldosa con el mouse, y es posible colocar y eliminar banderas.

Internamente, cada azulejo también conoce sus azulejos vecinos, y ya puede calcular cuántas minas están cerca.

Descubriendo azulejos

Ya hemos agregado la capacidad de colocar banderas con un clic derecho. Ahora, agreguemos la capacidad de descubrir mosaicos con un clic izquierdo.

En el El ratón por encima() función, donde tenemos el código de reconocimiento de clics, necesitamos reconocer un clic izquierdo. Adapta la función para que se vea así:

función OnMouseOver () if (state == "idle") renderer.material = materialLightup; if (Input.GetMouseButtonDown (0)) UncoverTile (); if (Input.GetMouseButtonDown (1)) SetFlag ();  else if (estado == "marcado") renderer.material = materialLightup; if (Input.GetMouseButtonDown (1)) SetFlag (); 

Cuando se presiona el botón izquierdo del ratón, UncoverTile () La función será llamada. Asegúrese de crear esta función, para que no cause un error!

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

Para que esto funcione, necesitamos introducir un nuevo material..

material de var público descubierto: material;

Cree algo que tenga un color diferente al de los azulejos básicos, así que si sus azulejos básicos son azules, puede elegir el verde para el descubierto estado. Pero no uses rojo; Lo necesitaremos más tarde para demostrar que hemos activado una mina..

Cuando se llama a esa función, sucede lo siguiente: 

  • Primero, verificamos si el azulejo está realmente minado. 
  • Si no, ponemos el estado en descubierto, active la pantalla de texto que nos muestra el número de minas cercanas y configure el material a descubierto material. 
  • Después, no se puede volver a hacer clic en el mosaico, y tampoco se volverá a encender, lo que significa que la retroalimentación pasiva de los azulejos que reaccionan al cursor del mouse solo ocurrirá en los azulejos en los que podemos hacer clic..

Antes de que podamos probar esto, debemos asegurarnos de que el material no cambie cuando el cursor del mouse salidas el azulejo Para ello, adapta el OnMouseExit () funciona como tal:

función OnMouseExit () si (estado == "inactivo" || estado == "marcado") renderer.material = materialIdle; 

De esta manera, el color solo se vuelve a cambiar si el azulejo aún no se ha descubierto.

¡Pruébalo! Debes poder descubrir los azulejos. Sin embargo, si se extrae una baldosa, nada sucederá ahora.

Hacer azulejos vacíos descubrir unos a otros

Esto será un poco complicado. En Buscaminas, cuando descubres un azulejo con nojunto a las minas, descubrirá todas las fichas adyacentes que tampoco tienen minas, y las fichas adyacentes a ellos que no tienen minas, etc..

Considere este campo:

En realidad no vemos los números o las minas, solo los azulejos regulares.

Cuando un azulejo con cero Las minas cercanas están descubiertas, todas las baldosas que están a su lado también deben ser descubiertas automáticamente. El azulejo descubierto luego descubre a todos los vecinos..

Estas baldosas recién descubiertas también revisarán a sus vecinos, y, si no hay minas, también las descubrirán.

Esto ondulará a través del campo hasta que alcancemos los azulejos que en realidad tienen minas adyacentes a ellos, donde se detendrá..

Esto crea el áreas vacías Podemos ver en Buscaminas.

Para que esto funcione, necesitamos dos funciones más., UncoverAdjacentTiles () y UncoverTileExternal ():

función privada UncoverAdjacentTiles () for (var currentTile: Tile in adyacente a las fichas) // descubrir todos los nodos adyacentes con 0 minas adyacentes if (! currentTile.isMined && currentTile.state == "idle" && currentTile.adjacentMines == 0) currentTile .UncoverTile (); // descubrir todos los nodos adyacentes con más de 1 mina adyacente, luego dejar de descubrir else if (! currentTile.isMined && currentTile.state == "idle" && currentTile.adjacentMines> 0) currentTile.UncoverTileExternal ();  función pública UncoverTileExternal () state = "uncovered"; displayText.renderer.enabled = true; renderer.material = materialUncovered; 

También necesitamos hacer esta modificación al UncoverTile () función:

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

Cuando descubrimos un azulejo, y no hay minas al lado, llamamos al UncoverAdjacentTiles () función. Esto luego revisa cada ficha vecina para ver si tiene minas o no. también. Si no hay ninguno, también descubre este mosaico e inicia otra ronda de verificación. Si hay minas cercanas, solo se descubre el azulejo en el que se encuentra actualmente.

Ahora, pruébalo. Para obtener una buena probabilidad de que aparezca un campo vacío, cree un campo bastante grande con algunas minas, por ejemplo, 81 fichas, con nueve fichas por fila y 10 minas en total.

Ahora puedes jugar esto como un juego, excepto que aún no puedes disparar minas. Agregaremos esa característica a continuación.

Minas desencadenantes

Cuando descubrimos una ficha que se extrae, el juego se detiene y el jugador pierde. Además, todos los demás azulejos minados se hacen visibles. Para que esto suceda, necesitamos un material más, para los bloques de mina detonados:

material de var públicoDetonado: material;

Sugiero usar algo rojo para esto.

Además, necesitamos agregar dos funciones más para manejar la explosión de todas las minas:

función Explode () state = "detonated"; renderer.material = materialDetonated; para (var currentTile: Tile en Grid.tilesMined) currentTile.ExplodeExternal ();  function ExplodeExternal () state = "detonated"; renderer.material = materialDetonated; 

Activamos esos métodos en el UncoverTile () función:

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

Si un azulejo es minado, el azulejo explota. los Explotar() La función luego envía un comando "explotar" a todas las demás fichas con minas, revelándolas todas..

Ganando el juego

El juego se gana una vez que todas las fichas con minas se han marcado correctamente. En este punto, todas las fichas que no están descubiertas también están descubiertas. Entonces, ¿cómo hacemos un seguimiento de eso?

Empecemos agregando una estado variable a la Cuadrícula clase, de modo que podamos rastrear en qué parte del juego estamos actualmente (aún jugando, perdiendo o ganando).

static var state: String = "inGame";

Mientras estamos en ello, también podemos comenzar a agregar una GUI simple, de modo que podamos mostrar la información necesaria en la pantalla. Unity viene con su propio sistema GUI que usaremos para esto.

función OnGUI () GUI.Box (Rect (10,10,100,50), estado); 

Esto nos mostrará en qué estado estamos actualmente. Llamaremos a estos estados En el juego, juego terminado, y juego ganado.

También podemos agregar cheques al Azulejo, para asegurarnos de que solo podamos interactuar con los azulejos mientras el estado actual del juego es En el juego.

En el El ratón por encima() y OnMouseExit funciones, mover todo el código existente en una Si bloque que comprueba si Estado de la rejilla es actualmente En el juego, al igual que:

función OnMouseOver () if (Grid.state == "inGame") if (state == "idle") renderer.material = materialLightup; if (Input.GetMouseButtonDown (0)) UncoverTile (); if (Input.GetMouseButtonDown (1)) SetFlag ();  else if (estado == "marcado") renderer.material = materialLightup; if (Input.GetMouseButtonDown (1)) SetFlag ();  función OnMouseExit () if (Grid.state == "inGame") if (state == "idle" || state == "marcado") renderer.material = materialIdle; 

En realidad, hay dos formas de verificar si el juego ha sido ganado: podemos contar cuántas minas se han marcado correctamente o podemos verificar si se han descubierto todas las fichas que no son minas. Para eso, necesitamos las siguientes variables; agregarlos a la Cuadrícula clase:

static var minesMarkedCorrectly: int = 0; estática var tilesUncovered: int = 0; static var minesRemaining: int = 0;

No te olvides de configurar minasMantenerseen el Comienzo() función para numberOfMines, y las otras variables para 0. los Comienzo() La función ahora debería verse así:

función Start () CreateTiles (); minesRemaining = numberOfMines; minesMarkedCorrectly = 0; tilesUncovered = 0; state = "inGame"; 

La última línea establece el estado para el juego. (Esto será importante cuando queremos introducir una función de "reinicio" más adelante).

Luego verificamos nuestras condiciones de juego final en el Actualizar() función:

función Update () if (state == "inGame") if ((minesRemaining == 0 && minesMarkedCorrectly == numberOfMines) || (tilesUncovered == numberOfTiles - numberOfMines)) FinishGame (); 

Terminamos el juego estableciendo el estado en juego ganado, descubriendo todas las fichas restantes, y marcando todas las minas restantes:

function FinishGame () state = "gameWon"; // descubre los campos restantes si todos los nodos se han colocado para (var currentTile: Tile in tilesAll) si (currentTile.state == "idle" &&! currentTile.isMined) currentTile.UncoverTileExternal (); // marca las minas restantes si todos los nodos, excepto las minas, se han descubierto para (var currentTile: Tile en Grid.tilesMined) if (currentTile.state! = "flagged") currentTile.SetFlag (); 

Para que todo esto funcione realmente, necesitamos incrementar las variables que rastrean nuestro progreso en los lugares correctos. Adaptar el UncoverTile () función para hacer eso:

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

... así como el UncoverTileExternal () función:

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

También necesitamos aumentar y disminuir la minas marcadas correctamente y minasMantenerse variables dependiendo de si se ha establecido una bandera:

función SetFlag () si (estado == "inactivo") estado = "marcado"; displayFlag.renderer.enabled = true; Grid.minesRemaining - = 1; if (isMined) Grid.minesMarkedCorrectly + = 1;  else if (estado == "marcado") estado = "inactivo"; displayFlag.renderer.enabled = false; Grid.minesRemaining + = 1; if (isMined) Grid.minesMarkedCorrectly - = 1; 

Perdiendo el juego

De la misma manera, tenemos que hacer posible perder un juego. Esto lo logramos a través de Explotar() función dentro de la baldosa. 

Simplemente agregue esta línea a la Explotar() función:

Grid.state = "gameOver";

Una vez que se ejecuta esa línea, el estado del juego cambia a juego terminado, y los azulejos ya no se pueden interactuar con.

Añadiendo una GUI más funcional

Usamos la GUI de Unity hace unos pasos para decirle al jugador en qué estado se encuentra actualmente. Ahora lo extenderemos para mostrar algunos mensajes reales.

El marco para esto se parece a lo siguiente:

función OnGUI () if (state == "inGame")  else if (state == "gameOver")  else if (state == "gameWon") 

Dependiendo del estado, diferentes mensajes se muestran en la GUI. Si el juego se pierde o se gana, por ejemplo, podemos mostrar mensajes que lo digan:

función OnGUI () if (state == "inGame")  else if (state == "gameOver") GUI.Box (Rect (10,10,200,50), "Usted pierde");  else if (state == "gameWon") GUI.Box (Rect (10,10,200,50), "You rock!"); 

También podemos mostrar el número de minas encontradas hasta el momento, o agregar un botón que vuelva a cargar el nivel una vez que el juego haya terminado:

función 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), "Usted pierde"); if (GUI.Button (Rect (10,70,200,50), "Reiniciar")) Reiniciar ();  else if (state == "gameWon") GUI.Box (Rect (10,10,200,50), "You rock!"); if (GUI.Button (Rect (10,70,200,50), "Reiniciar")) Reiniciar ();  función Restart () state = "loading"; Application.LoadLevel (Application.loadedLevel); 

Puedes probarlo todo en esta versión final.!

Conclusión

¡Eso es! Hemos creado un juego de rompecabezas simple con Unity, que puedes usar como base para crear el tuyo. Espero que hayas disfrutado esta serie; Por favor haga cualquier pregunta que tenga en los comentarios.!