Juego en Ionic 3 – Creación del fantástico ahorcado – Parte 3

ionic 3 ahorcado

¿Recordáis el juego que comenzamos a desarrollar en Ionic 2 hace cosa de 7 meses?… ¿no?. A continuación os dejo los 2 enlaces que hacen referencia a la parte 1 y 2 de la creación del juego, de este modo os podéis poner rápidamente al día. En este momento, tenéis 2 opciones:

  1. Seguir las explicaciones y picar el código (recomendado).
  2. Descargar directamente el código desde los enlaces, si es que tu nivel es más avanzado.

https://luisjordan.net/tutorial-de-ionic/juego-con-ionic-2-creacion-del-fantastico-ahorcado/ https://luisjordan.net/tutorial-de-ionic/juego-en-ionic-2-creacion-del-fantastico-ahorcado-parte-2/ En este artículo, vamos a perfeccionar el juego ampliando funcionalidades y mejorando aspectos visuales. Tengamos en cuenta que en estos meses el framework Ionic ha lanzado y madurado su versión 3, es más, está a punto de lanzar la versión 4 que traerá nuevas novedades. Si ya habéis retomado el tema, vamos a ver cómo quedará nuestro proyecto al finalizar el ejercicio. Ionic 3 juego ahorcado Vaya! … ya va tomando forma, incluso va pareciendo un juego de verdad. Quien sabe, igual cualquier día de estos podemos ofrecerlo a nuestros ‘colegas’ que lo instalen en sus dispositivos móviles. Empecemos.

Primeros pasos con Ionic 3.

  1. Actualizar nuestro framework ionic a la versión 3. [codesyntax lang=»bash»]
    npm update -g ionic
    [/codesyntax]
  2. Comprobar las versiones de Ionic y de NPM. [codesyntax lang=»bash»]
    ionic -v
    npm -v
    [/codesyntax]

  3. Creación del proyecto. Vamos a ser ágiles y no entraremos en estos detalles, doy por hecho que si has realizado las entradas 1 y 2, no tendrás ningún problema.

Scripts y librerías con las que vamos a trabajar.

Seguimos utilizando los scripts de los ejercicios anteriores. Home.ts para la lógica y home.html para la vista, incluyendo una nueva hoja de estilos general: app.scss. Las librerías utilizadas son: [codesyntax lang=»typoscript»]

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
// Importación necesaria para mensajes flash.
import { ToastController } from 'ionic-angular';
// Importación necesaria para alerts.
import { AlertController } from 'ionic-angular';

[/codesyntax]

Vista home.html

Podéis ver tal y como acostumbro a hacer, que el código viene con un buen ‘puñado’ de comentarios, siendo así facilito su comprensión. Veamos que tenemos en la vista home.html [codesyntax lang=»xml»]

<!-- Sección superior -->
<ion-header>
    <ion-navbar>
        <!-- Título de la aplicación -->
        <ion-title>AHORCADO</ion-title>
    </ion-navbar>
</ion-header>

<!-- Sección contenido -->
<ion-content padding no-bounce>

    <!-- Sección header-->
    <ion-card class="panel-juego">
        <ion-card-header text-center>
            PANEL DE JUEGO
        </ion-card-header>
    </ion-card>
    
    <!-- Sección imagen + vidas + puntos -->
    <ion-row>
        <ion-col col-4 ><img src="/assets/imgs/ahor-{{imagen}}.png" width='80%' /></ion-col>
        <ion-col col-8 >
            <ion-row>
                <ion-col col-3 text-center><H5><ion-icon name="heart" color="danger"></ion-icon></H5></ion-col>
                <ion-col col-6><H5>Vidas:</H5></ion-col>
                <ion-col col-3 text-center><H5>{{vidas}}</H5></ion-col>
            </ion-row>
            <ion-row>
                <ion-col col-3 text-center><H5><ion-icon name="calculator" color="primary"></ion-icon></H5></ion-col>
                <ion-col col-6><H5>Puntos:</H5></ion-col>
                <ion-col col-3 text-center><H5>{{puntos}}</H5></ion-col>
            </ion-row>
        </ion-col>    
    </ion-row>

    <!-- Sección huecos de palabra -->
    <ion-list >

        <!-- Mensajes de la aplicación -->
        <ion-label text-center>{{mensaje}}</ion-label>

        <ion-grid>
            <ion-row *ngIf="ganador!=1">
                <ion-col class="palabra" col-1 *ngFor="let item of palabra">
                    {{item}}
                </ion-col>
            </ion-row>
            <ion-row *ngIf="ganador==1">
                <ion-col class="acierto">
                    La palabra secreta es: {{nombreSecreto}}
                </ion-col>
            </ion-row>
        </ion-grid>

        <ion-item>
            <!-- Selector que nos permitirá elegir una letra -->
            <ion-label text-center>Seleccione una letra</ion-label>
            <ion-select [(ngModel)]="letra">
                <ion-option value="A">A</ion-option>
                <ion-option value="B">B</ion-option>
                <ion-option value="C">C</ion-option>
                <ion-option value="D">D</ion-option>
                <ion-option value="E">E</ion-option>
                <ion-option value="F">F</ion-option>
                <ion-option value="G">G</ion-option>
                <ion-option value="H">H</ion-option>
                <ion-option value="I">I</ion-option>
                <ion-option value="J">J</ion-option>
                <ion-option value="K">K</ion-option>
                <ion-option value="L">L</ion-option>
                <ion-option value="M">M</ion-option>
                <ion-option value="N">N</ion-option>
                <ion-option value="Ñ">Ñ</ion-option>
                <ion-option value="O">O</ion-option>
                <ion-option value="P">P</ion-option>
                <ion-option value="Q">Q</ion-option>
                <ion-option value="R">R</ion-option>
                <ion-option value="S">S</ion-option>
                <ion-option value="T">T</ion-option>
                <ion-option value="U">U</ion-option>
                <ion-option value="V">V</ion-option>
                <ion-option value="W">W</ion-option>
                <ion-option value="X">X</ion-option>
                <ion-option value="Y">Y</ion-option>
                <ion-option value="Z">Z</ion-option>
            </ion-select>
        </ion-item>
    </ion-list>


    <ion-row>
        <!-- Listado de letras utilizadas -->
        <ion-label text-center>Listado de letras utilizadas</ion-label>
        <ion-col col-12 text-center><H6>{{letras_utilizadas}}</H6></ion-col>
    </ion-row>


<!-- En caso de no tener vidas, mostramos un botón para reiniciar el juego -->
    <ion-row *ngIf="vidas!=0 && ganador!=1">
        <ion-col col-4>
            <!-- Si continuamos teniendo vidas, mostramos el botón para seleccionar letra  -->
            <button ion-button block color="primary" (click)="confirmarResolver()">Resolver</button></ion-col>
        <ion-col col-8>
            <!-- Si continuamos teniendo vidas, mostramos el botón para seleccionar letra  -->
            <button ion-button block color="secondary" (click)="compruebaLetra()">Seleccionar letra</button>
        </ion-col>
    </ion-row>

    <button *ngIf="vidas==0 || ganador==1" ion-button block (click)="reiniciaJuego()">Jugar de nuevo</button>

</ion-content>

[/codesyntax] <!– Sección superior –> corresponde a la zona superior gris donde indica el título del juego. <!– Sección header–> creación de un panel para destacar: panel del juego. <!– Sección imagen + vidas + puntos –> se ha añadido esta sección para darle un poco de dinamismo a la aplicación, cada vez que pongamos una letra se realizará una serie de acciones en la lógica y…, en caso de acierto se sumarán puntos, y en caso de error se restarán vidas, puntos y la imagen del ahorcado cambiará. <!– Sección huecos de palabra –> como ya pasaba en las versiones anteriores del juego, en esta sección se reemplazarán los huecos de la palabra secreta por las letras que se hayan acertado. <!– Selector que nos permitirá elegir una letra –> selector de letras del abecedario. <!– Listado de letras utilizadas –> para evitar tener que ir memorizando cada una de las letras que se seleccionan, se añade un histórico o leyenda de letras utilizadas. Y por último, la sección de botones. Aquí se mostrarán unos botones u otros dependiendo del estado del juego.

Hoja de estilos: app.scss

[codesyntax lang=»css»]

H2{
	color: #777777;
}

.panel-juego{
	margin-bottom: 25px;
}

.palabra{
	 
}

.letras{
	
}

.acierto{
	color:#32db64!important;
}

// Mensages TOAST
.toast-success{
  > div{
       background-color:#32db64!important;
   }
}
.toast-danger{
  > div{
       background-color:#f53d3d!important;
   }
}
.toast-warning{
  > div{
       background-color:#f2cd3c!important;
   }
}

#palabraSolucion{
	text-transform: uppercase;
}

[/codesyntax]

Lógica de juego ahorcado con ionic 3 en home.ts

[codesyntax lang=»typoscript»]

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
// Importación necesaria para mensajes flash.
import { ToastController } from 'ionic-angular';
// Importación necesaria para alerts.
import { AlertController } from 'ionic-angular';

@Component({
  	selector: 'page-home',
  	templateUrl: 'home.html'
})
  
export class HomePage {
	// Definimos las variables
	letra: string = '';
	nombres: any = ['COCHE'];
	nombreSecreto: any = this.palabraAleatoria(0, (this.nombres.length - 1));
	palabra: any = '';
	muestraHuecos: any = this.muestraHuecosPalabra();
	mensaje: string = '';
	letras_utilizadas: string = '';
	nombresecretomostrar: string = '';

	vidas: number = 6;
	puntos: number = 0;
	ganador: number = 0;
	imagen: number = 1;

	durationMessages: number = 3000;

	// Creamos un array para guardar las letras que se van seleccionando.
	controlLetras = new Array;

	constructor(public navCtrl: NavController, 
				private toastCtrl: ToastController,
				public alertCtrl: AlertController) {  }

	// Método que valida la letra seleccionada.	
	public compruebaLetra() {
	    // Formateamos a mayúsculas para mejorar la legibilidad.
	    let letraMayusculas = this.letra.toUpperCase();
	    
	    // Si se ha seleccionado una letra...		
	    if (letraMayusculas) {

	    	if (this.controlLetras.indexOf(letraMayusculas) == -1) {
	    	
		    	// Recorremos las letras de la palabra (array), para detectar si la letra se encuentra en ella.
		        if (this.nombreSecreto.indexOf(letraMayusculas) != -1) {

		            let nombreSecretoModificado = this.nombreSecreto;
				    let posicion = new Array;
				    let posicionTotal = 0;

				    let contador = 1;

				    while (nombreSecretoModificado.indexOf(letraMayusculas) != -1) {
								
						posicion[contador] = nombreSecretoModificado.indexOf(letraMayusculas);
				        nombreSecretoModificado = nombreSecretoModificado.substring(nombreSecretoModificado.indexOf(letraMayusculas) + letraMayusculas.length, nombreSecretoModificado.length);

						// Calculamos la posición total.
						if (contador > 1) {
						    posicionTotal = posicionTotal + posicion[contador] + 1;
					    }
						else { 
						    posicionTotal = posicionTotal + posicion[contador];
						}

						// Preparamos la palabra para que sea mostrara en modal de solución directa.
						this.palabra[posicionTotal] = letraMayusculas;

		                // Sumamos puntos
						if (this.controlLetras.indexOf(letraMayusculas) == -1) {
						    this.puntos = this.puntos + 10;

						    // Hacemos uso de Toast Controller para lanzar mensajes flash.
						    let toast = this.toastCtrl.create({
							    message: 'Genial, la letra ' + letraMayusculas + ' está en la palabra secreta.',
							    duration: this.durationMessages,
							    cssClass: 'toast-success',
							    position: 'top'
						  	});
	  						toast.present();
						}

		                contador++;

		                // Si ya no quedan huecos, mostramos el mensaje para el ganador.
						if (this.palabra.indexOf('_') == -1) { 

						    // Sumamos puntos
						    if (this.controlLetras.indexOf(letraMayusculas) == -1) {
							this.puntos = this.puntos + 50;
						    }

						    // Damos el juego por finalizado, el jugador gana.
						    this.finDelJuego('gana')					
						}
			        }
			    }
		        else {
		            // Restamos una vida.
				    this.nuevoFallo();
				    // Actualizamos la imagen
				    this.nuevaImagen(this.imagen);

				    // Comprobamos si nos queda alguna vida.
				    if (this.vidas > 0) {

				        // Restamos puntos siempre y cuando no sean 0.
						if (this.puntos > 0) { 
						    if (this.controlLetras.indexOf(letraMayusculas) == -1) {
								this.puntos = this.puntos - 5;
						    }
						}

						// Mostramos un mensaje indicando el fallo.	
					    let toast = this.toastCtrl.create({
						    message: 'Fallo, la letra ' + letraMayusculas + ' no está en la palabra secreta. Recuerda que te quedan ' + this.vidas + ' vidas.',
						    duration: this.durationMessages,
						    cssClass: 'toast-danger',
						    position: 'top'
					  	});
						toast.present();				
					}
				    else { 
						// Damos el juego por finalizado, el jugador pierde.
						this.finDelJuego('pierde')
				    }
		        }

		        // Array de letras utilizadas para mostrar al jugador.
		        if(this.letras_utilizadas == ''){
					this.letras_utilizadas += letraMayusculas;
				}
				else{
					this.letras_utilizadas += ' - '+letraMayusculas;
				}

				// Añadimos al array de letras la nueva letra seleccionada.
				this.controlLetras.push(letraMayusculas);
		    }
		    else{
				// En caso de que la letra ya hubiera sido seleccionada, mostramos un mensaje.
			    let toast = this.toastCtrl.create({
				    message: 'La letra ' + letraMayusculas + ' fue seleccionada anteriormente. Por favor, seleccione una letra diferente.',
				    duration: this.durationMessages,
				    cssClass: 'toast-warning',
				    position: 'top'
			  	}); 
			    toast.present();
			}

		}
	}

	public muestraHuecosPalabra() {
	    let totalHuecos = this.nombreSecreto.length;

	    // Declaramos la variable huecos como nuevo array.		
	    let huecos = new Array;
	    for (let i = 0; i < totalHuecos; i++) {
			//Asignamos tantos huecos como letras tenga la palabra.
			huecos.push('_');
	    }

	    // Para empezar formamos la variable palabra tan solo con los huecos, ya que en este momento aún no se ha seleccionado ninguna letra.	
	    this.palabra = huecos;
	    return this.palabra;
	}

  	// Método que genera una palabra aleatoria comprendida en el array nombres.	
	public palabraAleatoria(primer, ultimo) {
	  	let numberOfName = Math.round(Math.random() * (ultimo - primer) + (primer));
	  	return this.nombres[numberOfName];
	}

	public nuevoFallo() {
	    this.vidas = this.vidas - 1;
	    return this.vidas;
	}

	public nuevaImagen(imagen) {
	    this.imagen = imagen + 1;
	    return this.imagen;
	}

	public confirmarResolver(){
		this.showPrompt();
	}

	public showPrompt() {
		const prompt = this.alertCtrl.create({
		  	title: 'Solución directa',
		  	message: "¿Está seguro de resolver la palabra secreta directamente?",
		  	inputs: [
		    {
		      	name: 'palabraSolucion',
		      	id: 'palabraSolucion',
		      	placeholder: this.palabra
		    },
		  	],
		  	buttons: [
		    {
		      	text: 'Cancelar',
		      	handler: data => {
		        	// Se cierra ventana.
		      	}
		    },
		    {
		      	text: 'Resolver',
		      	handler: data => {
		        	// Llamamos a método que compara la palabra secreta con la insertada mediante teclado.
		        	// var solucion = this.palabra.toString();
		        	// var solucion = solucion.replace(/,/g, '');
					var solucion = ((document.getElementById("palabraSolucion") as HTMLInputElement).value);
		        	this.resolver(solucion);
		      	}
		    }]
		});
		prompt.present();
	}

	public showConfirm(accion) {

		// Resolver
		if(accion == 'resolver'){
			const confirm = this.alertCtrl.create({
		      	title: 'Solución directa',
		      	message: '¿Está seguro de resolver la palabra secreta directamente?',
		      	buttons: [
		        {
		          	text: 'Cancelar',
		          	handler: () => {
		            	//
		          	}
		        },
		        {
		          	text: 'Confirmar',
		          	handler: () => {
		            	//
		          	}
		        }]
		    });
			confirm.present();
		}
	    
	}

	public resolver(solucion){
		// Comprobamos la solución directa.

		if(this.nombreSecreto == solucion.toUpperCase()){
			var totalOcultas = 0;
			// Recorremos el array para detectar huecos sin transformar a letras.
			for ( var i = 0; i < this.palabra.length; i++ ) {
		        if(this.palabra[i] == '_'){
		        	totalOcultas = totalOcultas + 1;
		        }
		    }

		    // ACIERTO :: Sumamos +50 y + 20 por cada hueco sin desvelar.
		    this.puntos = this.puntos + 50 + (20 * totalOcultas);
			
			this.finDelJuego('gana')

			// Colocamos la palabra secreta en el
		}else{
			// ERROR :: RESTAMOS 50.
			this.puntos = this.puntos - 25;

			let toast = this.toastCtrl.create({
			    message: 'Lo sentimos!, La palabra '+solucion+' no es la palabra secreta. Su error le resta 25 puntos.',
			    duration: this.durationMessages,
			    cssClass: 'toast-danger',
			    position: 'top'
		  	});
			toast.present();
		}


	}

	public finDelJuego(valor) { 
	    // Perdedor
	    if (valor == 'pierde') {

	    	this.ganador = 0;

			// Mostramos el mensaje como que el juego ha terminado
		    let toast = this.toastCtrl.create({
			    message: 'Perdiste!, Inténtalo de nuevo. Has conseguido un total de ' + this.puntos + ' puntos. La palabra secreta es ' + this.nombreSecreto,
			    duration: this.durationMessages,
			    cssClass: 'toast-danger',
			    position: 'top'
		  	});
			toast.present();
		}

	    // Ganador
	    if (valor == 'gana') { 

	    	this.ganador = 1;

	    	let toast = this.toastCtrl.create({
			    message: 'Enhorabuena!, Has acertado la palabra secreta. Has conseguido un total de ' + this.puntos + ' puntos.',
			    duration: this.durationMessages,
			    cssClass: 'toast-success',
			    position: 'top'
		  	});
			toast.present();
	    }		
	}

	public reiniciaJuego() { 
	    this.letra = '';
	    this.palabra = '';
	    this.vidas = 6;
	    this.mensaje = '';
	    this.ganador = 0;
	    this.puntos = 0;
	    this.nombreSecreto = this.palabraAleatoria(0, (this.nombres.length-1));
	    this.muestraHuecos = this.muestraHuecosPalabra();
	    this.imagen = 1;
	    this.letras_utilizadas = '';
	 	this.nombresecretomostrar = '';
	 	this.controlLetras = new Array;
	}

}

[/codesyntax]

Explicación de la lógica del juego.

Ya que la lógica del juego va quedando algo extensa, en lugar de comentar línea por línea, lo que voy a hacer es explicar por donde pasa el flujo de la aplicación en cada uno de los escenarios posibles. En el momento que seleccionamos una letra del desplegable y pulsamos sobre el botón seleccionar letra, estamos llamando al método compruebaLetra() y es aquí donde realizamos las siguientes comprobaciones:

  • Seleccionar letra:
    • Comprobar que se haya seleccionado una letra.
    • Comprobar si la letra está en la palabra secreta:
      • SI:
        • Recorremos la palabra secreta y reemplazamos los huecos por la letra seleccionada tantas veces como corresponda.
        • Sumamos puntos.
        • Lanzamos mensaje toast de acierto.
        • Comprobamos si quedan huecos en la palabra secreta:
          • SI: Seguimos jugando.
          • NO: @Ganador solución indirecta.
      • NO:
        • Restamos puntos.
        • Restamos vida.
        • Comprobamos si quedan vidas:
          • SI: Seguimos jugando.
          • NO: Fin del juego.
        • Lanzamos mensaje toast de error.
        • Cambiamos imagen añadiendo un palito al muñeco del ahorcado.
  • Resolver directamente:
    • Solución directa:
      • SI: Sumamos más puntos que en la solución indirecta.
      • NO: Restamos puntos y seguimos jugando.

Conclusión final.

Sinceramente, estoy contento. Estamos aprendiendo Ionic realizando un juego que está tomando una pinta chulísima. Versiones utilizadas, tiempo y dificultad de desarrollo: Plataforma: Ionic v3 Dificultad: Avanzado. Tiempo de desarrollo: 6 horas.

Summary
Juego en Ionic 3 - Creación del fantástico ahorcado - Parte 3
Article Name
Juego en Ionic 3 - Creación del fantástico ahorcado - Parte 3
Description
Creación de juegos para móviles con Ionic 3
Author

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Responsable de los datos: Luis María Jordán Muñoz | Finalidad: Responder a la solicitud que me envíes y ofrecerte información | Legitimación: Tu consentimiento de forma expresa | Destinatario: Nicalia mi proveedor de hosting | Derechos: Tienes derecho al acceso, rectificación, supresión, limitación, portabilidad y olvido, para más información, te dejo enlace a mi política de privacidad ... enlace

Scroll al inicio
Ir arriba