No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Depurando errores del juego

20/24
Recursos

Aportes 36

Preguntas 1

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

si esta complicado mover la pantalla mientras juegas XD
 

no mas por que mi compu no me aguanta el obs si no grabaría como queda con las animaciones mientras juego XD en fin.
 
si quieren evitar un dolor de cabeza con las posiciones y las colisiones, animaciones …o si le puse animaciones a esto cuando explotan las bombas y cuando mueres y mas cosas locas como sea esta es la solución para hacerles la vida mas sencilla
 

 
es tan fácil como transformar el mapa en un array lineal y solo crear las funciones de posX y poxY usando el índice y YA no hay mas es los mas fácil del mundo en fin espero le sirva a alguien

Yo lo solucioné creando otra objeto para la posición del jugador
Utilizo el objeto playerPostion para guardar el índice de la 🚪 y mi funciónmoverPlayer quedo así:
Por último, por cada movimiento con las teclas o los botones solo aumento en uno el valor de playerPostion

Les comparto mi código para que lo puedan verificar.

Reposicionar al player al cambiar el tamaño de la viewport

Solución escrita:

  • Tuve que guardar el tamaño anterior del canvas, en mi caso toda la información del canvas estaba guardada globalmente en un objeto identificado como objCanvas, por lo que solo tuve que almacenarlo al inicio de la función setCanvasSize, antes de que se le asignara un nuevo tamaño al canvas. Al nuevo atributo lo llamé “oldSize”:

    .
  • Para reposicionar al player desarrolle una función que calculara los porcentajes equivalentes a las posiciones X e Y del player, a partir del tamaño anterior del canvas(oldSize). Luego ambos porcentajes llevarlos a su medida equivalente con el nuevo tamaño del canvas, lo que será suficiente para reposicionar al player. Para calcular las medidas equivalentes y porcentajes equivalentes solo utilicé la regla de tres.

    .
  • El llamado de la nueva función (repositionPlayer) se ejecutó al final de la función setCanvasSize antes de renderizar, dibujar o mostrará el juego.

La verdad fui resolviendo todos esos bugs mientras seguía el curso, y de hecho ahí faltan mencionar;
Que el tiempo que agarra para ponerlo en el record, está un poco desfasado, por unos segundo, por lo que hay que hacer unos pequeños ajuste.
Así quedo mi repo
https://estrelladlm.github.io/game-with-js/

Bueno vamos con los errores que no he visto aun solucionados o que puedo ofrecer una solucion alternativa.

Bug # 1 decimales incorrectos al reajustar el canvasSize:

function resizeCanvas () {
    windowHeight = window.innerHeight * 0.8;
    windowWidth = window.innerWidth * 0.8;
    //Dependiendo del tamaño de la pantalla, va a colocar el tamaño cuadrado del canvas
    //Al dividir entre 10 y luego aproximar el valor a un entero garantiza que el canvas será un entero múltiplo de 10. Finalmente se multiplica la expresión por 10 para obtener el dato real del canvas
    //Con Math.ceil nos ahorramos el problema de los decimales
    if (window.innerHeight > window.innerWidth) {
        if ((windowWidth % 10) !== 0) {
             canvasSize = Math.ceil(windowWidth / 10) * 10;
        } else {
             canvasSize = windowWidth;
        }} 
    else {
        if ((windowHeight % 10) !== 0) {
             canvasSize = Math.ceil(windowHeight / 10) * 10;
        } else {
             canvasSize = windowHeight;
        }
    }

     canvas.setAttribute('width', canvasSize);
     canvas.setAttribute('height', canvasSize);
     elementsSize = (canvasSize / 10);
     startGame(lvl);
 }

En vez de ponerme a retocar el valor elementalSize recortando decimales me decidi por ir a la raiz del error, el canvasSize, aplicando lo mostrado arriba en los casos de resoluciones extrañas que probe el valor de elementsSize en todo momento es un entero por lo que no genera ningun tipo de problema en las coordenadas.

/////////////

Bug #2 Al perder todas las vidas y volver a comenzar sin refrescar la pagina el juego no termina el timer al llegar a la meta del ultimo mapa:

function bombsDetection () {
    //Buscamos una coincidencia en el array contenido en la propiedad lvl(ej. 0) y en caso de que la posicion del jugador coincida con alguna de las bombas reseteamos su posicion al inicio del mapa 
    bombsDetected = bombsPosition[lvl].find(bomb => bomb[0] == playerPosition.x && bomb[1] == playerPosition.y);
    if (bombsDetected) {
        console.log("collision detected")
        lives--;
        if (lives <= 0) {
            lvl = 0;
            lives = 3;
            timeStart = undefined;
            clearInterval(timeInterval)
        }

        playerPosition.x =  undefined;
        playerPosition.y =  undefined;
        startGame(lvl);
      }
}

Este fue muy sencillo de resolver pero raro de encontrar. Debemos buscar en nuestro codigo donde reseteamos el juego si perdemos todas las vidas y observar que cuando sucede eso nosotros establecemos como undefined a la variable timeStart, pero ademas debemos agregarle un clearInterval para volver a comenzar desde cero el tiempo del juego ya que sino el juego abre otra instancia de timeInterval y esa no podemos detenerla al llegar a la meta, lo cual seria el comportamiento normal de nuesto juego.

/////////////

Bug #3 Desfasaje entre el tiempo en que completamos el juego y el record que seteamos en localStorage.

Si analizamos nuestro codigo, durante la clase definimos una variable llamada: timePlayer, la cual no manipulamos en todo el ciclo de ejecucion:

let lvl = 0;
let lives = 3;
let timeStart;
let timePlayer;
let timeInterval;

Bueno esa variable utilizaremos para alojar el contenido que insertaremos en el local storage de la siguiente manera:

En la parte visual:

function timeSystem() {
    timePlayer = formatTime(Date.now() - timeStart);
    spanTime.innerHTML = timePlayer;
}


function showRecord(){
    spanRecord.innerHTML= localStorage.getItem('record_time');
}

Y dentro de la logica:

function gameWin() {
    console.log('¡Terminaste el juego!');
    clearInterval(timeInterval)
    
    const recordTime = localStorage.getItem('record_time');
    const playerTime = timePlayer;

    if (recordTime) {
        if (recordTime >= playerTime) {
            localStorage.setItem('record_time', playerTime);
            pResult.innerHTML = 'Superaste el record';
        } else {
            pResult.innerHTML = 'No superaste el record';
        }
    } else {
        localStorage.setItem('record_time', playerTime);
        pResult.innerHTML = 'Nuevo record, ahora trata de superarlo';
    }
    showRecord()
}

Si bien de la manera que vimos en clase trabajamos con el mismo valor, asumo que lo consulta con un pequeño desfasaje al momento de setear el visual y setar el localStorage, haciendolo de esta forma nos aseguramos que tanto uno como otro consulten el mismo valor (timePlayer).

Para solucionar un poco los errores de los decimales, se le puede poner un toFixed(0) al ancho y alto del canvas.

Creo 2 variables:

let ultima_x;
let ultima_y;

Dentro de la función movePlayer, le asigno a la variable ultima_x el valor de playerPosition.x, y a la variable ultima_y le asigno el valor de playerPosition.y.

    ultima_x = playerPosition.x;
    ultima_y = playerPosition.y;

Entonces dentro de la funcion setCanvasSize le asigno a playerPosition.x y playerPosition.y los últimos valores que tuvieron y que capturé en movePlayer:

        playerPosition.x = ultima_x;
        playerPosition.y = ultima_y;

Esto funciona si se hace resize de una sola dimensión a la vez, ya que en este caso las dimensiones internas del canvas se mantienen constantes. Pero si se hace resize en ambas dimensiones de la pantalla al mismo tiempo, la dimensión interna del canvas cambia constantemente y el player se desplaza.
Para resolver esto último, creo una tercera variable

let canvasSize1;

A la cual le asigno un valor con el mismo condicional del canvasSize original pero dentro de la función movePlayer

    if(window.innerHeight > window.innerWidth) {
        canvasSize1 = window.innerWidth * 0.7;
    }
    else {
        canvasSize1 = window.innerHeight * 0.7;
    }

Ahora dentro de la función setCanvasSize, modifico lo que había escrito, colocando la asignación de valores que había hecho dentro de la condición de que ambos valores de dimension del canvas sean iguales, pero que si la dimension interna del canvas cambió, entonces se utilce un factor de ajuste

    if(canvasSize == canvasSize1) {
        playerPosition.x = ultima_x;
        playerPosition.y = ultima_y;
    }
    else {
        playerPosition.x = ultima_x*(canvasSize/canvasSize1);
        playerPosition.y = ultima_y*(canvasSize/canvasSize1);
    }

cambie unos textos y variables a ingles! y asi fue como con la funcion fixNumber() arregle el codigo que el profe menciono 😃

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./styles.css">
    <title>VideoGame Project</title>
</head>
<body>
    <div class="game-container">
        <canvas id="game"></canvas>
        <div class="btns">
            <button id="up">Up</button>
            <button id="left">Left</button>
            <button id="right">Right</button>
            <button id="down">Down</button>
        </div>
        <div class="messages">
            <p>lives: <span id="lives"></span></p>
            <p>Time: ⏰: <span id="time"></span></p>
            <p>Record: 🏁<span id="record"></span></p>
            <p id="result"></p>
        </div>
    </div>
    <script src="./maps.js"></script>
    <script src="./game.js"></script>
</body>
</html>

game.js

const canvas = document.querySelector('#game');
const game = canvas.getContext('2d');
const upButton = document.querySelector ('#up');
const rightButton = document.querySelector ('#right');
const leftButton = document.querySelector ('#left');
const downButton = document.querySelector ('#down');
const spanLives = document.querySelector('#lives')
const spanTime = document.querySelector('#time');
const spanRecord = document.querySelector('#record');
const pResult = document.querySelector('#result');

let canvasSize;
let elementsSize;
let level = 0;
let lives = 3;

let timeStart;
let timePlayer;
let timeInterval;


const playerPosition = {
  x: undefined,
  y: undefined,
};
const giftPosition = {
  x: undefined,
  y: undefined,
};
let enemyPositions = [];

window.addEventListener('load', setCanvasSize);
window.addEventListener('resize', setCanvasSize);

function fixNumber(n) {
  return Number(n.toFixed(0));
}

function setCanvasSize() {
  if (window.innerHeight > window.innerWidth) {
    canvasSize = window.innerWidth * 0.7;
  } else {
    canvasSize = window.innerHeight * 0.7;
  }
  canvasSize = Number(canvasSize.toFixed(0));
  
  canvas.setAttribute('width', canvasSize);
  canvas.setAttribute('height', canvasSize);
  
  elementsSize = fixNumber(canvasSize / 10);
  
  playerPosition.x = undefined;
  playerPosition.y = undefined;

  startGame();
}

function startGame() {
  console.log({ canvasSize, elementsSize });
  console.log(window.innerWidth, window.innerHeight);
  game.font = elementsSize + 'px Verdana';
  game.textAlign = 'end';
  
  const map = maps[level];
  if (!map) {
    gameWin();
    return;
  }

  if (!timeStart) {
    timeStart = Date.now();
    timeInterval = setInterval(showTime, 100);
    showRecord();
  }
  const mapRows = map.trim().split('\n');
  const mapRowCols = mapRows.map(row => row.trim().split(''));
  console.log({map, mapRows, mapRowCols});

  showLives();
  
  enemyPositions = [];

  game.clearRect(0, 0, canvasSize, canvasSize);
  mapRowCols.forEach((row, rowI) => {
    row.forEach((col, colI) => {
      const emoji = emojis[col];
      const posX = fixNumber(elementsSize * (colI + 1));
      const posY = fixNumber(elementsSize * (rowI + 1));

      if (col == 'O') {
        if (!playerPosition.x && !playerPosition.y) {
          playerPosition.x = fixNumber(posX);
          playerPosition.y = fixNumber(posY);
          console.log({playerPosition});
        }
      } else if (col == 'I') {
        giftPosition.x = fixNumber(posX);
        giftPosition.y = fixNumber(posY);
      } else if(col == 'X') {
        enemyPositions.push ({
          x: fixNumber(posX),
          y: fixNumber(posY),
        });
      }
      
      game.fillText(emoji, posX, posY);
    });
  });

  movePlayer();
}

function movePlayer() {
  const giftCollisionX = playerPosition.x.toFixed(3) == giftPosition.x.toFixed(3);
  const giftCollisionY = playerPosition.y.toFixed(3) == giftPosition.y.toFixed(3);
  const giftCollision = giftCollisionX && giftCollisionY;

  if(giftCollision) {
    winningLevel();
  } 
  const enemyCollision = enemyPositions.find(enemy => {
    const enemyCollisionX =  enemy.x.toFixed(3) == playerPosition.x.toFixed(3);
    const enemyCollisionY = enemy.y.toFixed(3) == playerPosition.y.toFixed(3);
    return enemyCollisionX && enemyCollisionY;
  });

  if(enemyCollision) {
    levelFail();
  }

  game.fillText(emojis['PLAYER'], playerPosition.x, playerPosition.y);
  
}

function winningLevel() {
  console.log('Level up');
  level ++;
  startGame();
}

function levelFail() {
  console.log("ENEMY!")
  lives--;

  console.log(lives)
  if(lives <= 0) {
    level = 0;
    lives = 3;
    timeStart=  undefined;
  }
  playerPosition.x = undefined;
  playerPosition.y = undefined;
  startGame();
}
function gameWin() {
  console.log('¡Terminaste el juego!');
  clearInterval(timeInterval);

  const recordTime = localStorage.getItem('record_time');
  const playerTime = Date.now() - timeStart;

  if (recordTime) {
    if (recordTime >= playerTime) {
      localStorage.setItem('record_time', playerTime)
      pResult.innerHTML ='you made a new record! Congratulations!';
     } else {
      pResult.innerHTML ='Sorry, this is not a record';
     }
  } else {
    localStorage.setItem('record_time', playerTime)
    pResult.innerHTML = 'first time playing? Try to get a new record';
  }

  console.log({recordTime, playerTime});
}

function showLives() {
  const heartsArray = Array(lives).fill(emojis['HEART']);
  console.log(heartsArray);

  spanLives.innerHTML = "";
  heartsArray.forEach(heart => spanLives.append(heart))
  
}
function showTime() {
  spanTime.innerHTML = Date.now() - timeStart;
}
function showRecord() {
  spanRecord.innerHTML = localStorage.getItem('record_time');
}

  //rowI and colI are the index
  
  // for (let row = 1; row <= 10; row++) {
    //   for (let col = 1; col <= 10; col++) {
      //     game.fillText(emojis[mapRowCols[row - 1][col - 1]], elementsSize * col, elementsSize * row);
      //   }
      // }
    
window.addEventListener('keydown', moveByKeys);
upButton.addEventListener('click', moveUp);
rightButton.addEventListener('click', moveRight);
leftButton.addEventListener('click', moveLeft);
downButton.addEventListener('click', moveDown);

function moveByKeys (event) {
  if(event.key == 'ArrowUp') moveUp();
   else if (event.key == 'ArrowRight') moveRight();
   else if (event.key == 'ArrowLeft')  moveLeft();
   else if (event.key == 'ArrowDown') moveDown();
}
  //console.log(event)

  function moveUp() {
    console.log('Moving up');
  
    if ((playerPosition.y - elementsSize) < elementsSize) {
      console.log('OUT');
    } else {
      playerPosition.y -= elementsSize;
      startGame();
    }
  }
  function moveLeft() {
    console.log('moving left');
  
    if ((playerPosition.x - elementsSize) < 20) {
      console.log('OUT');
    } else {
      playerPosition.x -= elementsSize;
      startGame();
    }
  }
  function moveRight() {
    console.log('moving right');
  
    if ((playerPosition.x + elementsSize) > canvasSize) {
      console.log(elementsSize)
      console.log('OUT');
    } else {
      playerPosition.x += elementsSize;
      startGame();
    }
  }
  function moveDown() {
    console.log('moving down');
    
    if ((playerPosition.y + elementsSize) > canvasSize) {
      console.log('OUT');
    } else {
      playerPosition.y += elementsSize;
      startGame();
    }
  }

Yo… desde el principio me di cuenta que tendríamos problemas con los decimales y los pixeles. Mi solución fue guardar los indices de los ciclos cuando recorremos el mapa para manejar el canvas como una matriz 10*10

rows.forEach((row, j) => {
        columns[j].forEach((key, i) => {
            emoji = emojis[key];

	    // Position on pixels
            posX = Math.floor(elementSize * (i + 1));
            posY = Math.floor(elementSize * (j + 1));

            game.fillText(emoji, posX, posY);

            // Set origin position to render the player in the very first position
            if (!playerPosition.X && key == 'O') {
                playerPosition.X = i + 1;
                playerPosition.Y = j + 1;
            }

        });
    });

Y ya luego es más sencillo todo la verdad, simplemente renderizar al jugador:

function renderPlayer() {
    //console.log(playerPosition)
    game.fillText(
        emojis['PLAYER'],
        // To draw the player we need to calculate the position on pixels
        Math.floor(elementSize * playerPosition.X + 1),
        Math.floor(elementSize * playerPosition.Y + 1)
    );
}

Y en la función de movimiento usamos un switch para comparar el emoji basados en las filas y columnas de nuestro array bidimendional

switch (columns[newRow-1][newColumn-1]) {
        case 'O':
            //console.log('Returned to the start');
            (level-1 < 0) ? level=level : level-=1;
            break;

        case 'X':
            //console.log('Collided');
            lostLive(); // reduce lives
            return true;

        case 'I':
            levelPassed();
            break;
    }

Agregué esto 😅

Para actualizar la ubicación del jugador al cambiar el tamaño de la pantalla plantee la siguiente solución:

  1. En el objeto playerPosition cree dos nuevas propiedades row y col, las cuales van a almacenar la fila y columna en la que esta ubicado el jugador.
const playerPosition = {
  x: undefined,
  y: undefined,
  col: undefined,
  row: undefined,
};
  1. Cree la variable oldElementSize en la cual se almacenara el valor del tamaño anterior, se define en la función setCanvasSize, para realizar una comparación con el tamaño actual y asi poder volver a ubicar al jugador
if (elementSize) {
    oldElementSize = elementSize;
  } else {
    oldElementSize = (canvasSize / 10);
  }
  elementSize = (canvasSize / 10) ;
  1. Por ultimo se hace la comparación del oldElementSize vs elementSize dentro de la función startGame, dentro se actualiza la ubicación del jugador con el nuevo elementSize y la fila y columna en la cual esta ubicado el jugador.
if (oldElementPos !== elementPos) {
    playerPosition.x = (elementPos * playerPosition.col) - (elementPos/2) + fixPos;
    playerPosition.y = (elementPos * playerPosition.row) - fixPos;
    playerPosition.x = fixNumber(playerPosition.x);
    playerPosition.y = fixNumber(playerPosition.y);
    oldElementPos = elementPos;
  }

https://paulaxam.github.io/taller-practico-js-videojuego/

Dejo mi jueguito, le cambié un poquito la temática para mandárselo a mi sobrino que tiene 4 añitos a que lo juegue, asique cambié las calaveras y explosiones por un paseo por el bosque. Agregué algunos mapitas, y cambié la estructura del css. De a poquito le iré agregando mas mapas y mejorándolo. Espero les guste!!!

este fue mi juego al final. https://eduardbarrios.github.io/taller_practico_JS_videogame/

Solucione lo de la calavera de la siguiente forma.
Hay que tomar la formula de la posicion en x y en y
Yo la tengo en +5 y --10 para acomodar las bombas y que no se salgan.del canvas-

      const posX = elementsSize * (col + 1) + 5;
      const posY = elementsSize * (rowIndex + 1) - 10;

Bueno vamos a despejar la ubicación de la columna y de la fila donde se encuentra nuestra calavera.

despejando col y row, me quedaron estas formulas. ahora ya se en que columna y fila se encuentra mi player

  const colPlayer=((playerPosition.x-5)/elementsSize)-1;
  const rowPlayer=((playerPosition.y+10)/elementsSize)-1;

y lo que hago es volver a realizar la misma formula de un principio con el nuevo elementSize y lo dibujamos.

  playerPosition.x=elementsSize*(colPlayer+1)+5;
  playerPosition.y=elementsSize*(rowPlayer+1)-10;
  game.fillText(emojis["PLAYER"], playerPosition.x, playerPosition.y);

y ya nos quedara nuestra calavera en la posición que esta.

Comparto mi solución para corregir el desfase del objeto jugador(calavera) al hacer resize.

Tengo dos variables globales llamadas:

let canvasSize; //Esta fue creada desde las primeras clases
let reCanvas; //Se guardará el primer canvasSize al cargar la página luego de un resize

// También agregué dos propiedades más a al objeto player; lastX y lastY
const player = {
    x: null,
    y: null,
    lastX : null,
    lastY : null,
}

Al principio de la función resizeCanvas(){} agregué el siguiente condicional

function resizeCanvas(){

    if(canvasSize){
        reCanvas = canvasSize; //Aquí se guarda el primer tamaño del canvas anterior como expliqué arriba
    }
.
.
.
	starGame();
}

Luego al llamarse starGame() justo dentro del ciclo forEach doble tengo el siguiente condicional:

starGame(){
.
.
.
map.forEach((row, rowIndex) => {
        row.forEach((col, colIndex) => {
           
            if(player.x || player.y){
                if(player.x.toFixed(2) == (elementSize*(colIndex+1.14)).toFixed(2)){
                    player.lastX = colIndex;
                }
                if(player.y.toFixed(2) == (elementSize*(rowIndex+0.88)).toFixed(2)){
                    player.lastY = rowIndex;
                }
            }
	 
	.
	.
	.
 	}
}

Como se puede ver; este condicional entra sólo si player.x o player.y tienen algún valor. Luego va a verificar si el valor de la posición de mi jugador en cada coordenada de la matriz es igual al valor de la coordenada actual dentro del ciclo y de esta manera guardará la posición del jugador dentro de la matriz, en que fila o columna está, y las guarda en player.lastX y player.lastY, ósea, estoy guardando los índices que tenía mi jugador dentro de la matriz.

Finalmente en la función movePlayer(){} ejecuto el siguiente condicional:

if(canvasSize > reCanvas || canvasSize < reCanvas){
        console.log('entro en 2');
    
        player.y = elementSize*(player.lastY+0.88);
        player.x = elementSize*(player.lastX+1.14);
       
        game.fillText(emojis['PLAYER'], player.x+4, player.y);      
    }
    else{
        //Renderizado de auto
        game.fillText(emojis['PLAYER'], player.x+4, player.y);
    }

Cuando halla un resize el va a comparar si el nuevo tamaño del canvas es mayor o menor al anterior, de ser así, va a guardar la posición en X y Y del jugador haciendo uso de los índices que tenía antes de que se haga resize, luego lo renderiza. Pero no no existe un resize pasará al else{} donde pondrá las coordenadas que normales cuando el usuario mueva al jugador.

Se que puede no entenderse sin ver el código completo. Lo estaré compartiendo la próxima clase. De todos modos es muy parecido a todo o que se ha hecho hasta ahora.

Antes de la clase hice algunos cambios y fui arreglando algunas partes visuales dividiendo por 1000 y con toFixed(2) para mostrar el tiempo y el record.

    let diferencia = Date.now() - timeStart;
    segundos = diferencia /1000;
    spanTime.innerHTML = segundos.toFixed(2);

Ademas agregando que un event keydown para que en el momento en que el player presiona una tecla inicie el tiempo.

let onKeyDown = false;
document.body.onkeydown = () => onKeyDown = true;
document.body.onmousedown = () => onKeyDown = true;
//......
    if(!timeStart){
        if(onKeyDown){
        timeStart = Date.now();
        timeInterval = setInterval(showTime, 10)
        spanResult.innerHTML = ' ';
        }
        showRecord()
    }

Un titulo que muestra el level en el que estas.

function showLevel(){
    if(!level >0){
        spanLevel.innerHTML = level + 1;
    } else {
        spanLevel.innerHTML = ` Game Over`
    }
}

Mi solución a ese problema fue solo redondear la resta en el condicional pero solo en la función de mover hacia arriba (porque solo ahi tenia el problema la inicio). No me valia para las démas direcciones. Está mejor solucionar el problema desde su raiz…👍👍

Hola…les comparto…
Mi repositorio: https://github.com/libian19/videogame
Mi deploy: https://libian19.github.io/videogame/

Ok reubicar el player me tomo dos días, tuve que pensar super profundo porque no me funciono las soluciones de los compañeros, así que no voy a pasar el código ya que es mas confuso, esto es para ayudar a los que como yo no les funciono lo que publicaron los demás.

La manera en que lo hice fue haciendo la regla de tres antes de empezar el start game justo en el resize, sigiendo esta formula:

(NuevoCanvasSize * playerPosition.x ) / anteriorCanvasSize

Hacemos esto mismo con el playerPosition.y

  • Pasos
  1. Extrae y metelos en un array cada “canvasSize”, porque cuando reajustes el tamaño de la pantalla, se va a borrar del original canvasSize, yo lo extraje desde el startGame() al final.
    ///////////////////////////////////////////////////////////////
  2. Yo separe la funcion del resize de la funcion del load window, llamando all load window al final de la funcion resize, esto es para que primero se carge la pagina, el canvas y el startGame(), asi que cuando ya con todo cargado y las posiciones establecidas (esto es instantaneo, es imposible que muevas el tamaño de la pagina antes de que esta se haya cargado) ya cuando muevas el tamaño de la pagina, ya esta guardada en un array la posicion del canvas anterior.
    ///////////////////////////////////////////////////////////////////
  3. En la funcion de resize del canvas, vas a añadir la regla de tres, cuando hagas un moviento de la pagina y el resize se ejecute, ya debes estar “Guardada” la anterior playerPosition.x y playerPosition.y, asi que esas te sirven para la regla de tres, y con el array que guarda los anteriores canvaSize, lo insertas de manera que lea al ultimo elemento, que es el tamaño del canvas anterior al que tienes ahora (Elijo la ultima porque el nuevo canvasSize aun no esta en el array, que esto lo extrae mas abajo)… Y LISTO 😃, asegurate que llame a la funcion del windows load al final porque esa funcion llama a todas las demas y escribe la nueva posicion

Entonces que fue lo que hice, sobre escribi la playerPosition X y Y y lo mande a imprimir cada ves que se hace un resize

Ahora que ya lo detalle como lo hice, aca esta mi codigo

const canvas = document.querySelector("#game")
const game = canvas.getContext('2d')
let canvasSize //Cree una variable que un if le dara un valor, esta variable es para el tamaño del canvas
let elementsSize
let level = 0
let lives = 3

let timeStart
let timePlayer
let timeInterval

const playerPosition = {x: undefined, y: undefined}
const giftPosition = {x: undefined, y: undefined}

let enemiesPosition = []
let resizeElementXandY
let arrayUltimoCanvasSize =[]


window.addEventListener('load', setCanvasSize)
window.addEventListener('resize', resize)


function resize(){
    if(window.innerHeight > window.innerWidth){
        canvasSize = window.innerWidth * 0.7

        //si el alto de la pagina es mas grande que el ancho, el canvas solo va a tomar el 80% del ancho
    }else if (window.innerHeight < window.innerWidth){
        canvasSize = window.innerHeight * 0.7

          //si el ancho de la pagina es mas grande que el alto, el canvas solo va a tomar el 80% del alto
    }

    playerPosition.x = (canvasSize * playerPosition.x) / arrayUltimoCanvasSize[arrayUltimoCanvasSize.length - 1]
    playerPosition.y = (canvasSize * playerPosition.y) / arrayUltimoCanvasSize[arrayUltimoCanvasSize.length - 1]

    console.log(playerPosition.x, playerPosition.y)
    setCanvasSize()
}


function setCanvasSize(){

    if(window.innerHeight > window.innerWidth){
        canvasSize = window.innerWidth * 0.7

        //si el alto de la pagina es mas grande que el ancho, el canvas solo va a tomar el 80% del ancho
    }else if (window.innerHeight < window.innerWidth){
        canvasSize = window.innerHeight * 0.7

          //si el ancho de la pagina es mas grande que el alto, el canvas solo va a tomar el 80% del alto
    }

    canvas.setAttribute('width', canvasSize)
    canvas.setAttribute('height', canvasSize)

    elementsSize = canvasSize / 10

    // playerPosition.x = undefined
    // playerPosition.y = undefined
    
    arrayUltimoCanvasSize.push(canvasSize)
    // resizeElementPosition.push(elementsSize)

    // if (resizeElementPosition){
    //     let ultimoElementSize = resizeElementPosition[resizeElementPosition.length - 2]
    //     console.log(ultimoElementSize)
    // }

  
    startGame()
}


function startGame(){

    game.font = (elementsSize - 15) + "px Verdana"
    game.fillStyle ="orange"
    game.textAlign = "end"

    const map = maps[level]
    if(!map){
       gameWinAndSetRecord()
       return
    }
    if(!timeStart){
        timeStart = Date.now()
        timeInterval = setInterval(showTime, 100 )
        showRecord()
    }
    // console.log(timeStart)

    const mapRows = map.trim().split("\n")
    const mapRowsSinEspacios = mapRows.map((row) => row.trim().split(''))
    // console.log(mapRows, mapRowsSinEspacios )


    //si con un ForEach recibe un segundo parametro. este regresa la ubicacion en el index de ese elemento
    enemiesPosition = []
    game.clearRect(0, 0, canvasSize, canvasSize)
    showLives()
    mapRowsSinEspacios.forEach((row, rowI) => {
        row.forEach((col, colI) => {
            let emoji = emojis[col]
            let posX = elementsSize * (colI + 1)
            let posY = (elementsSize * (rowI + 1))
            // console.log({col, colI, row, rowI, emoji})
            if(col == 'O' && playerPosition.x == undefined && playerPosition.y == undefined){
                playerPosition.x = posX
                playerPosition.y = posY
                console.log({"Aqui debe estar el jugador": 0, posX, posY, playerPosition})
            }
            else if (col === 'I'){
                giftPosition.x = posX
                giftPosition.y = posY
                // console.log({giftPosition})
            }
            else if (col === 'X'){
                enemiesPosition.push({col, positionX: posX, positionY: posY})
                // console.log(enemiesPosition)

            }

            
            game.fillText(emoji, posX, posY)
            game.fillText(emojis["PLAYER"], playerPosition.x, playerPosition.y)

        })

    });
    // console.log(enemiesPosition)
    let tempUltimaPosiciones = [playerPosition.x, playerPosition.y]
    resizeElementXandY = tempUltimaPosiciones
   movePlayer()
}

window.addEventListener('keydown', moveByKeys)
const btnUp = document.getElementById('up')
const btnRight = document.getElementById('right')
const btnLeft = document.getElementById('left')
const btnDown = document.getElementById('down')
const spanLives = document.getElementById('lives')
const spanTime = document.getElementById('tiempo')
const spanRecord = document.getElementById('record')
const pResult = document.getElementById('result')


btnUp.addEventListener('click', moveUp)
btnRight.addEventListener('click', moveRight)
btnLeft.addEventListener('click', moveLeft)
btnDown.addEventListener('click', moveDown)



function movePlayer(){
    const giftCollisionX = playerPosition.x.toFixed(1) == giftPosition.x.toFixed(1)
    const giftCollisionY = playerPosition.y.toFixed(1) == giftPosition.y.toFixed(1)
    const giftCollision = giftCollisionX && giftCollisionY;

    if(giftCollision){
         levelWin()
    }

    // game.fillText(emojis["PLAYER"], playerPosition.x, playerPosition.y)
//    console.log(playerPosition.x, playerPosition.y)
///colicion bombas//
  const enemyCollision = enemiesPosition.find(enemy => {
    const enemyCollisionX = (enemy.positionX).toFixed(2) == (playerPosition.x).toFixed(2);
    const enemyCollisionY = (enemy.positionY).toFixed(2) == (playerPosition.y).toFixed(2);
    return(enemyCollisionX && enemyCollisionY)
  })


    if(enemyCollision){
        levelFail()
    }

}

function levelWin(){
    console.log("subiste de nivel")
    level++
    return startGame()
}

function levelFail(){
    console.log('Chocaste con un enemigo :(')
    playerPosition.x = undefined
    playerPosition.y = undefined
    lives--
    if(lives <= 0){
       level = 0
       lives = 3
       timeStart = undefined 
    }


    startGame()

}

function gameWinAndSetRecord(){
    console.log("Superaste el juego")
    level = 0
    playerPosition.x = undefined
    playerPosition.y = undefined
    clearInterval(timeInterval)

    const recordTime = localStorage.getItem('record_time') //Variable que nos trae una iformacion del local storaage
    const playerTime = Date.now() - timeStart //Variable que nos trae el ultimo tiempo cuando ya se acabaron los niveles del juego, osea tu marca en la partida

    //Revisa si ya existia un record y empieza a comparar
    if(recordTime){
        //Si el record en el localStorage es mayor, este lo sobre escribe
        if(recordTime >= playerTime ){
            localStorage.setItem('record_time', playerTime)
            pResult.innerHTML = 'Haz superado el record ✨✨'
        }
        //Y si el tiempo en esta partida no es mayor al record ya impuesto en otras partidas, te avisa que no lo has roto
        else{
            pResult.innerHTML = 'lo siento, no has roto el record 💀'
        }
    }
    //Si no habia un record en el localStorage, lo va a creear
    else{
        localStorage.setItem('record_time', playerTime)
        pResult.innerHTML = 'Primer ingreso de tiempo ✍'
    }

    //Y aca te enseña en la consola el record anterior (si no existe es null), y el record de esta partida
    console.log({recordTime, playerTime})
    // startGame()
}

function moveUp(){
    // console.log('Arriba')
    if((playerPosition.y - elementsSize) < elementsSize){
        console.log("limite")
    } else{
       playerPosition.y = playerPosition.y - elementsSize
        startGame()
    }


}

function moveRight(){
    // console.log('Derecha')

    if((playerPosition.x + elementsSize) > canvasSize){
        console.log("limite")
    } else{
        playerPosition.x = playerPosition.x + elementsSize
        startGame()
    }

}

function moveLeft(){
    // console.log('Izquierda')
    if((playerPosition.x - elementsSize) < elementsSize){
        console.log("limite")
    } else{
        playerPosition.x = playerPosition.x - elementsSize
        startGame()
    }

}

function moveDown(){
    // console.log('Abajo')
    if((playerPosition.y + elementsSize) > canvasSize ){
        console.log("limite")
    } else{
        playerPosition.y = playerPosition.y + elementsSize
        startGame()
    }

}

function moveByKeys(event){
//    console.log(event.key)

    let teclaPulsada = event.key
    switch(teclaPulsada){
        case "ArrowUp":
            moveUp()
        break;
        case "ArrowRight":
            moveRight()
        break;
        case "ArrowLeft":
            moveLeft()
        break;
        case "ArrowDown":
            moveDown()
        break;
    }

}

/////////////Utilidades visuales (vidas, tiempo, etc )

function showLives(){
    //Esto es interesante, el metodo "Array" con a mayuscula te permite crear un array que le puedes decir cuantas posiciones quieres y despues con el metodo fill, la llenas de lo que le pediste
    let corazonesArray = Array(lives).fill(emojis['h'])
    // console.log(corazones)
    // let corazones = ""
    
    spanLives.innerHTML = ""
    corazonesArray.forEach(corazon => {
        spanLives.append(corazon)
        // corazones = corazones + corazon
        // console.log(corazones)
    } )
    

}

function showTime(){
    spanTime.innerHTML = Date.now() - timeStart
}

function showRecord(){
    spanRecord.innerHTML = localStorage.getItem('record_time')
}

//Esta ves usaremos una herramiendo de navegadores que se llama el localStorage, que nos sirve para guardar informacion en el navegador y lo podamos usar de nuevo con su informacion guardada asi hallamos cerrado la pagina, usa 3 metodos (que conozco) como ".getItem" es para leer la informacio que este guardada , ".setItem" para guardar la informacion, variable etc, ".removeItem" saca la variable que se guardo

Lo solucione creando un objeto para guardar la fila y la columna don de esta el jugador.

const playerPositionRowCol = {
    x: undefined,
    y: undefined,
};

En startGame() guardo la posicion de la columna y la fila.

function startGame() {
    const map = maps[level];

    if (!map) {
        gameWin();
        return;
    }

    if (!timeStart) {
        timeStart = Date.now();
        timeInterval = setInterval(showTime, 100);
        showRecord();
    }

    const mapRows = map.trim().split('\n');
    const mapRowCols = mapRows.map(row => row.trim().split(''));

    // console.log('startGame', lives);
    showLives();

    game.font = (elementsSize * 0.9) + 'px Verdana';
    game.textAlign = 'end';
    
    bombPosition = [];
    game.clearRect(0, 0, canvasSize, canvasSize); 

    mapRowCols.forEach((row, rowI) => {
        row.forEach((col, colI) => {
            const emoji = emojis[col];
            const posX = elementsSize * (colI + 1) + 3;
            const posY = elementsSize * (rowI + 1) - 7;

            if (col == 'O') {
                if (!playerPosition.x && !playerPosition.y) {
                    playerPosition.x = posX;
                    playerPosition.y = posY;
/*ACA LA GUARDO */
                    playerPositionRowCol.x = colI;
                    playerPositionRowCol.y = rowI;                 
                }
            } else if (col == 'I') {
                giftPosition.x = posX;
                giftPosition.y = posY;
            } else if (col == 'X') {
                bombPosition.push({
                    x: posX,
                    y: posY,
                })
            }
            
            game.fillText(emoji, posX, posY)     
        });
    });
    console.log('startGame', {playerPositionRowCol});
    movePlayer()
}

La actualizo en cada en cada una de las funciones de movimiento.

function moveUp() {
    if ((playerPosition.y - elementsSize - 4) > 0) {
        playerPosition.y -= elementsSize;        
        playerPositionRowCol.x = ((playerPosition.x - 3) / elementsSize) - 1;
        playerPositionRowCol.y = ((playerPosition.y + 7) / elementsSize) - 1;
    }
    startGame();
    console.log('moveUp', {playerPositionRowCol});
}

y en la funcion setCanvasSize() le asigno la posicion de nuevo

unction setCanvasSize() {
    canvasSize = Math.min(window.innerHeight, window.innerWidth) * 0.7;

    console.log({playerPosition});
    
    canvas.setAttribute('width', canvasSize);
    canvas.setAttribute('height', canvasSize);
    
    elementsSize = canvasSize / 10;

    playerPosition.x = elementsSize * (playerPositionRowCol.x + 1) + 3;
    playerPosition.y = elementsSize * (playerPositionRowCol.y + 1) - 7;
    
    startGame();
}

Les dejo el repositorio
https://github.com/CarlosMarioToro/taller-practico-javascript-videogame.github.io

Si entontrasemos otra posciión donde vuelva a existir un error, no necesitamos aplicar la funcion fixNumber en todas partes, basta con aplicarlo en la definición de la “Colision”, redondeamos los numeros en el condicional para que se pueda cumplir la condición.

A mi solo me funciono esto con los decimales, tuve que aplicarlo a todos los valores:

canvasSize = Math.round((innerHeight * 0.8).toFixed(2));

Para reposicionar al jugador yo cree dos variables horizontalMovement y verticalMovement. Cuando se crea la calavera en startGame() le asigno columIndex y rowIndex a las dos variables anteriores, de forma que ya tengo identificada la posición inicial.

funtion startGame() {
...
	if (playerPosition.x == undefined && colum == 'O') {
        playerPosition.x = posX;
        playerPosition.y = posY;

        horizontalMovement = columIndex;
        verticalMovement = rowIndex + 1;

        console.log({ playerPosition });
      }
...
}

Ahora, cuando me muevo le resto o le sumo a horizontalMovement y verticalMovement, por ejemplo si me muevo hacia arriba:

function moveUp() {
  console.log('up');

  if ((playerPosition.y - elementSize).toFixed(2) < elementSize) {
    console.log('OUT');
  } else {
    playerPosition.y -= elementSize;
    verticalMovement--;

    if (enemiesCollision()) {
      levelFail();
    } else {
      giftCollision();
    }
    startGame();
  }
}

Le resto a verticalMovement.
Para que al hacer resize al llamar a setCanvaSize, si tengo valores en playerPosition, reasigno los valores de x y y usando horizontalMovement y verticalMovement :

function setCanvaSize() {
...
  if (playerPosition.x != undefined) {
      playerPosition.x = horizontalMovement * elementSize;
      playerPosition.y = verticalMovement * elementSize;
  }
...
}

En mi cabeza fue una buena idea, no sé si en cuanto a rendimiento sea buena.

para solucionar lo del cambiar la solucion del jugador mientras juegas lo que hice fue establecer una variable global llamada oldElementSise y agregar una funcion llamada cambioTamañoJugador

<function setCanvasSize() {
  if (window.innerHeight > window.innerWidth) {
    canvasSize = window.innerWidth * 0.75;
  } else {
    canvasSize = window.innerHeight * 0.75;
  }
  
  canvas.setAttribute('width', canvasSize+15);
  canvas.setAttribute('height', canvasSize+15);
  oldElementZise=elementsSize;
  elementsSize = Math.floor(canvasSize / 10);
  if (playerPosition.x!=undefined && playerPosition.y!=undefined) {
    cambioTamañoJugador(elementsSize,oldElementZise);
  }
  startGame();
}
function cambioTamañoJugador(nuevaEscala,antiguaEscala) {
  playerPosition.x=playerPosition.x*(nuevaEscala/antiguaEscala);
  playerPosition.y=playerPosition.y*(nuevaEscala/antiguaEscala);
}> 
ya que en cualquier calculo ocupamos canvasSize o elementSize, lo que hice fue quitarle los decimales a esas 2 variables```js canvasSize= parseInt(canvasSize) elementsSize = parseInt(canvasSize / 10); ```.
ya que en cualquier calculo ocupamos canvasSize o elementSize simplemente le quitamos los decimales a esas 2 variables. canvasSize= parseInt(canvasSize) elementsSize = parseInt(canvasSize / 10);

Solución a error de decimales en elementSize y canvaSize


Solución a error de decimales en elementSize y canvaSize

_

La solución al reto del minuto 3:27

Lo que hice desde el inicio fue tener 2 funciones distintas: startGame y reStartGame, de esta forma el objeto playerPosition sería flexible

¿Cuando utilizar cada una?









_

📌Prueba la demo del juego 🎮 ⭐⭐⭐⭐⭐

📌Échale un vistazo al repositorio de GitHub

DESPLIEGUE DEL JUEGO EN GITHUB PAGES:

🎮Taller: Crea tu primer videojuego 🎮

El videojuego ha sido elaborado durante las sesiones del taller de Platzi con HTML🧡, CSS 💙y JavaScript💛 y adicionalmente se han añadido algunas funcionalidades, como:

Funcionalidad Tutorial
Incorporación de fuego 🔥 en las colisiones contra las bombas Entra aquí
Contador de tiempo ⏱ en segundos + 2 decimales Entra aquí
Tarjetas interactivas al perder, ganar sin récord y ganar con nuevo récord Entra aquí
Pantalla de inicio del juego 🤠 Entra aquí

📌Prueba la demo del juego 🎮 ⭐⭐⭐⭐⭐

📌Échale un vistazo al repositorio de GitHub

📌Échale un vistazo al Taller de Platzi

Al fin salio el error que el profesor siempre dijo y que se tardo 2 años en salir

Este proceso de mejorar e iterar el código es igual de satisfactorio como frustante xd

Una solucion que le di a el posicionamiento del jugador y que no se altere al modificar el tamaño de la ventana, fue crear un objeto con 10 valores, cada uno de estos valores representa una posición en el canvas, dado que el juego tiene basicamente 10 posiciones en el eje X y 10 en el eje Y. Si dividimos el valor del ancho del canvas entre 10, tendremos el valor que cada posición debe tener, por ejemplo si el ancho del canvas es 800, el objeto sería algo así:

positions ={
    0: 0,
    1: 80,
    2: 160, 
    3: 240,
    4: 320, 
    5: 400,
    6: 480, 
    7: 560, 
    8: 640,
    9: 720
}

De este modo si la posición del jugador es la 4, podemos crear una función para que modifique las posiciones cada que el tamaño de la ventana se modifique, lo cual serviria tanto para el eje X como el eje Y:

function setPositions(){
    for(let i = 0; i < 10; i++){
        positions[i] = (canvas.width / 10) * i;
    }
}

De este modo la posición del jugador puede ser asignada o modificada haciendo referencia a el objeto positions[n] que representa una posición distinta según el tamaño del canvas.

Les dejo mi solución en mi github por si les interesa revisarla:

https://github.com/pipemalz/14_practico_js_videojuego

Yo hice algo un poco diferente para evitar que el personaje cambie de posición al hacer resize.

En lugar la posición de X y Y, guardo un multiplicador basado en el elementsSize

posXoY / elementsSize

y cada vez que se mueve el jugador, incremento o disminuyo en 1 el valor de X o Y según sea el caso.
Al momento de renderizar el player simplemente multiplico el elementsSize por el multiplicador para X y Y respectivamente.