No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Depurando errores del juego

20/24
Recursos

Aportes 34

Preguntas 1

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

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 鈥 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茅 鈥渙ldSize鈥:

    .
  • 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/

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;
    }

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 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鈥es 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 鈥渃anvasSize鈥, 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 鈥淕uardada鈥 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 鈥淐olision鈥, 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);
}> 

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

Agregu茅 esto 馃槄

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.