Refactor del mapa de juego
Clase 7 de 24 • Taller Práctico de JavaScript: ¡Crea tu Primer Videojuego!
Contenido del curso
Clase 7 de 24 • Taller Práctico de JavaScript: ¡Crea tu Primer Videojuego!
Contenido del curso
Tommy Toala Cox
Juan Castro
Hector Maluy Fernandez
Andriw Jose Rollo Castro
Raúl Adolfo Sánchez Rodríguez
Angel David Velasco Bonifaz
Miguel Negron Garcia
Luis Oscar Sequera Contreras
Alejandro Ramos
Henry Alexander Velásquez Rosas
sergio andres navarro melendez
Steven García
Paula Inés Cudicio
Fabio Escobar
Juan Carlos Montilla Sánchez
Francisco Ponce
Juan Carlos Montilla Sánchez
Fernando Gonzalez
Kevin Daniel Hincapie Lumbaque
Juan Pablo Cortés
Enzo Diego Deorazio
Victor Hugo Cruz Carballo
Axel Enrique Galeed Gutierrez
Jhon Eduard Bocanegra Ortiz
JeanCarlos Atoche Pascual
Hola!
Lo que hice diferente fue crear un objeto literal del mapa, para poder luego solo usar sus funciones o cambiar las propiedades, que en este caso sería el nivel. Con este objeto literal creado, ya cuando se ejecuta el juego, solo especifico el nivel y luego le digo que lo renderice. El otro punto diferente al del profe, es que para limpiar y transformar al mapa como lo necesito, use un match y expresiones regulares, para encontrar directamente cada fila. Por lo demás está todo igual.
const canvas = document.getElementById('game'); const game = canvas.getContext("2d"); let canvasSize; let canvasElement; //creo un objeto literal del mapa, y agrego una propiedad que es el nivel y otra que es una funcion render const map={ lvl:0, render:function () { if(this.lvl>=maps.length){ return console.log ("Ese mapa no existe") } // Encontramos el mapa y lo preparamos como queremos const Map = maps[this.lvl].match(/[IXO\-]+/g) .map(a => a.split("")) // le configuramos las propeidades de los elementos que vamos a dibujar game.font = canvasElement + "px Verdana" game.textAlign = "end" // recorremos el mapa para poder obtener las coordenadas de cada una de las posiciones que necesitamos Map.forEach((x, xi) => { x.forEach((y, yi) => { const posX = canvasElement * (xi + 1) const posY = canvasElement * (yi + 1) game.fillText(emojis[y], posY, posX) }) }) } } window.addEventListener('load', setCanvasSize); window.addEventListener('resize', setCanvasSize) function setCanvasSize() { window.innerHeight > window.innerWidth ? canvasSize = window.innerWidth * 0.9 : canvasSize = window.innerHeight * 0.7; canvas.setAttribute("height", canvasSize) canvas.setAttribute("width", canvasSize) canvasElement = canvasSize / 10; startGame() } function startGame() { map.lvl=1 map.render() }
Me gusta mucho esta propuesta :star:
¡Me urge aprender expresiones regulares!
Para evitar complicarnos con el índice +1 al momento de renderizar los elementos, podemos utilizar los métodos del canvas: game.textBaseLine define la posición vertical del texto en el canvas, recibe varios atributos que puedes revisar en la documentación. El valor 'top' hace que nuestro tome como superior la ubicación que le damos, de esta forma, si la coordenada y tiene el valor de 0, el texto no se pondrá por encima sino por debajo del 0, así es como quedan alineados de tal forma que se vean completamente en el canvas.
Resumen de la clase
Explicación del código
Test
Para simplificar el reto. Solo con el mapRows se puede realizar el recorrimiento. Ya que se puede recorrer un string colocando el indice de la letra en la variable. ejemplo mapRows[0][0] me daria la primar letra del primer array. Ya no se necesitaría usar mapRowCols.
const mapRows = map.trim().split('\n'); mapRows.forEach((row, rowIndex) => { for (let col = 0; col < row.length; col++) { const emoji = emojis[row[col]]; const posX = elementsSize * (col + 1) + 5; const posY = elementsSize * (rowIndex + 1) - 10; game.fillText(emoji, posX, posY); } });
XD soy el unico que toma los cursos de JS sin saber crear una civilizacion super avanzada con dicho lenguaje?
Para los que tienen aún problemas en que los emojis se salen de su pantalla (aún no he averiguado exactamente el por qué sucederá esto) pero la solución que descubrí fue modificar los valores que se suman con los índices de "col" y "row", por ejemplo, en el código de JuanDc es:
const posX = elementsSize * (colI + 1); const posY = elementsSize * (rowI + 1);
Pero yo le moví y moví los valores hasta que encajaron completamente todos los emojis dentro de los bordes del canvas, quedando el ciclo así:
mapRowCols.forEach((row, rowI) => { row.forEach((col, colI) => { const emoji = emojis[col]; const posX = elementsSize * (colI + 1.2); const posY = elementsSize * (rowI + 0.85); game.fillText(emoji, posX, posY); }); });
Dediquen un rato a buscar los valores que sean acordes a su código, yo probé todo el responsive y funcionó perfecto incluso cambiando literalmente de monitor, así que me funciona y lo dejaré quieto así 😅.
GRACIAS! (mientras funcione no se toca XD)
Lo mismo pero más barato :v
No me gustaba que todo quedara algo descuadrado, así que aplique una técnica hecha por un compañero en otra clase y cree las variables según mi criterio para entenderlas mejor:
const canvas = document.querySelector('#game'); const game = canvas.getContext('2d'); let canvas_size; let elements_size; let map; let emoji; let x; let y; window.addEventListener('load', calculate_canvas_size); window.addEventListener('resize', calculate_canvas_size); function calculate_canvas_size(){ window.innerHeight > window.innerWidth ? canvas_size = window.innerWidth * 0.8 : canvas_size = window.innerHeight * 0.8 canvas.setAttribute('width', canvas_size); canvas.setAttribute('height', canvas_size); calculate_elements_size(); } function calculate_elements_size(){ elements_size = (canvas_size * 0.1) - 1; game.font = `${elements_size}px Verdana`; map = (maps[0].trim().split('\n')).map(x => x.trim().split('')); console.log({map}); map.forEach((row, ri) => { // element, index row.forEach((col, ci) => { emoji = emojis[col]; x = elements_size * ci; y = elements_size * (ri+1); game.fillText(emoji, x, y); }); }); } // registro de la consola: console log
Volví despues de una semana, cada vez que veo esta clase y no la entendia me desmotivaba, ya despues de la quinta vez viendola logre entender el codigo. Recomienan ver un curso antes de este que te permita entenedr mejor lo que queda de curso?
Este es una duda que me surgio en mis inicios en el Desarrollo, ¿porque un forEach y no un For normal o un map o cualquier metodo para recorer arrays?, y bien se hacen sobre todo por cuestiones de legibilidad del codigo, un for normal tiene mejor rendimiento en cuestion de CPU, sin embargo los metodos implicitos que poseen ya los lenguajes 1: nos ahorran tiempo para no estar declarnado los ciclos a manito el famoso no volver a inventar la rueda, y 2: los hace mucho mas entendible al momento de leer el codigo. (claramente ya en cuando avances mas te daras cuenta que hay mas factores que se tienen que tomar en cuenta como la mutabilidad pero principalmente estos dos primeros son los mas importantes cuando empiezas y te hacen bolas los ciclos) o ami me sirvio por lo menos
Yo en el paso anterior ya había definido una función para obtener los elementos del mapa
function getMaps(level) { const mapRows = maps[level].trim().split('\n'); const mapCol = mapRows.map(row => row.trim().split('')); return mapCol }
Pero lo del ciclo anidado de for no lo pude desenmarañar sola 🤭🤭
Antes de ver la clase lo hice con un ciclo FOR y .MAP, porque no conocia bien el metodo FOREACH. Asi me habia quedado:
const canvas = document.querySelector("#game"); const context = canvas.getContext("2d"); window.addEventListener("load", setCanvasSize); window.addEventListener("resize", setCanvasSize); let canvasSize; let elementsSize; function startGame() { context.textAlign = "end"; context.font = elementsSize + "px Verdana"; const map = maps[2]; const cleanMap = map.trim().split("\n"); const matrixMap = cleanMap.map((row) => row.trim().split("")); console.log(matrixMap); for (let i = 0; i < 10; i++) { matrixMap.map((row, indice) => context.fillText( emojis[row[i]], elementsSize * (i + 1), elementsSize * (indice + 1) ) ); } } // context.fillRect(100, 100, 1, 1); // context.clearRect(50, 0, 100, 50); // context.font = "30px Cabin"; // context.fillStyle = "green"; // context.textAlign = "center"; // context.fillText("Fabio", 100, 100); function setCanvasSize() { canvasSize; if (window.innerWidth >= window.innerHeight) { canvasSize = innerHeight * 0.8; } else { canvasSize = innerWidth * 0.8; } canvas.setAttribute("width", canvasSize); canvas.setAttribute("height", canvasSize); elementsSize = canvasSize * 0.1; startGame(); }
🤔 ¿En el curso de manipulación de arrays enseñan eso de los array multidimensionales?
Hmmm me parece que no, si no mal recuerdo hay una parte que hace el tratamiento de arrays con "n" dimensiones, pero no estoy seguro si eso sea lo que buscas.
Te comparto las clases. https://platzi.com/clases/2461-arrays/40883-flat/ https://platzi.com/clases/2461-arrays/41334-flatmap/
Gracias, Francisco. Los veré y te comento si me funcionó
const mapaPrueba = maps[2].split('') let fila = 1; let columna = 0;
mapaPrueba.forEach(a =>{
if (a == '-' || a == 'O' || a == 'X' || a == 'I'){
if(columna < 10){ columna = columna +1} else{columna = 1}
game.fillText(emojis[a],elementosTamanio * (columna - 1), elementosTamanio * fila)
if(columna ==10){ fila = fila +1}
}
})
Algo que me alegra de ir avanzando en Js es que empiezas a tener mas iniciativa por modificar el codigo que enseñan segun creas conveniente. En mi caso, mi codigo quedo asi:
function initialPaint(blocksSize) { game.font = blocksSize * 0.9 + "px impact"; const map = maps[2].trim(); const mapRows = map.split("\n").map((row) => row.trim()); for (let row = 0.85; row <= mapRows.length; row++) { for (let column = 0.05; column < 10; column++) { const emoji = emojis[mapRows[Math.floor(row)][Math.floor(column)]]; game.fillText(emoji, blocksSize * column, blocksSize * row); } } }
Las principales diferencias son:
La desventaja es que debido a que la variable del for no es un entero debo usar Math.floor para convertirlo y usarlo para ubicar el emoji en el array.
Yo lo intenté con dos ciclos for of y fallé. tengo el mismo código del profe solo cambié una parte del código para centras las imágenes dentro del canva.
function startGame () { console.log({canvasSize, elementsSize}) game.font = elementsSize + 'px Verdana'; game.textAlign = 'center'; const map = maps[0]; const mapRows = map.trim().split('\n'); const columnsOfmapRows = mapRows.map(row => row.trim().split('')); columnsOfmapRows.forEach((row, rowIndex) => { row.forEach((column, columnIndex) => { const emoji = emojis[column]; const positionX = (elementsSize * 0.93) * (columnIndex + 1) ; const positionY = (elementsSize * 0.97) * (rowIndex + 1); game.fillText(emoji, positionX, positionY) console.log(rowIndex) }); });
Hola, Yo pude hacerlo usando map y me resulto mucho mas facil
mapRowsCols.map((row, rowIndex) => { return row.map((col, colIndex) => { game.fillText(emojis[col], elementsSize * colIndex, elementsSize * (rowIndex + 1)); }); });
function startGame(){ game.font = elemtSize + 'px Verdana'; game.textAlign = 'end'; // for (let i = 1; i <= 10; i++) { // game.fillText(emojis['X'], elemtSize*i+4, elemtSize-10); // } const map = maps[0]; const mapRow = map.trim().split('\n'); const mapRowCol = mapRow.map(row => row.trim().split('')); // console.log({mapRowCol}) // for (let x = 1; x <= 10; x++) { // for (let y = 1; y <= 10; y++) { // game.fillText(emojis[mapRowCol[y-1][x-1]],elemtSize*x,elemtSize*y-10); // } // } mapRowCol.forEach((row, x)=>{ row.forEach((column, y)=>{ const emoji=emojis[column] game.fillText(emoji,elemtSize*(y+1),elemtSize*(x+1)-10); }) }) }
Les comparto mi solución para esta clase. :D
He utilizado un ciclo forque cuenta hasta 100 para poder renderizar los caracteres y una serie de contadores que me ayudan a saber en la posición en la que se tienen que dibujar cada carácter.
Les comparto los archivos .jscon los que estoy trabajando, el resultado que obtuve y el link del repositorio en GitHub.
maps.js
export const EMOJIS = { '-' : ' ', 'O' : '💻', 'X' : '👾', 'I' : '✅', 'PLAYER' : '👨💻', 'BOMB_COLLISION' : '💥', 'GAME_OVER' : '👎', 'WIN' : '🏆', 'LIFE' : '❤️' }; const MAPS = []; MAPS.push(` IXXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX OXXXXXXXXX `); MAPS.push(` O--XXXXXXX X--XXXXXXX XX----XXXX X--XX-XXXX X-XXX--XXX X-XXXX-XXX XX--XX--XX XX--XXX-XX XXXX---IXX XXXXXXXXXX `); MAPS.push(` I-----XXXX XXXXX-XXXX XX----XXXX XX-XXXXXXX XX-----XXX XXXXXX-XXX XX-----XXX XX-XXXXXXX XX-----OXX XXXXXXXXXX `); MAPS.push(` XXXXX----I XXXXX-XXXX XX----XXXX XX-XXXXXXX ------XXXX -XXXXX-XXX -------XXX X-XXXXXXXX X-----OXXX XXXXXXXXXX `); export const MAPS_USE = MAPS.map(map => map.trim().replaceAll(' ', ''));
game.js
import { EMOJIS, MAPS_USE as MAP } from './maps.js'; const startGame = (fontSize, elementSize) => { GAME.font = `${fontSize}px sans-serif`; GAME.textAlign = 'left'; let yDraw = elementSize; let xPositionEmoji = 0; let yPositionEmoji = 0; const MAP_EMOJIS = MAP[1] .split('\n') .map(row => row.split('')); for (let i = 0; i < 100; i++) { if (i % 10 === 0 && i !== 0) { yPositionEmoji++; xPositionEmoji = 0; yDraw = elementSize * (yPositionEmoji + 1) * 0.98; } const X_DRAW = elementSize * xPositionEmoji * 1.015; const EMOJI_DRAW = MAP_EMOJIS[yPositionEmoji][xPositionEmoji]; GAME.fillText(EMOJIS[EMOJI_DRAW], X_DRAW, yDraw); xPositionEmoji++; } }; const setCanvasSize = () => { const WIDTH_WINDOWS = window.innerWidth; const HEIGHT_WINDOWS = window.innerHeight; const IS_WIDTH_SMALLER = WIDTH_WINDOWS < HEIGHT_WINDOWS && WIDTH_WINDOWS < 550; const WIDTH_BASE = window.innerWidth * 0.9; const HEIGHT_BASE = window.innerHeight * 0.6; const SIDE_CANVAS = (IS_WIDTH_SMALLER) ? (WIDTH_BASE).toString() : (HEIGHT_BASE).toString(); CANVAS.setAttribute('width', SIDE_CANVAS); CANVAS.setAttribute('height', SIDE_CANVAS); const ELEMENT_SIZE = Number(SIDE_CANVAS) / 10.25; const FONT_SIZE = ELEMENT_SIZE * 0.82; startGame(FONT_SIZE, ELEMENT_SIZE); }; const CANVAS = document.querySelector('#main__game-container-id'); const GAME = CANVAS.getContext('2d'); window.addEventListener('load', setCanvasSize); window.addEventListener('resize', setCanvasSize);
Resultado
Asi lo realize:
// * Inicio del juego const startGame = () => { game.font = elementsSize + 'px Verdana'; game.textAlign = 'start'; game.textBaseline = 'top'; let map = maps[0]; map = map.trim().split('\n'); map = map.map(element => element.trim().split('')); map.forEach((row, rowIndex) => { row.forEach((col, colIndex) => { const emoji = emojis[col]; game.fillText(emoji, elementsSize * colIndex, elementsSize * rowIndex) }) }); };