Detectando colisiones con arrays
Clase 13 de 24 • Taller Práctico de JavaScript: ¡Crea tu Primer Videojuego!
Contenido del curso
Clase 13 de 24 • Taller Práctico de JavaScript: ¡Crea tu Primer Videojuego!
Contenido del curso
Manuel Araujo
Gianluca Enzo Procopio
JUAN DAVID MARIN URIBE
Angel David Velasco Bonifaz
César Augusto Cortés Labrada
Luis Palomo
marco antonio
Aaron Santiago Loyo Zabala
Emilio Sala
Valeria Soledad Paez
Eduard Barrios
Kevin Camacho
Jhon Eduard Bocanegra Ortiz
Gianluca Enzo Procopio
Luis Palomo
Gianluca Enzo Procopio
Hector Maluy Fernandez
leonardo alies fuentes
Rubén Cuello
Juan camilo piedrahita londoño
Nestor Rios Garcia
Mauricio Combariza
Alejandro Ramos
Carlos Andres Alavez Santiago
David Fernando Estacio Quiroz
David Ochoa
Ivo Zayas
Cristian Zapata
Andres Eduardo Maneiro Antunez
Tranquilo Juan, el error de los decimales me pasó desde la primera vez que probé el código jejeje. Te creemos!
X2 jajajaj
x3 Despues de un rato debugueando me di cuenta lo que pasaba, sin saber que en los ultimos 3 minutos del video estaba la explicaion jajaj
Para no volver hacer el array en cada movimiento y estar limpiando, solo coloque una variable bandera.
let flag=true;
y donde asignamos las posiciones de las bombas se pone un condicional.
if (col== "X" && flag) { bombasPosition.push({ x: posX, y: posY, }); }
y afuera del ciclo for each de las rows, colocamos la bandera en falso
flag=false;
con eso cuando se vuelva a pasar ya no agregara otra vez las bombas
Me parece una solución bastante acertada.
Si, yo pensé lo mismo. Si el array fuera más largo estaríamos renderizando siempre eso.
//contect const canvas = document.querySelector('#game'); const btnArriba = document.querySelector('#arriba'); const btnAbajo = document.querySelector('#abajo'); const btnDer = document.querySelector('#derecha'); const btnIzq = document.querySelector('#izquierda'); const juego = canvas.getContext('2d'); //signals: window.addEventListener('load', resizeEvent); window.addEventListener('resize', resizeEvent); let posJugador = undefined; let mapa = Array(); let nivel = 0; let prePosJugador = undefined; let celda_t = 0; let nivel_actual = undefined; let puntoDePartida = Number(0); let canvas_t = 0; var actualizado = Boolean(false); let explociones = Array(); let vidas = 3; function resizeEvent(){ actualizado = false; canvas_t = (window.innerWidth < window.innerHeight ? window.innerWidth : window.innerHeight) *.75 ; canvas.setAttribute('width', canvas_t); canvas.setAttribute('height',canvas_t); celda_t = canvas_t / 10; juego.textAlign='end'; juego.font = celda_t - celda_t / 10 +'px arial'; update();} function update(){ if(Victoria() && actualizado) return; cargarMapa(); clear(); paintEvent(); paintEventPlayer(); paintEventExplosion(); paintEventGameOver(); paintEventVictory(); actualizado = true;} function cargarMapa(){ if(nivel_actual == nivel || Victoria()) return; nivel_actual = nivel; mapa = map[nivel].match(/[IOX-]/g);} function clear(){ if(!actualizado) { juego.clearRect(0,0, canvas_t, canvas_t); return;} if(posJugador == prePosJugador) return; clearRect(posJugador); clearRect(prePosJugador);} function clearRect(indice){ const x = posX(indice ) - celda_t / 10; const y = posY(indice ) + celda_t / 5; juego.clearRect(x - 1 ,y , - celda_t, - (celda_t + 1));} function posY(indice){ return (~~(indice/10) + 1) * celda_t - celda_t / 5; } function posX(indice){ return (indice - (~~(indice/10) * 10) + 1) * celda_t + celda_t / 10;} function paintEvent(){ if(!actualizado && !Victoria()) mapa.forEach((char, idx) => { if(char == 'O' && posJugador == undefined) puntoDePartida = posJugador = idx; juego.fillText(emojis[char], posX(idx), posY(idx) );});} function paintEventPlayer(){ if(victoryEvent()) paintEventVictory(); else { if(posJugador != undefined && prePosJugador != posJugador){ juego.fillText(emojis[mapa[prePosJugador]],posX(prePosJugador),posY(prePosJugador)); if(collisionEvent()) juego.fillText(emojis['PLAYER'], posX(posJugador),posY(posJugador)); else paintEventGameOver();}}} function collisionEvent(){ if(vidas && mapa[posJugador] == 'X'){ explociones.push(posJugador); --vidas; juego.fillText(emojis['BOMB_COLLISION'],posX(posJugador),posY(posJugador)); posJugador = puntoDePartida;} if(!vidas) { explociones = Array(); return false;} /*juego terminado*/ return true; /*continuar con el juego*/} function paintEventExplosion(){ if(!explociones.length) return; explociones.forEach((pos) =>{ clearRect(pos); juego.fillText(emojis['BOMB_COLLISION'],posX(pos),posY(pos));});} function victoryEvent(){ if(mapa[posJugador] != 'I') return false; else if(nivel < map.length) ++nivel; explociones = Array(); if(Victoria()) return true; posJugador = prePosJugador = undefined; actualizado = false; update(); return false;} function paintEventGameOver(){ if(vidas) return; paintScreen('Game Over','GAME_OVER', '#FF2F22', '#211A27');} function paintEventVictory(){ if(!Victoria()) return; paintScreen('Has Ganado','WIN','#ffd700','#01433A');} function Victoria(){return nivel >= map.length;} function paintScreen(txt, emoji, colortxt = '#fff', backgroundColor ='#000'){ juego.clearRect(0,0, canvas_t, canvas_t); mapa.forEach((char, idx) =>{ if(char == 'X') char = emoji; juego.fillText(emojis[char],posX(idx),posY(idx));}); const fuente = celda_t - celda_t / 10 ; const media = canvas_t / 2; juego.fillStyle = backgroundColor; juego.fillRect(0, media - (fuente /2), canvas_t, fuente); juego.textAlign = 'center'; juego.fillStyle = colortxt; juego.fillText(txt, media, media + fuente/3); juego.textAlign='end';} //signals: window.addEventListener('keydown',keyMov); btnArriba.addEventListener('click', movArriba); btnAbajo.addEventListener('click', movAbajo); btnDer.addEventListener('click', movDer); btnIzq.addEventListener('click', movIzq); //slots: function keyMov(event){ switch(event.keyCode){ case 37: movIzq(); break;//izquierda case 38: movArriba(); break;//arriba case 39: movDer(); break; //derecha case 40: movAbajo(); break;//abajo default: break;}} function movArriba(){ prePosJugador = posJugador; posJugador -= 10; if(posJugador < 0) posJugador = prePosJugador; update();} function movAbajo(){ prePosJugador = posJugador; posJugador += 10; if(posJugador >= 100) posJugador = prePosJugador; update();} function movDer(){ prePosJugador = posJugador; const pered = (~~(posJugador / 10)) *10 + 10; ++posJugador; if(posJugador >= pered) posJugador = prePosJugador; update();} function movIzq(){ prePosJugador = posJugador; const pered = ~~(posJugador /10) * 10; --posJugador; if(posJugador < pered) posJugador = prePosJugador; update();}
Mucho Texto
tal vez el propósito de la clase es enseñarnos lo que no debemos hacer y buscar mejor formas de resolver los problemas que nos van surgiendo por nuestra cuenta. en los aportes hay muy buenas formas de avanzar
para no cambiar el const del array
enemyPosition.splice(0, enemyPosition.length);
jajajaja me da risa que a Juan no le parece el error pero a mi me apreció antes de que él lo mencionara y lo solucioné luego él lo menciona pero no le sale y a mi me sale cada que muevo al jugador
vaciar un array
Así me enseñó Notion IA.
const array = ['h', 'o', 'l', 'a', 'm', 'u', 'n', 'd', 'o'] array.length = 0
Obviamente funciona pero no se si es la mejor manera de hacerlo.
Para detectar una colisión hice una validación true/false con el método SOME de arrays. Lo vimos en el curso de manipulación de arrays. Pero si no lo viste, el metodo some se trata de que si al menos un elemento del array cumple con la condición, devuelve true.
if(enemyPosition.some(item => item.x == playerPosition.x && item.y == playerPosition.y)){ alert("Chocaste con una bomba") }
Juan! A mi me pasó el error que decías. Incluso lo arreglé antes que lo mencionaras y me rascaba la cabeza pensando porque a mi no me funcionaba y a vos si. Así que te entiendo jaja.
X2 jajajajaja
Mejor ponemos todas las coordenadas a dos decimales y nos quitamos de problemas.
la verdad no me funciona el código copie y pegue el tuyo y tampoco me funciono mmm no por cual motivo
Hola @leoaliesf397. ¿Podrías compartirnos tu código y una descripción un poco más detallada sobre qué es lo que no funciona? De esta manera estoy seguro de que podremos ayudarte.
Colocastes el toFixed(3)? A mi no me estaba funcionando. Pero con toFixed(3). No hubo problema
Yo tengo un contador de movimientos
let moves = 0;
Y aumenta cada vez que se mueve el jugador
function movePlayer(direction) { moves++ . . .
Asi que si integro mi contador en el condicional solo se ejecutara una vez
if (column === 'X' && moves === 0) { bombsPositions.push({ x: xPosition, y: yPosition }) }
Yo cree una funcion render, una start game y una movePlayer y en la de startGame cree el array con todos los elementos, incluyendo los espacios y los regalos y lo que pongamos después, en el render no creo ningun array. Este es mi código:
function startGame() { console.log('Mapas:', mapa, mapRows, mapRowCols) mapa = maps[level] mapRows = mapa.trim().split('\n') mapRowCols = mapRows.map(row => row.trim().split('')) if (!game) { console.error('El contexto del lienzo no está disponible'); return; } game.font= elementSize + 'px Verdana' mapRowCols.forEach((row, i) => { row.forEach((col, j) => { const emoji = emojis[col] let posX = j * elementSize let posY = (i+1) * elementSize element = { elem: col, x: posX, y: posY } mapaActual.push(element) if (col == 'O'){ playerPosition = { x: posX, y: posY } // Se crea un array con el valor y las coordenadas } game.fillText(emoji, posX, posY) movePlayer() }) }) } function renderGame() { if (!game) return; game.clearRect(0, 0, canvas.width, canvas.height); mapRowCols.forEach((row, i) => { row.forEach((col, j) => { const emoji = emojis[col]; let posX = j * elementSize; let posY = (i + 1) * elementSize; if (col === 'I'){ winPosition = { x: posX, y: posY } } game.fillText(emoji, posX, posY); }); }); } function movePlayer() { if (!game) return renderGame() game.fillText(player, playerPosition.x, playerPosition.y); mapaActual.map(item => { let actualPositionX = Math.floor(playerPosition.x) === Math.floor(item.x) let actualPositionY = Math.floor(playerPosition.y) === Math.floor(item.y) let actualPosition = actualPositionX && actualPositionY if (actualPosition){ console.log(item.elem) if(item.elem === 'X') { console.log('Perdiste') level = 0 // setCanvasSize() } if(item.elem === 'I') { console.log('Ganaste eeee') if(level > maps.length - 1){ console.log('Fin del juego Ganaste!!') } else { level += 1 mapaActual = [] startGame() } } } }) }
yo estoy usando Math.Floor en lugar del toFixed e.e
Convertí los maps a Let, agregue un index para cambiar de mapa, para checar si es bomba o regalo, convertí las coordenadas a posición de un arrglo, para solucionar el problema de los decimales, usé Math.Round.
function detectarObjeto(){ posX = Math.round(playerPosition.x/elementSize)-1; posY = Math.round(playerPosition.y/elementSize)-1; if(mapCols[posY][posX]==='X'){ playerPosition.i=0; } else if(mapCols[posY][posX]==='I'){ if(indexMap< (maps.length-1)){ indexMap++; } } }
Aquí dejo el resto del programa, donde incluso reinicio de niviel con un index al playerPosition, que se resetea cada que encuentra bomba.
Dejo todo el programa porque hice varios cambios
const canvas= document.querySelector('#game'); const game = canvas.getContext('2d'); const btnUp = document.querySelector('#up'); const btnLeft = document.querySelector('#left'); const btnRight = document.querySelector('#right'); const btnDown = document.querySelector('#down'); let canvasSize; let elementSize; let indexMap = 0; let map; let mapRows; let mapCols; let posX; let posY; const playerPosition={ x: undefined, y: undefined, i: 0, }; window.addEventListener('load', setCanvasSize); window.addEventListener('resize',setCanvasSize); function setCanvasSize(){ if (window.innerHeight > window.innerWidth){ canvasSize = window.innerWidth * 0.80; } else{ canvasSize = window.innerHeight * 0.80; } canvas.setAttribute('width', canvasSize); canvas.setAttribute('height', canvasSize); elementSize = canvasSize/10; startGame(); } function startGame(){ game.font = elementSize-10 + 'px Verdana'; game.textAlign = 'end'; convertMap(); game.clearRect(0,0,canvasSize,canvasSize); mapCols.forEach((rows,rowsI) => { rows.forEach((item, itemI)=>{ if(item == 'O' && playerPosition.i==0){ playerPosition.x = elementSize*(itemI+1); playerPosition.y = elementSize*(rowsI+1); playerPosition.i = 1; //console.log(elementSize*(itemI+1), elementSize*(rowsI+1)); } game.fillText(emojis[item], elementSize*(itemI+1), elementSize*(rowsI+1)); }); }); movePlayer(); /* for(let i=1; i<=10;i++){ for(let j=1; j<=10; j++){ game.fillText(emojis[mapCols[i-1][j-1]], elementSize*j, elementSize*i); } } */ } function convertMap(){ map = maps[indexMap]; mapRows = map.trim().split('\n'); mapCols = mapRows.map(row=>row.trim().split('')); } function movePlayer(){ game.fillText(emojis['PLAYER'], playerPosition.x, playerPosition.y); } window.addEventListener('keydown', moveByKeys); btnUp.addEventListener('click',moveUp); btnLeft.addEventListener('click',moveLeft); btnRight.addEventListener('click',moveRight); btnDown.addEventListener('click',moveDown); function moveByKeys(event){ if(event.key=='ArrowUp'){ moveUp(); } else if(event.key=='ArrowDown'){ moveDown(); } else if(event.key=='ArrowRight'){ moveRight(); } if(event.key=='ArrowLeft'){ moveLeft(); } } function moveUp(){ if(playerPosition.y>elementSize){ playerPosition.y-=elementSize; detectarObjeto(); } setCanvasSize(); } function moveLeft(){ if(playerPosition.x > elementSize+1){ playerPosition.x -= elementSize; detectarObjeto(); } setCanvasSize(); } function moveRight(){ if(playerPosition.x<elementSize*10){ playerPosition.x+=elementSize; detectarObjeto(); } setCanvasSize(); } function moveDown(){ if(playerPosition.y < elementSize*10){ playerPosition.y+=elementSize; detectarObjeto(); } setCanvasSize(); } function detectarObjeto(){ posX = Math.round(playerPosition.x/elementSize)-1; posY = Math.round(playerPosition.y/elementSize)-1; if(mapCols[posY][posX]==='X'){ playerPosition.i=0; } else if(mapCols[posY][posX]==='I'){ if(indexMap< (maps.length-1)){ indexMap++; } } }
Yo decidí hacer algo diferente, en lugar de guardar la posición del jugador en pixeles guardé la posición por filas y columnas:
if(!playerPosition.X && key == 'O'){ playerPosition.X = i; playerPosition.Y = j; }
Uso esta funcion para renderizar al jugador:
function renderPlayer() { console.log(playerPosition) game.fillText( emojis['PLAYER'] , Math.floor(elementSize * playerPosition.X+1), Math.floor(elementSize * playerPosition.Y+1) ); }
Y luego, para detectar las colisiones, uso un condicional en cada movimiento que invoca a una función:
function isColliding(newRow, newColumn) { switch (columns[newRow][newColumn]) { case 'O': console.log('Returned to the start'); (nivel-1 < 0) ? nivel=nivel : nivel-=1; break; case 'X': console.log('Collided'); return true; case 'I': console.log('Finished level'); (nivel+1 == maps.length) ? nivel=nivel : nivel+=1; break; default: break; } return false }
Para evitar el problema de que las posiciones no coincidan, lo solucione almacenando los indices de cada bomba y del regalo en lugar de su valor "x" y "y"
if (col == "I") { giftIndex = { ix: colI, iy: rowI, }; } if (col == "X") { bombIndexes.push({ ix: colI, iy: rowI }); }
De igual manera mi objeto de player position almacena el indice en el que se encuentra
if (col == "O") { if (!playerPosition.x && !playerPosition.y) { playerPosition = { x: posX, y: posY, ix: colI, iy: rowI, }; } }
Y finalmente se realiza la comparación con los índices
function checkCollision() { const giftCollitionX = giftIndex.ix == playerPosition.ix; const giftCollitionY = giftIndex.iy == playerPosition.iy; const isGift = giftCollitionX && giftCollitionY; if (isGift) { console.log("Ganaste!"); return; } const isBomb = bombIndexes.find((bomb) => { const collitionX = bomb.ix == playerPosition.ix; const collitionY = bomb.iy == playerPosition.iy; return collitionX && collitionY; }); if (isBomb) { console.log("Colision"); return; } }
Espero haber ayudado a alguien 😉
Hola amiguitos!!! comparto mi solución que creo que es más práctica:
if (col == 'O') { if (!playerPosition.x && !playerPosition.y){ playerPosition.x = posX playerPosition.y = posY } } else if(col == 'I'){ checkGiftCollision(posX, posY) } else if(col == 'X'){ checkBombCollision(posX, posY) }
Cuando estoy fijándome dentro del forEach() si el indice actual es una bomba o un regalo, llamo a las funciones 'chackBombCollision()' o 'checkGiftCollision()' dependiedno el caso. Estas reciben como parámetro la posición actual de 'col' (posX y posY).
function checkGiftCollision(giftX, giftY){ if (giftY.toFixed(3) == playerPosition.y.toFixed(3) && giftX.toFixed(3) == playerPosition.x.toFixed(3)) { console.log('Felicidades'); } }
function checkBombCollision(bombX, bombY) { if (bombY.toFixed(3) == playerPosition.y.toFixed(3) && bombX.toFixed(3) == playerPosition.x.toFixed(3)) { console.log('Perdiste'); } }
Entonces comparo 'playerPosition.x' y 'playerPosition.y' con 'giftX' y 'giftY' o 'bombX' y 'bombY' y listo el pollo pelada la gallina!!! Por ahora creo que funciona, no se si se va a complicar más adelante. Aparte puedo prescindir de los objetos 'giftPosition' y 'enemyPositions' que necesitaba el profe para su solución.
No es que no le salga el error, es que la consola no saca el error sino que no se ejecuta el console.log
mi solucion es que no cree ningun array, me aparecio mas simple realizar condicionales cuando se renderiza el mapa
la logica es mas simple, cuando se realiza un movimiento del jugador llama la function de render, y en el renderizado compara tan la "I" que es el regalo y la "X" que es el obstaciulo, llama a una funcion de vicoria o de derrota
function renderMap() { game.font = `${elementsSize}px Verdana`; game.textAlign = 'end'; const map = maps[level]; game.clearRect(0, 0, canvasSize, canvasSize); const mapRows = map.trim().split('\n'); //trim() es un metodo que remueve los espacios en blanco de los treing y lo devuelve en un nuevo `string` sin modificar el original, el .split, elimina el caracter que se le indique en el argumento const mapRowsCols = mapRows.map(row => row.trim().split('')); game.clearRect(0, 0, canvasSize, canvasSize); mapRowsCols.forEach((row, rowIndex) => { row.forEach((col, colIndex) => { const emoji = emojis[col]; const posX = (elementsSize * (colIndex + 1))+12; const posY = elementsSize * (rowIndex + 1); if (col == 'O') { playerPosition.initialX = posX; playerPosition.initialY = posY; }; if (col == 'I') { if(playerPosition.x == posX && playerPosition.y == posY) { win(); return } } if (col == 'X') { if(playerPosition.x == posX && playerPosition.y == posY) { lose() return } } game.fillText(emoji, posX, posY); }); }); return