Creo que se debería colocar la lógica en las funciones de moveUp, moveDown, etc. porque ahí se esta modificando la posición del jugador.
Introducción
Programemos un juego con JavaScript
Canvas
¿Qué es canvas en JavaScript?
Tamaño del canvas y sus elementos
Canvas responsive
Mapa del juego
¿Qué es un array bidimensional?
Arreglos multidimensionales en JavaScript
Refactor del mapa de juego
Movimientos del jugador
Eventos y botones
Objeto playerPosition
Limpieza de movimientos
No te salgas del mapa
Colisiones
Detectando colisiones fijas
Detectando colisiones con arrays
Victoria: subiendo de nivel
Derrota: perdiendo vidas
Bonus: adictividad
Sistema de vidas y corazones
Sistema de tiempo y puntajes
¿Qué es localStorage?
Guardando records del jugador
Deploy
Depurando errores del juego
Desplegando el juego a GitHub Pages
Próximos pasos
Reto: reinicio del juego
Reto: timeouts de victoria o derrota
¿Quieres un simulador laboral?
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Aportes 46
Preguntas 0
Creo que se debería colocar la lógica en las funciones de moveUp, moveDown, etc. porque ahí se esta modificando la posición del jugador.
lo de las colisiones fue muy fácil solo tuve que comparar el movimiento del jugador con el índex del mapa sin sumar canvasSize ni comparar X ni Y y pues ya 😅
La ventaja que veo que tiene mi código es que convertí el map en un arreglo normal de toda la vida intente asemejarlo a lo que normalmente veo en ensamblador por lo que las colisiones no resultan tan engorrosas ni confusas o al menos eso creo 🤔
//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 vidas = Array();
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(vidas.length == 3 && actualizado) return;
cargarMapa();
clear();
paintEvent();
paintEventPlayer();
paintEventGameOver();
actualizado = true;}
function cargarMapa(){
if(nivel_actual == nivel) 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)
mapa.forEach((char, idx) => {
if(char == 'O' && posJugador == undefined) puntoDePartida = posJugador = idx;
juego.fillText(emojis[char], posX(idx), posY(idx) );});}
function paintEventPlayer(){
if(mapa[posJugador] == 'I') {
nivel = nivel + 1 <= 2 ? ++nivel : 0;
posJugador = prePosJugador = undefined;
actualizado = false;
update();
return;}
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.length < 3 && mapa[posJugador] == 'X'){
vidas.push(posJugador);
juego.fillText(emojis['BOMB_COLLISION'],posX(posJugador),posY(posJugador));
posJugador = puntoDePartida;}
if(vidas.length == 3) return false; /*juego terminado*/
return true; /*continuar con el juego*/}
function paintEventGameOver(){
juego.textAlign='end';
vidas.forEach((idx) =>{
clearRect(idx);
juego.fillText(emojis['BOMB_COLLISION'],posX(idx),posY(idx));});
if(vidas.length < 3) return;
const fuente = celda_t - celda_t / 10 ;
const media = canvas_t / 2;
juego.fillStyle = '#000000';
juego.fillRect(0, media - (fuente /2), canvas_t, fuente);
juego.textAlign = 'center';
juego.fillStyle = '#ff5555';
juego.fillText('Game Over', media, media + fuente/3);}
//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();}
Y pensar que a mi me salio el error de los decimales en el primer mapa 🤣
Pausé un segundo la clase porque no me detectaba la colisión y era por los decimales (yo pensaba que había hecho algo mal). Puse play y dijo que ese era el problema jaja
Hola, les comparto mi solución al desafío.
Creo una variable global para almacenar la posición de todas las bombas let bombs = [ ];
En la función startGame
creo el siguiente condicional para agregar las posiciones de las bombas.
Cree una función para verificar si la posición del jugador coincide con alguna bomba
Ahora ejecuto esa función cada vez que se mueve el jugador.
Por último, en la función setCanvasSize
reasigno la variable bombs = [ ]
Yo si tuve ese error de los decimales
Una solución que se me ocurre es redondear las coordenadas a dos decimales.
const posX = parseFloat((elementSize * (indexColumn + 1)).toFixed(2));
const posY = parseFloat((elementSize * (indexRow + 1)).toFixed(2));
playerPosition.x = posX;
playerPosition.y = posY;
giftPosition.x = posX;
giftPosition.y = posY;
Para detectar la colisión, cambiar de nivel y dibujar al jugador cree una función mapPlayer 😃
Juego boom
const canvas = document.querySelector("#game");
const game = canvas.getContext("2d");
const up = document.getElementById("up");
const down = document.getElementById("down");
const left = document.getElementById("left");
const right = document.getElementById("right");
let leveltoplay = 0;
let moveVertical=1;
let moveHorizontal=1;
const playerPosition = {};
window.addEventListener("load",setCanvasSize);
window.addEventListener("resize",setCanvasSize);
// addEventLister movimiento del jugador
up.addEventListener("click",moveWithButton);
down.addEventListener("click",moveWithButton);
left.addEventListener("click",moveWithButton);
right.addEventListener("click",moveWithButton);
window.addEventListener("keyup",keyDetection);
let canvassize;
function setCanvasSize(){
if( window.innerWidth > window.innerHeight ){
canvassize = window.innerHeight*0.70;
}else{
canvassize = window.innerWidth*0.60;
}
canvas.setAttribute("width", canvassize);
canvas.setAttribute("height", canvassize);
startGame();
}
function startGame(){
// Espera que se cargue la página
// game.fillRect(0,50,100,100);
// game.clearRect(50,50,100,100);
// game.fillStyle ="Purple";
// game.font = "25px Verdana";
// game.textAlign ="center"; //start, end, left, center, right
// game.fillText("Platzi",50,50,200);
// const valueheight = window.innerHeight; // height value of windows
// const valuewidth = window.innerWidth; // width value of windows
/* other form to fill the map
const valuemaps = Object.keys(emojis);
console.log(valuemaps);
let count1 = 1;
let count2 = 1;
for (let i = 0; i <maps[0].length; i++) {
for (let j = 0; j < valuemaps.length; j++) {
if(maps[0][i] == valuemaps[j]){
game.fillText(emojis[valuemaps[j]] ,elementSize * count1 , elementSize * count2);
if(count1 == 10){
count1 = 1;
count2++;
}else{
count1++;
}
}
}
if(count2 > 10){
count2 = 1 ;
}
}
console.log({count1,count2})
*/
const elementSize = canvassize/10;
console.log({elementSize,game});
game.textAlign = "end";
game.textBaseline = "bottom"; // align text en forma vertical 'top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom'
game.font = String (elementSize -10) + "px Arial" ;
renderMap(leveltoplay);
}
function renderMap(level){
game.clearRect(0,0,canvas.width,canvas.height);
const filterMap = maps[level].trim().split("\n");
const maprender = filterMap.map(function(key){ return key.trim().split("")});
mapObjetcs(maprender);
}
function moveWithButton(event){
if(event.path[0].id == "up"){
moveVertical--;
}else if(event.path[0].id == "down"){
moveVertical++;
}else if(event.path[0].id == "left"){
moveHorizontal--;
}else if(event.path[0].id == "right"){
moveHorizontal++;
}
condicionalPosition();
console.log({moveHorizontal,moveVertical});
movePlayer();
}
function keyDetection(event){
const keyButtonMove = [38,37,39,40];
keyButtonMove.forEach(function(key){
if(event.keyCode == key){
if(key == 38){
moveVertical--;
}else if(key == 40){
moveVertical++;
}else if(key == 37){
moveHorizontal--;
}else if(key == 39){
moveHorizontal++;
}
condicionalPosition();
console.log({moveHorizontal,moveVertical});
movePlayer();
}
});
}
function condicionalPosition(){
if(moveVertical>10){
moveVertical =10;
}else if(moveVertical < 1){
moveVertical =1;
}
if(moveHorizontal>10){
moveHorizontal =10;
}else if(moveHorizontal < 1){
moveHorizontal =1;
}
}
function movePlayer(){
renderMap(leveltoplay);
// game.fillText(emojis['PLAYER'] ,elementSize * moveHorizontal , elementSize * moveVertical);
}
function mapObjetcs(maprender){
const elementSize = canvassize/10;
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
game.fillText(emojis[maprender[j][i]] ,elementSize * (i+1) , elementSize * (j+1));
}
}
mapPlayer(elementSize,maprender);
}
function mapPlayer(elementSize,maprender){
const valuegif= [];
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
if(maprender[j][i]=='O'){
if (!playerPosition["y"]) {
playerPosition["y"] = j + 1;
moveVertical=j+1;
} else {
playerPosition["y"] = moveVertical;
}
if(!playerPosition["x"]){
playerPosition["x"] = i + 1;
moveHorizontal = i+1;
}else{
playerPosition["x"] = moveHorizontal;
}
game.fillText(emojis['PLAYER'] ,elementSize * playerPosition.x , elementSize * playerPosition.y);
}
if(maprender[j][i]=='I'){
valuegif.push(i+1);
valuegif.push(j+1);
}
}
}
console.log({valuegif,playerPosition});
if(maprender[playerPosition.y-1][playerPosition.x-1] == "X"){
// game.fillText(emojis['PLAYER'] ,elementSize * moveHorizontal , elementSize * moveVertical);
setTimeout(() =>{yourDie()},200);
}
if((valuegif[1])==playerPosition.y && (valuegif[0])==playerPosition.x){
leveltoplay++;
if(leveltoplay>2){leveltoplay=0;}
startGame();
}
}
function yourDie(){
window.confirm("Moriste");
playerPosition.x = undefined;
playerPosition.y = undefined;
startGame();
}
Creo una variable “bombas” que es una rray vacío:
var bombas = [];
Complemento el condicional que guarda las posiciones con el siguiente código, para que agregue cada par de coordenadas en el array bombas cuando col coincida con X:
else if (col == 'X') {
bombas.push(posX, posY);
}
Dentro de la función movePlayer, agrego el siguiente ciclo for que inicia en 0 y aumenta 2 unidades con cada iteración, para asegurarme siempre mirar una coordenada de X de una bomba. Dentro del ciclo for, un condicional que compare la coordenada x de la bomba con la coordenada x del player, así como la coordenada y de la bomba (cuyo indice siempre será i+1) con la coordenada y del player, y en caso de ser ambos pares iguales, imprima un mensaje en consola:
for(i=0; i < bombas.length; i=i+2) {
if(bombas[i].toFixed(3) == playerPosition.x.toFixed(3) && bombas[i+1].toFixed(3) == playerPosition.y.toFixed(3)) {
console.log("Explosión!!!");
}
}
Yo usé Math.trunc para el problema de los decimales
<function movePlayer(){
const giftCollsionX = Math.trunc(playerPosition.x) == Math.trunc(giftPosition.x);
const giftCollsionY = Math.trunc(playerPosition.y) == Math.trunc(giftPosition.y);
const giftCollision = giftCollsionX && giftCollsionY
if(giftCollision){
alert("Ganaste")
}
game.fillText(emojis["PLAYER"], playerPosition.x, playerPosition.y)
}>
Mi solución paso a paso
Yo creé una variable ‘bombArr’ que se inicializa en la función startGame() como un arreglo vacío y cuando se pinta el mapa agrega por cada bomba un objeto con la posición en ‘X’ y ‘Y’ de la misma.
if (col == 'X') {
bombArr.push({ x: posX, y: posY });
}
Entonces se valida si al mover el personaje algún objeto del arreglo coincide en X y en Y con el personaje.
function movePlayer() {
const giftCollisionX = Math.trunc(playerPosition.x) == Math.trunc(giftPosition.x);
const giftCollisionY = Math.trunc(playerPosition.y) == Math.trunc(giftPosition.y);
const bombCollisionX = bombArr.find(bomb => Math.trunc(bomb.x) == Math.trunc(playerPosition.x));
const bombCollisionY = bombArr.find(bomb => Math.trunc(bomb.y) == Math.trunc(playerPosition.y));
if (giftCollisionX && giftCollisionY) {
console.log('ganaste!');
} else if (bombCollisionX && bombCollisionY) {
console.log('perdiste!');
} else {
game.fillText(emojis['PLAYER'], playerPosition.x, playerPosition.y);
}
}
Pude detectar las colisiones 😃 asi:
const bombPosition = [];
// Coordenadas de las bombas
else if (col==='X'){
bombPosition.push({
bombPositionX : posX,
bombPositionY : posY
})
function bombColision(){
for (let i = 0; i < bombPosition.length; i++) {
if(playerPosition.x === bombPosition[i].bombPositionX && playerPosition.y === bombPosition[i].bombPositionY){
console.log("boom");
}
}
}
Pero al momento de que choca con las bombas me imprime varios “BOOM”
Helpp!
Mi solucion fue hacer push de objetos con las coordenadas x,y por cada bomba hacia a un array para luego con un forEach en la funcion movePlayer recorrer dicho array a la vez que se comparan las coordenadas con las del jugador hasta obtener un match.
const obstacleCollision = obstacles.forEach(obstacle => {
let obstaclePosX = obstacle.x.toFixed() == playerPosition.x.toFixed()
let obstaclesPosY = obstacle.y.toFixed() == playerPosition.y.toFixed()
if(obstaclePosX && obstaclesPosY){
console.log('BOOOOOMMMMM!!!')
}
})
game.js
const canvas = document.querySelector("#game");
const game = canvas.getContext("2d");
window.addEventListener("DOMContentLoaded", startGame);
window.addEventListener("resize", setCanvasSize);
// Obtener referencias a los botones
const upButton = document.getElementById("up");
const leftButton = document.getElementById("left");
const rightButton = document.getElementById("right");
const downButton = document.getElementById("down");
// Agregar manejadores de eventos a los botones
upButton.addEventListener("click", () => movePlayer("up"));
leftButton.addEventListener("click", () => movePlayer("left"));
rightButton.addEventListener("click", () => movePlayer("right"));
downButton.addEventListener("click", () => movePlayer("down"));
document.addEventListener("keydown", handleKeyDown);
let playerPosition = {
x: undefined,
y: undefined,
};
let giftPosition = {
x: undefined,
y: undefined,
};
let bombPosition = [];
let startMap = 0;
function handleKeyDown(event) {
const key = event.key.toLowerCase();
let direction;
switch (key) {
case "arrowup":
case "w":
direction = "up";
break;
case "arrowleft":
case "a":
direction = "left";
break;
case "arrowright":
case "d":
direction = "right";
break;
case "arrowdown":
case "s":
direction = "down";
break;
default:
return;
}
// Realizar alguna acción basada en la dirección detectada
// Por ejemplo, llamar a una función para mover al jugador en esa dirección
movePlayer(direction);
}
let elementsSize;
let map;
function startGame() {
parseMap();
setCanvasSize();
}
function movePlayer(direction) {
// Realizar acciones según la dirección recibida
switch (direction) {
case "up":
if (!(playerPosition.y > 0)) {
return; // No se cumple la condición, se sale de la función sin hacer nada
}
playerPosition.y -= 1;
// Lógica para mover al jugador hacia arriba
break;
case "left":
if (!(playerPosition.x > 0)) {
return; // No se cumple la condición, se sale de la función sin hacer nada
}
playerPosition.x -= 1;
// Lógica para mover al jugador hacia la izquierda
break;
case "right":
if (!(playerPosition.x < 9)) {
return; // No se cumple la condición, se sale de la función sin hacer nada
}
playerPosition.x += 1;
// Lógica para mover al jugador hacia la derecha
break;
case "down":
if (!(playerPosition.y < 9)) {
return; // No se cumple la condición, se sale de la función sin hacer nada
}
playerPosition.y += 1;
// Lógica para mover al jugador hacia abajo
break;
default:
return;
}
if (
giftPosition.x === playerPosition.x &&
giftPosition.y === playerPosition.y
) {
startMap++;
bombPosition = [];
startGame();
return;
} else if (
bombPosition.some(
(bombItem) =>
bombItem.x === playerPosition.x && bombItem.y === playerPosition.y
)
) {
game.fillText(
emojis.BOMB_COLLISION,
playerPosition.x * elementsSize,
(playerPosition.y + 1) * elementsSize
);
playerPosition = {
x: undefined,
y: undefined,
};
setTimeout(function () {
startGame();
}, 600);
return;
}
// Actualizar el juego, dibujar el mapa actualizado, etc.
drawMap();
}
function setCanvasSize() {
const canvasSize = Math.min(window.innerHeight, window.innerWidth) * 0.75;
canvas.width = canvasSize;
canvas.height = canvasSize;
elementsSize = Math.floor(canvasSize / 10 - 1);
game.font = `${elementsSize}px Verdana`;
drawMap();
}
function drawMap() {
game.clearRect(0, 0, canvas.width, canvas.height);
map.forEach((row, y) => {
row.forEach((emoji, x) => {
game.fillText(emoji, x * elementsSize, (y + 1) * elementsSize);
if (emoji === "🚪") {
if (playerPosition.x === undefined && playerPosition.y === undefined) {
playerPosition = { x, y };
} // Utiliza object destructuring para simplificar la asignación de coordenadas
}
if (emoji === "🎁") {
giftPosition = { x, y };
}
if (emoji === "💣") {
bombPosition.push({ x, y });
}
});
});
game.fillText(
emojis.PLAYER,
playerPosition.x * elementsSize,
(playerPosition.y + 1) * elementsSize
); // Utiliza la notación de puntos para acceder a las propiedades del objeto emojis
}
function parseMap() {
try {
const mapString = maps[startMap].trim();
const lines = mapString.split("\n").map((line) => line.trim());
map = lines.map((line) => line.split("").map((symbol) => emojis[symbol]));
} catch (error) {
startMap = 0;
playerPosition = {
x: undefined,
y: undefined,
};
parseMap();
}
}
Detectar las colisiones no me resulto dificil lo que si se me complico fue como alojar la ubicacion de las bombas por nivel ya que me generaban duplicados para eso modifique el codigo de esta forma
Variables con la que trabajaremos
const canvas = document.querySelector('#game');
const game = canvas.getContext('2d');
const buttons = {
up: document.querySelector('#up'),
left: document.querySelector('#left'),
right: document.querySelector('#right'),
down: document.querySelector('#down')
};
let canvasSize;
let elementsSize;
let lvl = 0;
//Utilizamos genX y genY para corregir el problema que el emoji del jugador no sea responsive y provoque problemas de coordenadas y hacemos que el jugador se ubique en medio de la posicion (ej. 0,5) y se desplace en un entorno de 10x10
// x e y siguen siendo las coordenadas contextuadas con el resto de los elementos para detectar colisiones
const playerPosition = {
x: undefined,
y: undefined,
genX: undefined,
genY: undefined,
};
// Utilizaremos esta constante para ubicar la locacion de la meta en cada uno de los mapas
const giftPosition = {
x: undefined,
y: undefined,
};
// Sera la constante donde alojaremos las coordenadas de cada una de las bombas de cada uno de los mapas
const bombsPosition = {
}
// Espera a que cargue el contenido html de la pagina antes de lanzar la funcion
window.addEventListener('load', resizeCanvas);
window.addEventListener('resize', resizeCanvas);
Function de start game
function startGame (lvl) {
game.font = elementsSize + 'px Verdana';
//Situamos los elementos en el medio de la casilla tanto horizontal como verticalmente
game.textAlign = "center";
game.textBaseline = 'middle';
const map = maps[lvl]
//Agarramos el mapa del array de mapas y le quitamos los espacios en blanco al inicio y al final con trim y luego con split creamos un arreglo donde el inicio y el final de cada elemento se marca por los saltos de linea '\n'
const mapRows = map.trim().split('\n');
//Del array resultante usamos map para recorrer cada uno de sus elementos (rows) limpiando los espacios en blanco con trim y luego separandolos por "filas" con split
const mapRowCols = mapRows.map(row => row.trim().split(''));
game.clearRect(0,0,canvasSize, canvasSize);
//Recorremos con for each el array bidimensional a partir del string del map y recibimos dos parametros su valor y su indice
mapRowCols.forEach((row, rowIndex) => {
//Recorremos las filas iterando sobre cada columna y recibimos su valor y su indice
row.forEach((col, colIndex) => {
//Declaramos que valor tendra el emoji en esa columna
const emoji = emojis[col];
//Definimos el posicionamiento que tendra en horizontal y vertical esa columna al alinearse en el canvas (ej: elementsSize vale 60 el elemento se insertara en el medio que seria 30)
const posX = elementsSize * (colIndex + 1/2);
const posY = elementsSize * (rowIndex + 1/2);
//Solo le damos coordenadas a la posicion del jugador si aun no fueron dadas (si no fue definida en x no hace falta verificar que no haya sido definida en y ya que siempre se definen las dos)
if (col == 'O') {
if (!playerPosition.x && !playerPosition.y) {
playerPosition.genX = posX / elementsSize
playerPosition.genY = posY / elementsSize
playerPosition.x = posX
playerPosition.y = posY
}}
// Localizamos la ubicacion de la meta para comprobar colision posteriormente
else if (col == 'I') {
giftPosition.x = posX
giftPosition.y = posY
}
//Verificamos si el objeto donde se alojan las coordenadas de las bombas ya cuenta con una propiedad con el valor del nivel y si no la tiene la creamos como un array vacio
else if (col == 'X') {
if (!bombsPosition.hasOwnProperty(lvl)) {
bombsPosition[lvl] = []
}
//Verificamos que las coordenadas no hayan sido alojadas aun y si no las insertamos en el array de arriba
const coordExists = bombsPosition[lvl].some(coord => coord[0] === posX && coord[1] === posY);
if (!coordExists) {
bombsPosition[lvl].push([posX, posY]);
}
}
//Llenamos en nuestro canvas con cada iteracion
game.fillText(emoji, posX, posY)
});
});
movePlayer()
}
Funciones de movimiento
function movePlayer() {
// Ubicamos al jugador en la cuadrilla obteniendo su ubicacion por medio de las variables genX/Y
game.fillText(emojis['PLAYER'], playerPosition.genX * elementsSize, playerPosition.genY * elementsSize);
// Corregimos las coordenadas x e y del jugador para que coincidan con su ubicacion actual
playerPosition.x = ((playerPosition.genX) * elementsSize);
playerPosition.y = ((playerPosition.genY) * elementsSize);
// Detectamos la colision con las bombas y la meta
bombsDetection()
giftDetection()
}
window.addEventListener('keydown', moveByKeys);
buttons.up.addEventListener('click', moveUp);
buttons.left.addEventListener('click', moveLeft);
buttons.right.addEventListener('click', moveRight);
buttons.down.addEventListener('click', moveDown);
function moveByKeys(event) {
if (event.key == 'ArrowUp') moveUp();
else if (event.key == 'ArrowLeft') moveLeft();
else if (event.key == 'ArrowRight') moveRight();
else if (event.key == 'ArrowDown') moveDown();
}
function moveUp() {
playerPosition.genY -= 1;
//Si el jugador se desplaza fuera del canvas lo volvemos a insertar en la primera posicion de la coordenada y
if (playerPosition.genY < 0) {
playerPosition.genY = 0.5;
}
startGame(lvl);
}
function moveLeft() {
playerPosition.genX -= 1;
//Si el jugador se desplaza fuera del canvas lo volvemos a insertar en la primera posicion de la coordenada x
if (playerPosition.genX < 0) {
playerPosition.genX = 0.5;
}
startGame(lvl);
}
function moveRight() {
playerPosition.genX += 1;
//Si el jugador se desplaza fuera del canvas lo volvemos a insertar en la ultima posicion de la coordenada x
if (playerPosition.genX > 10) {
playerPosition.genX = 9.5;
}
startGame(lvl);
}
function moveDown() {
playerPosition.genY += 1;
//Si el jugador se desplaza fuera del canvas lo volvemos a insertar en la ultima posicion de la coordenada y
if (playerPosition.genY > 10) {
playerPosition.genY = 9.5;
}
startGame(lvl);
}
Funciones de deteccion
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")
playerPosition.x = undefined;
playerPosition.y = undefined;
startGame(lvl)
}
}
function giftDetection () {
//Verificamos si la posicion del jugador y la meta son identicas y si lo son lo desplazamos al siguiente nivel, si llegamos al ultimo nivel volvemos al primero
if (playerPosition.x == giftPosition.x && playerPosition.y == giftPosition.y) {
console.log("gift detected")
if (lvl < (maps.length - 1)) {
lvl += 1;
} else {
playerPosition.x = undefined;
playerPosition.y = undefined;
lvl = 0
}
startGame(lvl)
}
}
Funcion de resize
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)
}
Yo cambie las variables del maps a let y agregue un indeci
let indexMap = 0;
let map;
let mapRows;
let mapCols;
Cree una función para convertir el mapa siguiente
function convertMap(){
map = maps[indexMap];
mapRows = map.trim().split('\n');
mapCols = mapRows.map(row=>row.trim().split(''));
}
A la posición del jugador ingresé una bandera i, esta se resetea al iniciar el juego o cuando colisiona con una bomba
const playerPosition={
x: undefined,
y: undefined,
i: 0,
};
Y cada movimiento de jugador agregué lo siguiente
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 me encontre con el mismo error, y tuve pausado el video, cuando volvi a poner el video lo entendi todo jaja, lo unico que no me queda claro es por que tuve que cambiar las validaciones en los movimientos, pero bueno, ahi voy entendiendo de a poco, muy bueno!!!
Mi solución:
Genere una función collision la cual comparta la posición actual del jugador contra el array del mapa (variable global)
la cual es llamada desde cada función de movimiento
Saludos!
Ubiqué el condiconal de coordenadas en la función de aparecer al jugador, así solo la escribo una vez y me detecta solo el caso cuando las coordenadas del jugador y regalito coinciden:
function movePlayer() {
game.fillText(emojis['PLAYER'], playerPos.x, playerPos.y);
if (playerPos.x == giftPos.x && playerPos.y == giftPos.y){
console.log("you're here!")
}
}
También se podría realizar la colusión cuando el player abandone el camino libre de obstáculos.
Muy buena la explicación del const para un objeto.
Ahora me surge una duda. Seria lo mismo declarar el objeto con let ?
Les comparto mi solución a los retos que se presentaron hasta el momento… Me da pereza explicar ya que mi código está quedando bastante diferente del de la clase, pero intenté que sea lo más claro posible. Las posiciones las manejo con números enteros y no vuelvo a hacer render de todo el mapa cada vez que se mueve el personaje.
const canvas = document.querySelector('#game');
const game = canvas.getContext('2d');
const btnUp = document.getElementById('up');
const btnLeft = document.getElementById('left');
const btnRight = document.getElementById('right');
const btnDown = document.getElementById('down');
const vidas = document.getElementById('vidas')
let canvasSize = Math.min(window.innerHeight, window.innerWidth)*0.8;
let elementSize = canvasSize/10;
canvas.setAttribute('width', canvasSize);
canvas.setAttribute('height', canvasSize);
let lvl = 0;
let lives = 3;
const initialPosition = {
posX: undefined,
posY: undefined,
}
const position = {
posX: undefined,
posY: undefined,
}
window.addEventListener('load', renderMap);
window.addEventListener('resize', resizeMap);
function resizeMap() {
canvasSize = Math.min(window.innerHeight, window.innerWidth)*0.8;
elementSize = canvasSize/10;
canvas.setAttribute('width', canvasSize);
canvas.setAttribute('height', canvasSize);
renderMap();
}
function renderMap() {
game.font = (`${elementSize * 0.85}px verdana`);
game.textAlign = 'end'
const map = maps[lvl];
const mapRow = map.trim().replaceAll(' ', '').split('\n');
const mapRowCol = mapRow.map( row => row.split(''));
mapRowCol.forEach((row, rowI) => {
row.forEach((col, colI) => {
const emoji = emojis[col];
const posX = elementSize * (colI + 1);
const posY = elementSize * (rowI + 1);
game.fillText(emoji, posX, posY);
if (col === 'O') {
//Divido todo por elementSize para que las posiciones queden definidas del 1 al 10
initialPosition.posX = posX/elementSize;
initialPosition.posY = posY/elementSize;
if (!position.posX){
position.posX = posX/elementSize;
position.posY = posY/elementSize;
}
}
})
});
game.fillText(emojis['PLAYER'], position.posX * elementSize, position.posY * elementSize);
}
function startGame() {
lives = 3;
lvl = 0;
vidas.innerText = `Vidas: ${lives}`;
position.posX = undefined;
position.posY = undefined;
game.clearRect(0, 0, canvasSize, canvasSize);
renderMap();
}
btnUp.addEventListener('click', moveUp);
btnLeft.addEventListener('click', moveLeft);
btnRight.addEventListener('click', moveRight);
btnDown.addEventListener('click', moveDown);
window.addEventListener('keydown', (event) => {
switch (event.key) {
case 'ArrowUp':
moveUp()
break;
case 'ArrowLeft':
moveLeft()
break;
case 'ArrowRight':
moveRight()
break;
case 'ArrowDown':
moveDown()
break;
default:
break;
}
})
function moveUp() {
move('up');
}
function moveLeft() {
move('left');
}
function moveRight() {
move('right');
}
function moveDown() {
move('down');
}
function clear(position) {
const startClearX = position.posX * elementSize - elementSize;
const startClearY = position.posY * elementSize - (elementSize * .83);
const endClearX = elementSize;
const endClearY = (elementSize * 1.08);
game.clearRect(startClearX, startClearY, endClearX, endClearY);
}
function move(dir) {
if (lives == 0){
startGame();
return
}
const previousPosition = {
posX: position.posX,
posY: position.posY,
}
if (dir === 'up') position.posY -= 1;
else if (dir === 'left') position.posX -= 1;
else if (dir === 'right') position.posX += 1;
else if (dir === 'down') position.posY += 1;
if (position.posX < 1 || position.posY < 1 || position.posX > 10 || position.posY > 10) {
position.posX = previousPosition.posX;
position.posY = previousPosition.posY;
return
}
clear(previousPosition);
if (previousPosition.posX == initialPosition.posX && previousPosition.posY == initialPosition.posY) {
game.fillText(emojis['O'], previousPosition.posX * elementSize, previousPosition.posY * elementSize)
}
const map = maps[lvl];
const mapRow = map.trim().replaceAll(' ', '').split('\n');
const mapRowCol = mapRow.map( row => row.split(''));
const emoji = mapRowCol[position.posY - 1][position.posX - 1];
if (emoji === 'X'){
clear(position);
game.fillText(emojis['BOMB_COLLISION'], position.posX * elementSize, position.posY * elementSize);
lives -= 1;
vidas.innerText = `Vidas: ${lives}`
if (lives == 0) {
game.clearRect(0, 0, canvasSize, canvasSize);
game.fillText('YOU LOSE!!! \n'+ emojis['GAME_OVER'], canvasSize*0.8, canvasSize*0.5 )
return
}
position.posX = initialPosition.posX;
position.posY = initialPosition.posY;
}
if (emoji === 'I') {
lvl += 1;
game.clearRect(0, 0, canvasSize, canvasSize);
if (lvl == maps.length) {
game.fillText('YOU WIN!!! \n'+ emojis['WIN'], canvasSize*0.8, canvasSize*0.5 )
lives = 0
return
}
renderMap();
return
}
game.fillText(emojis['PLAYER'], position.posX * elementSize, position.posY * elementSize);
}
if(colision){
console.log(“hay colicion”)
if(mapaActual < maps.length){
mapaActual = mapaActual +1
map = maps[mapaActual];
console.log(mapaActual)
startGame();
}
}
yo lo hice haciendo a maps gloval y haciendo esta funcion
como solucione si colisiona con el reglo
function movePlayer (){
game.fillText(emojis['PLAYER'], playerPositions.x, playerPositions.y )
if (playerPositions.x == giftPosition.x && playerPositions.y == giftPosition.y) {
console.log('pasate de nivel')
}
}```
function movePlayer() {
const giftCollisionX = Math.floor(playerPosition.x) == Math.floor(giftPosition.x);
const giftCollisionY = Math.floor(playerPosition.y) == Math.floor(giftPosition.y);
const giftCollision = giftCollisionX && giftCollisionY;
if (giftCollision) {
console.log('Subiste de nivel');
}
game.fillText(emojis['PLAYER'],playerPosition.x,playerPosition.y)
}
me gusto mucho el ejemplo de Sergio Alejandro Saldaña Rangel, el uso los indices de los elementos, en vez de usar los pixeles, y tambien creo una funcion que ayuda a calcular la posicion de cada elemento las cuales son (getx y getY), al hacer esto se vuelve el juego mucho mas personalizable y es muchisimo mas facil calcular las coliciones, asi mismo cambie algunas cosas, como el bombPosition, en vez de usar simples variables, a X y a Y les puse un array y pongo cada cordenada de las bombas en ellos, para luego con un ciclo for recorrerlos y hacer la comparacion de las bombas, asi va mi codigo:
const canvas = document.querySelector('#game');
const game = canvas.getContext('2d');
let canvasSize;
let elementSize;
const playerRender = {
level: 2,
playerPosition: {
x: undefined,
y: undefined,
},
giftPosition: {
x: undefined,
y: undefined,
},
bombPosition: {
x: [],
y: [],
},
movePlayer: function () {
const x = this.getX('PLAYER', this.playerPosition.x)
const y = this.getY('PLAYER', this.playerPosition.y)
game.fillText(emojis['PLAYER'], x, y);
},
render: function () {
if (this.level >= maps.length) {
return console.log("Ese mapa no existe")
}
const map = maps[this.level].match(/[IXO\-]+/g)
.map(a => a.split(""))
game.font = `${elementSize}px Verdana`;
game.textAlign = 'center';
game.textBaseline = 'middle';
game.clearRect(0, 0, canvasSize, canvasSize)
map.forEach((row, rowIndex) => {
row.forEach((col, colIndex) => {
const emoji = emojis[col];
const posX = this.getX(col, colIndex + 1);
const posY = this.getY(col, rowIndex + 1);
if (col == 'O') {
if (!this.playerPosition.x && !this.playerPosition.y) {
this.playerPosition.x = colIndex + 1;
this.playerPosition.y = rowIndex + 1;
}
} else if (col == 'I') {
this.giftPosition.x = colIndex + 1;
this.giftPosition.y = rowIndex + 1;
} else if (col == 'X') {
this.bombPosition.x.push(colIndex + 1);
this.bombPosition.y.push(rowIndex + 1);
}
game.fillText(emoji, posX, posY);
});
});
this.movePlayer();
},
getX: function (icon, posicionNumber) {
if (icon === 'X') {
return elementSize * ((posicionNumber - 0.42));
} else if (icon === 'I') {
return elementSize * ((posicionNumber - 0.48));
} else if (icon === 'O') {
return elementSize * ((posicionNumber - 0.48));
} else if (icon === 'PLAYER') {
return elementSize * ((posicionNumber - 0.48));
} else if (icon === 'BOMB_COLLISION') {
return elementSize * ((posicionNumber - 0.42));
}
},
getY: function (icon, posicionNumber) {
if (icon === 'X') {
return elementSize * ((posicionNumber - 0.32));
} else if (icon === 'I') {
return elementSize * ((posicionNumber - 0.28));
} else if (icon === 'O') {
return elementSize * ((posicionNumber - 0.32));
} else if (icon === 'PLAYER') {
return elementSize * ((posicionNumber - 0.20));
} else if (icon === 'BOMB_COLLISION') {
return elementSize * ((posicionNumber - 0.32));
}
}
}
window.addEventListener('load', setCanvasSize);
window.addEventListener('resize', setCanvasSize);
function setCanvasSize() {
canvasSize = Math.min(window.innerWidth, window.innerHeight) * 0.7;
canvas.setAttribute('width', canvasSize);
canvas.setAttribute('height', canvasSize);
elementSize = canvasSize / 10.20;
startGame();
}
function startGame() {
playerRender.render()
}
// Mapeo entre teclas y direcciones
const keyToDirection = {
ArrowUp: 'up',
ArrowLeft: 'left',
ArrowDown: 'down',
ArrowRight: 'right',
w: 'up',
a: 'left',
s: 'down',
d: 'right'
};
const directionToButton = {
up: document.querySelector('#up'),
left: document.querySelector('#left'),
down: document.querySelector('#down'),
right: document.querySelector('#rigth'),
};
Object.keys(directionToButton).forEach(direction => {
directionToButton[direction].addEventListener('click', () => {
move(direction);
});
});
window.addEventListener('keydown', moveByKey);
function moveByKey(event) {
const direction = keyToDirection[event.key];
if (direction) {
move(direction);
}
}
function move(direction) {
if (direction == 'up') {
if ((playerRender.playerPosition.y) >= 2) playerRender.playerPosition.y -= 1
} else if (direction == 'left') {
if ((playerRender.playerPosition.x) >= 2) playerRender.playerPosition.x -= 1
} else if (direction == 'right') {
if (playerRender.playerPosition.x <= 9) playerRender.playerPosition.x += 1
} else if (direction == 'down') {
if (playerRender.playerPosition.y <= 9) playerRender.playerPosition.y += 1
}
if (playerRender.playerPosition.x == playerRender.giftPosition.x && playerRender.playerPosition.y == playerRender.giftPosition.y) {
console.log('colision jugador con regalo')
}
playerRender.bombPosition.x.forEach((xBombs, indexBomb) =>{
let yBombs = playerRender.bombPosition.y[indexBomb]
if (playerRender.playerPosition.x == xBombs && playerRender.playerPosition.y == yBombs) {
console.log('Colision Jugador con bombas')
}
})
console.log('Posicion jugador', playerRender.playerPosition, 'Posicion bomba', playerRender.bombPosition)
playerRender.movePlayer();
startGame();
}
Yo lo hice así all in one 😎
Psdt. Puse un setTimeout para poner el jugador por encima de todos los obstaculos
Yo lo solucioné en los movimientos del usuario para que evalúe si la posición es igual a la del objecto (emoji).
Yo también me topé con el error de los decimales y lo solucioné desde el principio con Math.round()
y me funciona muy bien
a mi no me detectaron!! =(
Si me llegó a pasar lo de que los decimales no coincidían xd
Para la colisión de bombas creé un array para almacenar las posiciones de cada bomba y luego verificar si existe colisión
Agregué la colisión entre el jugador y el regalo en la función de movePlayer()
Hice una funcion para detectar colisiones o coincidencias en general( uso la misma función para saber si llego al regalo o si esta en la entrada):
function detectColission({ x, y }) {
if (
playerPosition.x - x <= blocksSize * 0.05 &&
playerPosition.y - y <= blocksSize * 0.05 &&
playerPosition.x + blocksSize - x >= blocksSize * 0.05 &&
playerPosition.y + blocksSize - y >= blocksSize * 0.05
) {
return true;
} else {
return false;
}
}
Tengamos en cuenta que bombsPosition es un array de objetos con la posición de las bombas
bombsPosition.forEach((bomb) => {
if (detectColission(bomb)) {
console.log("You lose!!");
}
});
No creo que sea la manera mas optima evaluar con cada movimiento y comparar con todas las posiciones pero pensare en algo mientras continuo el curso.
Si usamos la lógica, claramente el mejor lugar de nuestro código para colocar el condicional que detecta las colisiones es en la función movePlayer().
¿Por qué?
Pues precisamente porque, hasta que no movamos nuestro jugador no sabremos si está en una u otra posición y por lo tanto el momento exacto en el que nos desplazamos es el ideal para analizar si hay una colisión.
Agregué un array vacío para guardar las posisiones
let bombPositions = [];
luego cuando iteramos las posisiones en el mapa asignamos las coordenadas de x , y y hacemos push al array con cada posisión
mapRowToCol.forEach( (row, rowIndex) => {
row.forEach( (col, colIndex) => {
let posX = elementSize * (colIndex + 1);
let posY = elementSize * (rowIndex + 1);
let emoji = emojis [col];
let bombPosition = {
X: undefined,
Y: undefined,
}
if (col == 'O') {
if(!playerPosition.X && !playerPosition.Y){
playerPosition.X = posX;
playerPosition.Y = posY;
};
} else if (col == 'I') {
giftPosition.X = posX;
giftPosition.Y = posY;
} else if (col == 'X') {
bombPosition.X = posX;
bombPosition.Y = posY;
bombPositions.push(bombPosition);
}
La razon por la cual los objetos constantes despues se pueden cambiar, es porque nunca cambiamos el tipo de dato.
const myObject = {
x : undefined
}
myObject.x = 10 //Estamos modificando la propiedad del objeto. pero sigue siendo un objeto.
myObject = new Array(1,2) //Error: ¿Porque? Porque estamos intentando cambiar el “TIPO” de dato
el dato curioso sobre los objetos en javascript no la sibia. Gracias Juan David
Como estoy un objeto literal de mi mapa, entonces ahi le inserte el array de las bombas
const map= {
...
bombPosition: [],
...
}
Y para que se agregue cada posición de la bomba
En el funcion render que esta dentro de mi objeto map, hago
if (y === "X") {
this.bombPosition.push({ x: posX, y: posY })
}
Así pues ya se guarda todas las posiciones de las bombas
Ahora me puse el reto de que se renderice el siguiente mapa, cuando gane,
tomar en cuenta
Para lo que agregre una nueva funcion que se llama lvlUp().
Esta funcion le aumenta el lvl, resetea el array de las bombas, puesto que en cada nivel tendremos diferentes posiciones, y vuelve a renderizar el game, con el siguiente nivel.
Es importante la parte de limpiar el array de bombas que creamos
function lvlUp() {
if (map.lvl < maps.length - 1) {
map.lvl += 1;
map.bombPosition = []
startGame()
}
}
Entonces como estamos haciendo prácticamente todo cuando se mueve el jugador,
mi funcion que lo renderiza(si están haciendo como el profe, seria su movePlayer), queda asi
playerPosition: {
x: undefined,
y: undefined,
render: function () {
game.fillText(emojis["PLAYER"], this.y, this.x);
const coincideX = Math.floor(this.x) === Math.floor(map.giftPosition.x);
const coincideY = Math.floor(this.y) === Math.floor(map.giftPosition.y);
if (coincideY && coincideX) {
console.log("Subiste de nivel")
lvlUp()
}
endGame()
}
}
Ah, tambien cree la funcion endGame, que está lo que hace es decirme cuando perdi, y tambien va dentro del render del jugador
function endGame() {
for (let bomb of map.bombPosition) {
const coincideX = Math.floor(map.playerPosition.x) === Math.floor(bomb.x);
const coincideY = Math.floor(map.playerPosition.y) === Math.floor(bomb.y);
if (coincideY && coincideX) {
console.log("perdiste")
}
}
}
Con todo esto, ya subo de nivel, y tambien me dice cuando perdi
Sin embargo el bug que me esta corriendo es que, cuando paso por encima de una bomba, me aparece como si hubiera pasado varias veces
Lo que hice fue crear un array con objetos de las posiciones de las bombas, en el mismo lugar que hicimos lo de PLAYER y GIF
y una variable global
let bombasPosition = [];
if (col == "X") {
bombasPosition.push({
x: posX,
y: posY,
});
}
y abajo de donde se agrego la colisión de gif
EL siguiente codigo.
const bombaColision = bombasPosition.find((position) => {
const bombaColisionX = playerPosition.x.toFixed(3) == position.x.toFixed(3);
const bombaColisionY = playerPosition.y.toFixed(3) == position.y.toFixed(3);
return playerPosition.x
? bombaColisionX && bombaColisionY
: false;
});
console.log("colision",bombaColision);
if(bombaColision) console.log("Bomba Perdiste");
};
Con el find en el memento que encuentra la colisión termina de buscar.
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?