Dando continuidad al ejercicio desarrollado por el compañero @cosmosoftroot, quise mostrar en forma de transiciones animadas la batalla.
Me pareció una estructura interesante en materia de buenas prácticas (uso de los arrow Functions y de más), y simpleza en el código, tanto así que todo el contenido (CSS, JS y semántica HTML5), decidí dejarlo en un único fichero HTML para mantener de momento la versión propuesta por el alumno y centrarme en modificar parte de la lógica del juego en fragmentos que comparten un mismo Scope.
La idea en general, es poder implementar el patrón de diseño “revealing module pattern” o patrón revelador en otro tutorial, para encapsular toda la lógica javascript dentro de una única función.
Para no extender un poco el tutorial decidí incluir la librería de JQuery para recorrer algunos elementos del DOM.
** Fuentes **
el repositorio donde pueden descargar el proyecto para proponer mejoras es:
Repositorio Github
la URL de github-pages para ejecutar el ejercicio:
github-pages
** Proceso **
Las etapas para refactorizar el código se listan en orden a continuación hasta el punto #8 que es donde está integrado todo el bloque de código Javascript:
(1) Definición de los miembros públicos al comienzo del bloque javascript.
<!-- SCRIPT --><scripttype="text/javascript">// SCOPE >>>>>let vidaGoku=100;
let vidaVegeta=100;
const minPower=5;
const maxPower=12;
let round =0;
let imgGoku=1;
let imgVegeta=1;
const altoLienzoActual = 500;
// Imagenes:let arrImages = {
'goku_injured': 'images/goku_injured.jpg',
'vegeta_injured': 'images/vegeta_injured.png'
}
// Arrow functionsconst ambosSiguenVivos = () => vidaGoku > 0 && vidaVegeta > 0;
const calcularGolpe = () => Math.round(Math.random() * (maxPower - minPower)) + minPower;
const sigueVivoGoku =() => vidaGoku > 0;
let contadorPelea = 0// de acá para abajo son los pasos siguientes...</script>
(2) Identificar la sentencia “while” para iterar sobre la Batalla mientras ambos sigan vivos y copiar todo este bloque a una nueva función, esta función tiene el nombre de “comenzarBatalla”:
// functión que permite comenzar la batallafunctioncomenzarBatalla(){
// estado golpeslet estados=null// inicio de la pelea while(ambosSiguenVivos()){
// todo el contenido de este while...
}
}
(3) para este paso, quise sacar las lineas que escriben elementos sobre el DOM, tipo: “document.white(’<div>elemento</div>’)”, que se implementan cuando el golpe de Goku es mayor al golpe de vegeta y viceversa.
Esto con el objetivo de refactorizar ese código en una función aparte y reciba como parametros el estado de la batalla.
// inicio de la pelea while(ambosSiguenVivos()){
console.log("cuerpo INICIO");
contadorPelea++
round++; // constantes
const golpeGoku = calcularGolpe();
const golpeVegeta = calcularGolpe();// golpes (evaluacion)if (golpeGoku > golpeVegeta){
if(imgGoku>3)
imgGoku=1;
vidaVegeta-=golpeGoku;// render
estados = renderEscena(round, "goku", golpeGoku, imgGoku, vidaVegeta)
imgGoku++;
}else{
if(imgVegeta>3)
imgVegeta=1;
vidaGoku-=golpeVegeta;// render
estados = renderEscena(round, "vegeta", golpeVegeta, imgVegeta, vidaGoku)
imgVegeta++;
}
console.log("cuerpo FINAL, imagenes de test: "+arrImages.goku_injured);
}
(4) Ahora el paso siguiente será implementar esa función nueva: “renderEscena”, la cual recibe como parámetros el round actual, quien golpea, imagen de quien golpea y la vida del oponente según sea el caso:
functionrenderEscena(round, nombreGanador, golpeGanador, imgGanador, vidaActualOponente){
// clear HTMLdocument.write('')
// definicioneslet golpeGoku=falselet golpeVegeta=falsedocument.write('<table class="storyboard">');
document.write(`<tr><th colspan='2'>Round ${round}</th></tr>`)
// Validaciónif (nombreGanador=="goku"){
// estado atacantedocument.write('<tr>')
document.write(`<td>Goku ataca con un golpe de ${golpeGanador}</td>`)
document.write(`<td><img src='images/goku_${imgGanador}.jpg'></td>`)
document.write('</tr>')
// estado oponentedocument.write('<tr>')
document.write(`<td>Vegeta queda con ${vidaActualOponente} de vida</td>`)
document.write(`<td><img src='images/vegeta_injured.png'></td>`)
document.write('</tr>')
}else{
// estado atacantedocument.write('<tr>')
document.write(`<td>Vegeta ataca con un golpe de ${golpeGanador}</td>`)
document.write(`<td><img src='images/vegeta_${imgGanador}.jpg'></td>`)
document.write('</tr>')
vidaGoku-=golpeVegeta;
// estado oponentedocument.write('<tr>')
document.write(`<td>Goku queda con ${vidaActualOponente} de vida</td>`)
document.write(`<td><img src='images/goku_injured.jpg'></td>`)
document.write('</tr>')
}
document.write(`</table>`)
return golpeGanador
}
(5) Justo después de la función definida en el paso anterior y justo antes del cierre del script, se ejecutan las siguientes lineas como flujo normal de eventos:
// salidas
console.log("alto lienzo "+altoLienzoActual)
if(sigueVivoGoku()){
document.write('<table class="storyboard">')
document.write(`<tr><thcolspan='2'>Goku gana la pelea!!!</th></tr>`)
document.write(`<td><imgsrc='images/goku_win.jpg'></td>`)
document.write(`</table>`)
}else{
document.write('<table class="storyboard">')
document.write(`<tr><th colspan='2'>Vegeta gana la pelea!!!</th></tr>`)
document.write(`<td><imgsrc='images/vegeta_win.jpg'></td>`)
document.write(`</table>`)
}
// final SCOPE <<<<<
Teniendo estos primeros 5 pasos listos, está casi terminado, pero falta lo más importante y un poco desafiante en la refactorización, y es el poder recorrer los elementos “storyboard” que son tablas HTML, y mostrar cada una por intervalos de tiempo hasta la última tabla.
Es decir, no se tiene certeza de si se desplegarán 15 tablas en una batalla se motrará en la ultima de las secuencuas a Goku como vencedor, o una siguiente batalla donde hayan 18 escenas o tablas donde la última (secuencua), es quizás Vegeta el ganador.
Primero intenté con:
while (ambosSiguenVivos()){
setInterval(function()//recorrer tablas y procesar ...
}, 3000)
}
Pero esto provoca un problema y no se despliega como debería.
Entonces el paso #6 será implementar ese intervalo de tiempo para cada recorrido del ciclo while.
(6) Se genera un retardo para cada estado de la batalla, es decir, un recorrido dentro del ciclo while para cuando golpea Goku o cuando golpea Vegeta.
La implementación esta vez no es con “setInterval”, sino con: “setTimeout…”:
while (ambosSiguenVivos()){
// ...// definicioneslet arrTables = $(".contenedor table")
let cont =0;
// animación
setTimeout(function() {
console.log("INICIAL")
$(arrTables[0]).fadeOut("slow")//.css({"display":"none"}) // recorrer los elementos diferentes del primero//
De momento no se coloca la implementación a partir de esta linea
}, 2000);
}
(7) Sobre el bloque de código anterior, se implementa el contenido dentro de “setTimeOut(function(){…})”, donde inicialmente se realiza un recorrido del JQuery Object definido con el nombre de “arrTables” dela siguiente manera:
arrTables.each(function(i,j){
// el elemento i, representa el indice actual del elemento en cuestion
// el elemento j, representa el contexto de cada elemento HTML recorrido
});
arrTables.each(function(i,j){
// incremento
con++
// el elemento i, representa el indice actual del elemento en cuestion
// el elemento j, representa el contexto de cada elemento HTML recorrido, es decir, cada tabla
})
$(j).fadeOut("slow")
// recorrer los elementos diferentes del primero
arrTables.each(function(i,j){
cont++
setTimeout(function () {
console.log("iteracion "+ $(j).attr("class")+ " contenido de i: "+i)
$(j).fadeOut("slow")
}, 3000 * cont)
})
(8) Una muestra de como se ve el código completo refactorizado (SOLO EL SCRIPT JS) es:
<!-- SCRIPT --><scripttype="text/javascript">// SCOPE >>>>>let vidaGoku=100;
let vidaVegeta=100;
const minPower=5;
const maxPower=12;
let round =0;
let imgGoku=1;
let imgVegeta=1;
const altoLienzoActual = 500;
// Imagenes:let arrImages = {
'goku_injured': 'images/goku_injured.jpg',
'vegeta_injured': 'images/vegeta_injured.png'
}
// Arrow functionsconst ambosSiguenVivos = () => vidaGoku > 0 && vidaVegeta > 0;
const calcularGolpe = () => Math.round(Math.random() * (maxPower - minPower)) + minPower;
const sigueVivoGoku =() => vidaGoku > 0;
let contadorPelea = 0// comenzar batalla
comenzarBatalla()
//-------------------------------------------------functioncomenzarBatalla(){
// estado golpeslet estados=null// inicio de la pelea while(ambosSiguenVivos()){
console.log("cuerpo INICIO");
contadorPelea++
round++;
// constantesconst golpeGoku = calcularGolpe();
const golpeVegeta = calcularGolpe();
// golpes (evaluacion)if (golpeGoku > golpeVegeta){
if(imgGoku>3)
imgGoku=1;
vidaVegeta-=golpeGoku;
// render
estados = renderEscena(round, "goku", golpeGoku, imgGoku, vidaVegeta)
imgGoku++;
}else{
if(imgVegeta>3)
imgVegeta=1;
vidaGoku-=golpeVegeta;
// render
estados = renderEscena(round, "vegeta", golpeVegeta, imgVegeta, vidaGoku)
imgVegeta++;
}
console.log("cuerpo FINAL, imagenes de test: "+arrImages.goku_injured);
}
// definicioneslet arrTables = $(".contenedor table")
let cont =0;
// animación
setTimeout(function() {
console.log("INICIAL")
$(arrTables[0]).fadeOut("slow")//.css({"display":"none"})// recorrer los elementos diferentes del primero
arrTables.each(function(i,j){
cont++
setTimeout(function () {
console.log("iteracion "+ $(j).attr("class")+ " contenido de i: "+i)
$(j).fadeOut("slow")
}, 3000 * cont)
})
}, 2000);
}
functionrenderEscena(round, nombreGanador, golpeGanador, imgGanador, vidaActualOponente){
// clear HTMLdocument.write('')
// definicioneslet golpeGoku=falselet golpeVegeta=falsedocument.write('<table class="storyboard">');
document.write(`<tr><th colspan='2'>Round ${round}</th></tr>`)
// Validaciónif (nombreGanador=="goku"){
// estado atacantedocument.write('<tr>')
document.write(`<td>Goku ataca con un golpe de ${golpeGanador}</td>`)
document.write(`<td><img src='images/goku_${imgGanador}.jpg'></td>`)
document.write('</tr>')
// estado oponentedocument.write('<tr>')
document.write(`<td>Vegeta queda con ${vidaActualOponente} de vida</td>`)
document.write(`<td><img src='images/vegeta_injured.png'></td>`)
document.write('</tr>')
}else{
// estado atacantedocument.write('<tr>')
document.write(`<td>Vegeta ataca con un golpe de ${golpeGanador}</td>`)
document.write(`<td><img src='images/vegeta_${imgGanador}.jpg'></td>`)
document.write('</tr>')
vidaGoku-=golpeVegeta;
// estado oponentedocument.write('<tr>')
document.write(`<td>Goku queda con ${vidaActualOponente} de vida</td>`)
document.write(`<td><img src='images/goku_injured.jpg'></td>`)
document.write('</tr>')
}
document.write(`</table>`)
return golpeGanador
}
// salidasconsole.log("alto lienzo "+altoLienzoActual)
if(sigueVivoGoku()){
document.write('<table class="storyboard">')
document.write(`<tr><th colspan='2'>Goku gana la pelea!!!</th></tr>`)
document.write(`<td><img src='images/goku_win.jpg'></td>`)
document.write(`</table>`)
}else{
document.write('<table class="storyboard">')
document.write(`<tr><th colspan='2'>Vegeta gana la pelea!!!</th></tr>`)
document.write(`<td><img src='images/vegeta_win.jpg'></td>`)
document.write(`</table>`)
}
// final SCOPE <<<<<</script>
Conclusión
La invitación es a los compañeros de Platzi que quieran contribuir con las tareas propuestas en el proyecto creado dentro del repositorio desde la página de Github, para ello tendrás que estar autenticado con una cuenta en github para poder clonar este repositorio.
Una vez tengas una propuesta o idea para agregar a este proyecto estaré atento para realizar el merge a la rama principal del repositorio “master” y juntos crearemos la mejor versiónde este juego.