Contenido del curso
Canvas
Mapa del juego
Movimientos del jugador
Colisiones
Bonus: adictividad
Deploy
Próximos pasos
Reto: timeouts de victoria o derrota
Contenido del curso
Reto: timeouts de victoria o derrota
Axel Enrique Galeed Gutierrez
studentJuan Castro
teacherManuel Araujo
studentSergio Javier Lopez Olivera
studentJose Carlos Espinosa Espinosa
studentSergio Javier Lopez Olivera
studentJosé Fernando Fuentes Roa
studentElío Diez
studentEder Raúl Abarca Raviela
studentCristian Paiva
studentMatias Ezequiel Rivero
studentOrlando Rodriguez
studentLeonardo Garrido
studentLaura Roa
studentJuan Castro
teacherLaura Roa
studentYhoshua Bernal
studentHarold Zurita Simon
studentRaúl Adolfo Sánchez Rodríguez
studentJose Angel Esquivel Cisneros
studentRaúl Adolfo Sánchez Rodríguez
studentManuel Andres García Vera
studentStivenson David Mejia Palacios
studentKevin Cantú Gómez
studentAlejandro Ramos
studentAlejandro Ramos
studentAlejandro Ramos
studentLes comparto, el resultado final de mi juego. 😄 Todo feedback es bienvenido. Repositorio de GitHub. Link del juego.
Dude, qué buen proyecto. Te quedó muy, muy genial tu personalización. Las instrucciones al principio :ok_hand:.
Creo una función showCollision que limpie todo el canvas y muestre un mensaje, cuyo contenido depende de si el jugador aún tiene vidas o no.
function showCollision() { game.clearRect(0, 0, canvasSize, canvasSize); game.font = '10px Verdana'; game.textAlign = 'center'; if(lives > 1) { game.fillText('PERDISTE UNA VIDA, VUELVE A INTENTARLO', canvasSize/2, canvasSize/2); } else { game.fillText('PERDISTE TODAS LAS VIDAS, VUELVE AL INICIO', canvasSize/2, canvasSize/2); } }
Luego, dentor de la fiunción movePlayer, en el condicional donde se verifica si hubo colisión, agrego la función showCollision y establezco un setTimeout para que espere un tiempo antes de llamar a la función leverlLost (levelFail para el profesor)
if (enemyCollision) { console.log('Chocaste contra un enemigo :('); showCollision(); setTimeout(levelLost, 2000); }
Después de muchos intentos y ver a otros compañeros así pude resolver el reto de las colisiones y que se mostrase donde había chocado:
else if (bombCollision) { showCollision(); setTimeout(gameLose, 1000); }
Dentro de la función de movePlayer en el condicional pude introducir esta función con el setTimeout .
function showCollision(){ firePos.x = playerPos.x; firePos.y = playerPos.y; console.log(firePos.x, firePos.y); fireExp(); } function fireExp(){ game.fillText(emojis['BOMB_COLLISION'], firePos.x, firePos.y) }
Arriba está la función que se llama en movePlayer y esta a su vez llama a otra que ubica en las posiciones del jugador el emoji del fuego, con ello mostrando donde se chocó el jugador.
Una pregunta lo intente hacer como tu lo hiciste, pero hace la animación de la explosión pero encima de la bomba y debajo del jugador, te pasa lo mismo o te paso, sabes como resolverlo.
@jose-carlos-espinosa-espinosa saludos, si me ocurría, aunque en este momento no recuerdo como lo resolví, eso creo es debido a que al momento de la colisión se ejecuta la función de showCollision() y luego la de setTimeOut con game lose:
function movePlayer() { game.fillText(emojis['PLAYER'], playerPos.x, playerPos.y); const bombCollision = bombsPos.find( bomb => { const bombPosX = bomb.x.toFixed(0) == playerPos.x.toFixed(0); const bombPosY = bomb.y.toFixed(0) == playerPos.y.toFixed(0); //this two values return booleans return bombPosX && bombPosY; //using return at the end with "&&" operator will help us to have as a value the coincidential positions }) if (playerPos.x == giftPos.x && playerPos.y == giftPos.y){ gameWin(); } else if (bombCollision) { showCollision(); setTimeout(gameLose, 1000); } }//esto asociado al movimiento del jugador function showCollision(){ firePos.x = playerPos.x; firePos.y = playerPos.y; console.log(firePos.x, firePos.y); fireExp(); } function fireExp(){ game.fillText(emojis['BOMB_COLLISION'], firePos.x, firePos.y) } function gameLose() { lives--; playerPos.x = undefined; playerPos.y = undefined; indicator = true; if (lives <= 0) { level = 0; lives = 3; timeStart = undefined; startGame(); } else { // game.fillText(emojis['BOMB_COLLISION'], playerPos.x, playerPos.y); startGame(); } }
Hola a todos
Acá les comparto algunas de las mejoras que le realicé al juego.
Aquí mi versión del juego. Todavía no he podido resolver el último reto sin que se rompa todo pero el mapa tiene algunos atajos y los encuentros con alienígenas se premian con vidas ;-).
Aquí mi repositorio: https://github.com/ElioDiez/Curso-Videojuego-en-JavaScript
Y aquí el juego: https://eliodiez.github.io/Curso-Videojuego-en-JavaScript/
¡Seguiré intentándolo!
Implemente mensajes en el canvas cada vez que chocas, pierdes o ganas:
GitHub: https://github.com/RedCode-123/Videojuego_JavaScript_2022
Acá dejo mi aporte. Le agregue un par de pantallas extras como por ejemplo el conteo hacia atrás antes de comenzar el game, como también una ventana de Game over y cuando ganas el game. Repo: Juego:
Hola comparto el proyecto. De los retos, el que más me costo es el de posicionar el jugador en el lugar correcto al hacer resize del HTML. mhgame web del proyecto repo en GitHub
Muchas gracias. Excelente curso.
Se me ocurrio usar una clase que nombro "inactive" en CSS le agrego display:none. En mi js luego, en el codigo de las colisiones agrego.
const boom = setInterval(() => {canvas.classList.toggle('inactive'); game.fillText(emojis['BOMB_COLLISION'], collision.x, collision.y)}, 70); console.warn('BOOOM'); setTimeout(() => {clearInterval(boom); startGame() }, 700);
El canvas desaparece y aparece . Pruébalo has una colision. https://orlando0x.github.io/videojuego-con-javascript/
Esta fue mi resolución en base a ver como usar el set interval en los comentarios.
if (enemyCollision) { showColision(); //espera 1 segundo para empezar la funciona level fail// setTimeout (levelFail,1000); }
para que el fuego aparezca y no se vea la calavera hice lo siguiente.
function showColision() { game.fillText (emojis['BOMB_COLLISION'], playerPosition.x, playerPosition.y); playerPosition.x = undefined; playerPosition.y = undefined; console.log ('choque'); }
Hola! estaba todo bien, puse el toFixed() y me funciono bien con los mapas que ya teniamos hechos, pero cuando intente agregar un mapa por mi parte, fallo y tengo otra vez ese error. Ya he mirado mucho y no se que pueda estar fallando. Agradeceria si tienen alguna sugerencia. Este es mi repositorio https://github.com/laulexa/video-game-project--learningJS
Veo que hiciste un nuevo commit después de agregar los nuevos mapas y que el jugo ya funciona otra vez: https://laulexa.github.io/video-game-project--learningJS/
¿Te late si nos cuentas cuál era eel error y cómo lo solucionaste? :D
Hola profe! No de hecho no lo habia solucionado :( , vi tu comentario y en mi computador aun habian errores. Trate de solucionarlo y di con una solucion que no estoy segura si sea tan buena pero hice todo tipo de pruebas y no salio ese error. Lo que hice fue restarle 1 a element size y multiplicar window.innerheight y window.innerWidth por 0.6:
function setCanvasSize() { if (window.innerHeight > window.innerWidth) { canvasSize = fixNumber(window.innerWidth * 0.6); } else { canvasSize = fixNumber(window.innerHeight * 0.6) } canvasSize = Number(canvasSize.toFixed(0)); canvas.setAttribute('width', fixNumber(canvasSize)); canvas.setAttribute('height', fixNumber(canvasSize)); elementsSize = fixNumber(canvasSize / 10) -1; playerPosition.x = undefined; playerPosition.y = undefined; restartGame(); }
Y finalmente en la funcion startGame(), cambie la siguiente linea y en vez de 1 le agregue un 1.2
const posX = fixNumber(elementsSize * (colI + 1.2)); const posY = fixNumber(elementsSize * (rowI + 1));
Seguie trabajando en ello porque antes de publicar este post vi que una pantalla fallo :( Hice la ultima prueba en un mac y en pantalla completa se rompio el ultimo mapa en las cajas de la parte derecha. este es mi repositorio seguire trabajando en el proyecto y no se si otras cosas cambien despues. https://github.com/laulexa/video-game-project--learningJS
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 restartButton = document.querySelector ('#restart'); const deleteRecord = document.querySelector('#delete-record'); const spanLives = document.querySelector('#lives'); const spanTime = document.querySelector('#time'); const spanRecord = document.querySelector('#record'); const pResult = document.querySelector('#result'); const countdownText = document.querySelector('.countdown'); let canvasSize; let elementsSize; let level = 0; let lives = 3; let timeStart; let playerTime; 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 = fixNumber(window.innerWidth * 0.6); } else { canvasSize = fixNumber(window.innerHeight * 0.6) } canvasSize = Number(canvasSize.toFixed(0)); canvas.setAttribute('width', fixNumber(canvasSize)); canvas.setAttribute('height', fixNumber(canvasSize)); elementsSize = fixNumber(canvasSize / 10) -1; playerPosition.x = undefined; playerPosition.y = undefined; //countdown() restartGame(); } function startGame() { console.log({ canvasSize, elementsSize }); console.log(window.innerWidth, window.innerHeight); game.font = fixNumber(elementsSize) + 'px Verdana'; game.textAlign = 'end'; const map = maps[level]; // console.log(map) 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.2)); 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'], fixNumber(playerPosition.x), fixNumber(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('¡Game Finished!'); 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() { if(timeStart == undefined) { spanTime.innerHTML = 0; } else { //spanTime.innerHTML = Date.now() - timeStart; spanTime.innerHTML = ((Date.now() - timeStart)/1000).toFixed(2) + " S"; } } function showRecord() { spanRecord.innerHTML = localStorage.getItem('record_time') + ' S'; } //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); deleteRecord.addEventListener('click', deleteRecords); upButton.addEventListener('click', moveUp); rightButton.addEventListener('click', moveRight); leftButton.addEventListener('click', moveLeft); downButton.addEventListener('click', moveDown); restartButton.addEventListener('click', restartGame); 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 deleteRecords() { console.log('record deleted') localStorage.removeItem('record_time'); spanRecord.innerText = '0' } 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(); } } function countdown() { console.log("counting down"); let numberCount = 3 function count () { if (numberCount > 0) { countdownText.innerHTML = numberCount; numberCount--; setTimeout(count, 700); } else { countdownText.innerHTML ='Go!'; } } count () startGame() } function restartGame() { console.log('restart game'); level = 0; lives = 3; pResult.innerHTML = ''; playerPosition.x = undefined; playerPosition.y = undefined; setTimeout(startGame, 3000); countdown() timeStart= undefined; } function victory() { } function collision() { // setTimeout() }
maps.js
/* * Reglas: * El final de cada nivel debe ser el inicio del siguiente */ // const img1 = document.createElement("img"); // img1.src = "./myDog.png"; const emojis = { '-': ' ', 'O': '🚪', 'X': '📦', 'I': '🧀', 'PLAYER':'🐹', 'BOMB_COLLISION': '🔥', 'GAME_OVER': '👎', 'W': '🏆', 'HEART': '❤️', 'S': '🌟' }; const maps = []; maps.push(` IXXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX -XXXXXXXXX OXXXXXXXXX `); maps.push(` O--XXXXXXX X--XXXXXXX XX----XXXX X--XX-XXXX X-XXX--XXX X-XXXX-XXX XX--XX--XX XX--XXX-XX XXXX---IXX XXXXXXXXXX `); maps.push(` I-----XXXX XXXXX-XXXX XX----XXXX XX-XXXXXXX XX-----XXX XXXXXX-XXX XX-----XXX XX-XXXXXXX XX-----OXX XXXXXXXXXX `); maps.push(` O--------X XXXXXXXX-X XXXX---X-X XXXX-X---X XXXX-XXXXX XXXX-----X XXXXXXXX-X XXX---XX-X ----X-XX-X IXXXX----X `); maps.push(` WWWWWWWWWW WWWWWWWWWW WWWWWWWWWW WWWWWWWWWW WWWWWWWWWW WWWWWWWWWW WWWWWWWWWW WWWWWWWWWW WWWWWWWWWW SWWWWWWWWW `);
styles.css
/* font-family: 'RocknRoll One', sans-serif; */ body { background-color: indigo; color: #fff; font-family: 'RocknRoll One', sans-serif;; margin: 0; padding: 0; } .game-container { display: flex; align-items: center; flex-direction: column; justify-content: space-between; height: 100vh; width: 100vw; } canvas { border: 4px solid #ff6600; border-radius: 8px; background-color: #feff9d; } .winner-message { align-items: center; background-color: #82B700; color: rgb(255, 255, 255); display: flex; font-size: 20px; font-weight: bold; justify-content: center; margin-top: 10px; height: 200px; width: 300px; } .loser-message { align-items: center; background-color: #b72500; color: rgb(255, 255, 255); display: flex; font-size: 20px; font-weight: bold; justify-content: center; margin-top: 10px; height: 200px; width: 300px; } .final-message { place-items: center; } .btns { display: flex; justify-content: center; flex-wrap: wrap; margin: 0 auto; width: 100%; } button { background-color: #fdef05; border-radius: 8px; border-color: #ff6600; font-family: inherit; margin: 0; padding: 5px 20px; width: 100px; } button:not(:last-child) { margin-right: 10px; } .restart-delete-btn { background-color: #82B700; border-color: #ff6600; font-weight: bold; font-size: 10px; padding: 5px 0 5px; height: 50px; } .messages { display: flex; flex-wrap: wrap; margin: 10px auto; width: 80%; max-width: 460px; } .stats { display: block; margin: 0; width: 100%; } .countdown { text-align: center; font-size: 30px; } .inactive { display: none; } @media (max-width: 440px) { button { margin: 5px 0 5px; padding: 10px 20px; } #up, #down { margin-left: 100%; margin-right: 100%; } #restart-delete-btn { margin: 15px 0 15px;} .messages {text-align: center;} }
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="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=RocknRoll+One&display=swap" rel="stylesheet"> <link rel="stylesheet" href="./styles.css"> <title>VideoGame Project</title> </head> <body> <div class="game-container"> <canvas id="game"></canvas> <div class="winner-message inactive"> <p class="final-message">You Win!</p> </div> <div class="loser-message inactive"> <p class="final-message">Game Over</p> </div> <p class="countdown"></p> <div class="btns"> <button id="up">Up</button> <button id="left">Left</button> <button id="right">Right</button> <button id="down">Down</button> <button id="restart" class="restart-delete-btn">Restart</button> <button id="delete-record" class="restart-delete-btn">Delete Record</button> </div> <div class="messages"> <p class="stats">Lives: <span id="lives"></span></p> <p class="stats">Time ⏰: <span id="time"></span></p> <p class="stats">Record 🏁: <span id="record"></span></p> <p class="stats" id="result"></p> </div> </div> <script src="./maps.js"></script> <script src="./game.js"></script> </body> </html>
HIce algunos cambios interesantes que si me retaron un poco link al juego:
github:
Hola 🙋♂️, les comparto mi proyecto: Dungeon Magician
Buscaminas y un rpg 2d tradicional, Dungeon Magician trata de un mago en busca de gemas encantadas.
Primera versión
🎮Taller: Crea tu primer videojuego -Mi proyecto🎮
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:
Incorporación de fuego 🔥 en las colisiones contra las bombas
Contador de tiempo ⏱ en segundos + 2 decimales
Tarjetas interactivas al perder, ganar sin récord y ganar con nuevo récord
Pantalla de inicio del juego 🤠con enlaces a redes sociales
Animación al ganar un nivel y perder vidas 🟠🟡🟢🔵🔴
Pantalla para registrar tus mejores récords.🎖🎖
⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
📌Prueba la demo del juego 🎮
📌Échale un vistazo al repositorio de GitHub
🎮Deploy de 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 | Ver |
| Contador de tiempo ⏱ en segundos + 2 decimales | Ver |
| Tarjetas interactivas al perder, ganar sin récord y ganar con nuevo récord | Ver |
| Pantalla de inicio del juego 🤠 | Ver |
| Animación al ganar un nivel 🟠🟡🟢🔵🔴 | Ver |
📌Prueba la demo del juego 🎮 ⭐⭐⭐⭐⭐
📌Échale un vistazo al repositorio de GitHub
📌Échale un vistazo al Taller de Platzi
Dejo mi jueguito después de tomar características como modelos de algunos compañeros logre terminar los retos
++Dejo mi juego desplegado++ https://manuel15939.github.io/taller-practico-javascript-videogame/ ++Mi repositorio++ https://github.com/manuel15939/taller-practico-javascript-videogame
creo que tienes bugs Manuel, en la resolución actual que tengo en el navegador no puedo acceder al regalo e intente ir a la derecha pero la bomba no explotó no me devolvió al inicio.
Cuando haya una colisión entre la calaverita y una bomba lo cambio por una explosión y uso el setTimeout para después retomar el punto de partida inicial del mapa en el que nos encontremos
if(giftCollisionXY){ levelUp(); }else if(bombaCollisionXY){ console.log('Tocaste una bomba'); game.fillText(emojis['EXPLOTION'],playerPosition.x,playerPosition.y); setTimeout(gameFailed,1000); }else{ game.fillText(emojis['PLAYER'],playerPosition.x,playerPosition.y); }
Si bien hay bastantes cosas para mejorar o añadir a mi código, por ahora mi problema es hacer que las interfaces de los navegadores en los móviles no interfieran con el contenido de la página tapándolo xd
Creo firmemente que hay mucho por mejorar en el aspecto de diseño :p
EN fin, link del juego :D ↓ juego
Hay muchas ideas interesantes en la sección de comentarios, haré mi idea, pero aprenderá de las demás!