CursosEmpresasBlogLiveConfPrecios

Detectando colisiones con arrays

Clase 13 de 24 • Taller Práctico de JavaScript: ¡Crea tu Primer Videojuego!

Clase anteriorSiguiente clase

Contenido del curso

Introducción
  • 1
    Programemos un juego con JavaScript

    Programemos un juego con JavaScript

    17:13
Canvas
  • 2
    ¿Qué es canvas en JavaScript?

    ¿Qué es canvas en JavaScript?

    18:37
  • 3
    Tamaño del canvas y sus elementos

    Tamaño del canvas y sus elementos

    17:37
  • 4
    Canvas responsive

    Canvas responsive

    12:37
Mapa del juego
  • 5
    ¿Qué es un array bidimensional?

    ¿Qué es un array bidimensional?

    05:48
  • 6
    Arreglos multidimensionales en JavaScript

    Arreglos multidimensionales en JavaScript

    23:38
  • 7
    Refactor del mapa de juego

    Refactor del mapa de juego

    20:48
Movimientos del jugador
  • 8
    Eventos y botones

    Eventos y botones

    14:14
  • 9
    Objeto playerPosition

    Objeto playerPosition

    11:34
  • 10
    Limpieza de movimientos

    Limpieza de movimientos

    09:53
  • 11
    No te salgas del mapa

    No te salgas del mapa

    11:15
Colisiones
  • 12
    Detectando colisiones fijas

    Detectando colisiones fijas

    13:29
  • 13
    Detectando colisiones con arrays

    Detectando colisiones con arrays

    14:11
  • 14
    Victoria: subiendo de nivel

    Victoria: subiendo de nivel

    07:50
  • 15
    Derrota: perdiendo vidas

    Derrota: perdiendo vidas

    11:45
Bonus: adictividad
  • 16
    Sistema de vidas y corazones

    Sistema de vidas y corazones

    13:02
  • 17
    Sistema de tiempo y puntajes

    Sistema de tiempo y puntajes

    19:30
  • 18
    ¿Qué es localStorage?

    ¿Qué es localStorage?

    07:33
  • 19
    Guardando records del jugador

    Guardando records del jugador

    18:35
Deploy
  • 20
    Depurando errores del juego

    Depurando errores del juego

    23:16
  • 21
    Desplegando el juego a GitHub Pages

    Desplegando el juego a GitHub Pages

    07:39
Próximos pasos
  • 22
    Reto: reinicio del juego

    Reto: reinicio del juego

    03:36
  • 23
    Reto: timeouts de victoria o derrota

    Reto: timeouts de victoria o derrota

    04:14
  • 24
    ¿Quieres un simulador laboral?

    ¿Quieres un simulador laboral?

    06:53
    Manuel Araujo

    Manuel Araujo

    student•
    hace 3 años

    Tranquilo Juan, el error de los decimales me pasó desde la primera vez que probé el código jejeje. Te creemos!

      Gianluca Enzo Procopio

      Gianluca Enzo Procopio

      student•
      hace 3 años

      X2 jajajaj

      JUAN DAVID MARIN URIBE

      JUAN DAVID MARIN URIBE

      student•
      hace 3 años

      x3 Despues de un rato debugueando me di cuenta lo que pasaba, sin saber que en los ultimos 3 minutos del video estaba la explicaion jajaj

    Angel David Velasco Bonifaz

    Angel David Velasco Bonifaz

    student•
    hace 3 años

    Para no volver hacer el array en cada movimiento y estar limpiando, solo coloque una variable bandera.

    let flag=true;

    y donde asignamos las posiciones de las bombas se pone un condicional.

    if (col== "X" && flag) { bombasPosition.push({ x: posX, y: posY, }); }

    y afuera del ciclo for each de las rows, colocamos la bandera en falso

    flag=false;

    con eso cuando se vuelva a pasar ya no agregara otra vez las bombas

      César Augusto Cortés Labrada

      César Augusto Cortés Labrada

      student•
      hace 3 años

      Me parece una solución bastante acertada.

      Luis Palomo

      Luis Palomo

      student•
      hace 3 años

      Si, yo pensé lo mismo. Si el array fuera más largo estaríamos renderizando siempre eso.

    marco antonio

    marco antonio

    student•
    hace 3 años

    ver tanta re-iteración hace que me duela la ram y me lastime el procesador XD, Ya hablando en serio el código de la clase se ve un poco.. rebelde, me cuesta seguirlo

    como sea agregue algunas cosillas a mi código y lo he hecho un poquito mas eficiente creo que esta listo para que lo encapsule en una clase y haga unas modificaciones finales para que sea adaptativo
    //contect const canvas = document.querySelector('#game'); const btnArriba = document.querySelector('#arriba'); const btnAbajo = document.querySelector('#abajo'); const btnDer = document.querySelector('#derecha'); const btnIzq = document.querySelector('#izquierda'); const juego = canvas.getContext('2d'); //signals: window.addEventListener('load', resizeEvent); window.addEventListener('resize', resizeEvent); let posJugador = undefined; let mapa = Array(); let nivel = 0; let prePosJugador = undefined; let celda_t = 0; let nivel_actual = undefined; let puntoDePartida = Number(0); let canvas_t = 0; var actualizado = Boolean(false); let explociones = Array(); let vidas = 3; function resizeEvent(){ actualizado = false; canvas_t = (window.innerWidth < window.innerHeight ? window.innerWidth : window.innerHeight) *.75 ; canvas.setAttribute('width', canvas_t); canvas.setAttribute('height',canvas_t); celda_t = canvas_t / 10; juego.textAlign='end'; juego.font = celda_t - celda_t / 10 +'px arial'; update();} function update(){ if(Victoria() && actualizado) return; cargarMapa(); clear(); paintEvent(); paintEventPlayer(); paintEventExplosion(); paintEventGameOver(); paintEventVictory(); actualizado = true;} function cargarMapa(){ if(nivel_actual == nivel || Victoria()) return; nivel_actual = nivel; mapa = map[nivel].match(/[IOX-]/g);} function clear(){ if(!actualizado) { juego.clearRect(0,0, canvas_t, canvas_t); return;} if(posJugador == prePosJugador) return; clearRect(posJugador); clearRect(prePosJugador);} function clearRect(indice){ const x = posX(indice ) - celda_t / 10; const y = posY(indice ) + celda_t / 5; juego.clearRect(x - 1 ,y , - celda_t, - (celda_t + 1));} function posY(indice){ return (~~(indice/10) + 1) * celda_t - celda_t / 5; } function posX(indice){ return (indice - (~~(indice/10) * 10) + 1) * celda_t + celda_t / 10;} function paintEvent(){ if(!actualizado && !Victoria()) mapa.forEach((char, idx) => { if(char == 'O' && posJugador == undefined) puntoDePartida = posJugador = idx; juego.fillText(emojis[char], posX(idx), posY(idx) );});} function paintEventPlayer(){ if(victoryEvent()) paintEventVictory(); else { if(posJugador != undefined && prePosJugador != posJugador){ juego.fillText(emojis[mapa[prePosJugador]],posX(prePosJugador),posY(prePosJugador)); if(collisionEvent()) juego.fillText(emojis['PLAYER'], posX(posJugador),posY(posJugador)); else paintEventGameOver();}}} function collisionEvent(){ if(vidas && mapa[posJugador] == 'X'){ explociones.push(posJugador); --vidas; juego.fillText(emojis['BOMB_COLLISION'],posX(posJugador),posY(posJugador)); posJugador = puntoDePartida;} if(!vidas) { explociones = Array(); return false;} /*juego terminado*/ return true; /*continuar con el juego*/} function paintEventExplosion(){ if(!explociones.length) return; explociones.forEach((pos) =>{ clearRect(pos); juego.fillText(emojis['BOMB_COLLISION'],posX(pos),posY(pos));});} function victoryEvent(){ if(mapa[posJugador] != 'I') return false; else if(nivel < map.length) ++nivel; explociones = Array(); if(Victoria()) return true; posJugador = prePosJugador = undefined; actualizado = false; update(); return false;} function paintEventGameOver(){ if(vidas) return; paintScreen('Game Over','GAME_OVER', '#FF2F22', '#211A27');} function paintEventVictory(){ if(!Victoria()) return; paintScreen('Has Ganado','WIN','#ffd700','#01433A');} function Victoria(){return nivel >= map.length;} function paintScreen(txt, emoji, colortxt = '#fff', backgroundColor ='#000'){ juego.clearRect(0,0, canvas_t, canvas_t); mapa.forEach((char, idx) =>{ if(char == 'X') char = emoji; juego.fillText(emojis[char],posX(idx),posY(idx));}); const fuente = celda_t - celda_t / 10 ; const media = canvas_t / 2; juego.fillStyle = backgroundColor; juego.fillRect(0, media - (fuente /2), canvas_t, fuente); juego.textAlign = 'center'; juego.fillStyle = colortxt; juego.fillText(txt, media, media + fuente/3); juego.textAlign='end';} //signals: window.addEventListener('keydown',keyMov); btnArriba.addEventListener('click', movArriba); btnAbajo.addEventListener('click', movAbajo); btnDer.addEventListener('click', movDer); btnIzq.addEventListener('click', movIzq); //slots: function keyMov(event){ switch(event.keyCode){ case 37: movIzq(); break;//izquierda case 38: movArriba(); break;//arriba case 39: movDer(); break; //derecha case 40: movAbajo(); break;//abajo default: break;}} function movArriba(){ prePosJugador = posJugador; posJugador -= 10; if(posJugador < 0) posJugador = prePosJugador; update();} function movAbajo(){ prePosJugador = posJugador; posJugador += 10; if(posJugador >= 100) posJugador = prePosJugador; update();} function movDer(){ prePosJugador = posJugador; const pered = (~~(posJugador / 10)) *10 + 10; ++posJugador; if(posJugador >= pered) posJugador = prePosJugador; update();} function movIzq(){ prePosJugador = posJugador; const pered = ~~(posJugador /10) * 10; --posJugador; if(posJugador < pered) posJugador = prePosJugador; update();}
      Aaron Santiago Loyo Zabala

      Aaron Santiago Loyo Zabala

      student•
      hace 3 años

      Mucho Texto

      Emilio Sala

      Emilio Sala

      student•
      hace 2 años

      tal vez el propósito de la clase es enseñarnos lo que no debemos hacer y buscar mejor formas de resolver los problemas que nos van surgiendo por nuestra cuenta. en los aportes hay muy buenas formas de avanzar

    Valeria Soledad Paez

    Valeria Soledad Paez

    student•
    hace 3 años

    para no cambiar el const del array

    enemyPosition.splice(0, enemyPosition.length);
    Eduard Barrios

    Eduard Barrios

    student•
    hace 3 años

    jajajaja me da risa que a Juan no le parece el error pero a mi me apreció antes de que él lo mencionara y lo solucioné luego él lo menciona pero no le sale y a mi me sale cada que muevo al jugador

    Kevin Camacho

    Kevin Camacho

    student•
    hace 3 años

    vaciar un array

    Así me enseñó Notion IA.

    const array = ['h', 'o', 'l', 'a', 'm', 'u', 'n', 'd', 'o'] array.length = 0

    Obviamente funciona pero no se si es la mejor manera de hacerlo.

    Jhon Eduard Bocanegra Ortiz

    Jhon Eduard Bocanegra Ortiz

    student•
    hace 3 años
    lalala.png
    Gianluca Enzo Procopio

    Gianluca Enzo Procopio

    student•
    hace 3 años

    Para detectar una colisión hice una validación true/false con el método SOME de arrays. Lo vimos en el curso de manipulación de arrays. Pero si no lo viste, el metodo some se trata de que si al menos un elemento del array cumple con la condición, devuelve true.

    if(enemyPosition.some(item => item.x == playerPosition.x && item.y == playerPosition.y)){ alert("Chocaste con una bomba") }
    Luis Palomo

    Luis Palomo

    student•
    hace 3 años

    Juan! A mi me pasó el error que decías. Incluso lo arreglé antes que lo mencionaras y me rascaba la cabeza pensando porque a mi no me funcionaba y a vos si. Así que te entiendo jaja.

      Gianluca Enzo Procopio

      Gianluca Enzo Procopio

      student•
      hace 3 años

      X2 jajajajaja

    Hector Maluy Fernandez

    Hector Maluy Fernandez

    student•
    hace 3 años

    Mejor ponemos todas las coordenadas a dos decimales y nos quitamos de problemas.

    platzi.png

    leonardo alies fuentes

    leonardo alies fuentes

    student•
    hace 3 años

    la verdad no me funciona el código copie y pegue el tuyo y tampoco me funciono mmm no por cual motivo

      Rubén Cuello

      Rubén Cuello

      student•
      hace 3 años

      Hola @leoaliesf397. ¿Podrías compartirnos tu código y una descripción un poco más detallada sobre qué es lo que no funciona? De esta manera estoy seguro de que podremos ayudarte.

      Juan camilo piedrahita londoño

      Juan camilo piedrahita londoño

      student•
      hace 3 años

      Colocastes el toFixed(3)? A mi no me estaba funcionando. Pero con toFixed(3). No hubo problema

    Nestor Rios Garcia

    Nestor Rios Garcia

    student•
    hace 2 años

    Yo tengo un contador de movimientos

    let moves = 0;

    Y aumenta cada vez que se mueve el jugador

    function movePlayer(direction) { moves++ . . .

    Asi que si integro mi contador en el condicional solo se ejecutara una vez

    if (column === 'X' && moves === 0) { bombsPositions.push({ x: xPosition, y: yPosition }) }
    Mauricio Combariza

    Mauricio Combariza

    student•
    hace 2 años

    Yo cree una funcion render, una start game y una movePlayer y en la de startGame cree el array con todos los elementos, incluyendo los espacios y los regalos y lo que pongamos después, en el render no creo ningun array. Este es mi código:

    function startGame() { console.log('Mapas:', mapa, mapRows, mapRowCols) mapa = maps[level] mapRows = mapa.trim().split('\n') mapRowCols = mapRows.map(row => row.trim().split('')) if (!game) { console.error('El contexto del lienzo no está disponible'); return; } game.font= elementSize + 'px Verdana' mapRowCols.forEach((row, i) => { row.forEach((col, j) => { const emoji = emojis[col] let posX = j * elementSize let posY = (i+1) * elementSize element = { elem: col, x: posX, y: posY } mapaActual.push(element) if (col == 'O'){ playerPosition = { x: posX, y: posY } // Se crea un array con el valor y las coordenadas } game.fillText(emoji, posX, posY) movePlayer() }) }) } function renderGame() { if (!game) return; game.clearRect(0, 0, canvas.width, canvas.height); mapRowCols.forEach((row, i) => { row.forEach((col, j) => { const emoji = emojis[col]; let posX = j * elementSize; let posY = (i + 1) * elementSize; if (col === 'I'){ winPosition = { x: posX, y: posY } } game.fillText(emoji, posX, posY); }); }); } function movePlayer() { if (!game) return renderGame() game.fillText(player, playerPosition.x, playerPosition.y); mapaActual.map(item => { let actualPositionX = Math.floor(playerPosition.x) === Math.floor(item.x) let actualPositionY = Math.floor(playerPosition.y) === Math.floor(item.y) let actualPosition = actualPositionX && actualPositionY if (actualPosition){ console.log(item.elem) if(item.elem === 'X') { console.log('Perdiste') level = 0 // setCanvasSize() } if(item.elem === 'I') { console.log('Ganaste eeee') if(level > maps.length - 1){ console.log('Fin del juego Ganaste!!') } else { level += 1 mapaActual = [] startGame() } } } }) }
    Alejandro Ramos

    Alejandro Ramos

    student•
    hace 3 años

    yo estoy usando Math.Floor en lugar del toFixed e.e

    Carlos Andres Alavez Santiago

    Carlos Andres Alavez Santiago

    student•
    hace 3 años

    Convertí los maps a Let, agregue un index para cambiar de mapa, para checar si es bomba o regalo, convertí las coordenadas a posición de un arrglo, para solucionar el problema de los decimales, usé Math.Round.

    function detectarObjeto(){ posX = Math.round(playerPosition.x/elementSize)-1; posY = Math.round(playerPosition.y/elementSize)-1; if(mapCols[posY][posX]==='X'){ playerPosition.i=0; } else if(mapCols[posY][posX]==='I'){ if(indexMap< (maps.length-1)){ indexMap++; } } }

    Aquí dejo el resto del programa, donde incluso reinicio de niviel con un index al playerPosition, que se resetea cada que encuentra bomba.

    Dejo todo el programa porque hice varios cambios

    const canvas= document.querySelector('#game'); const game = canvas.getContext('2d'); const btnUp = document.querySelector('#up'); const btnLeft = document.querySelector('#left'); const btnRight = document.querySelector('#right'); const btnDown = document.querySelector('#down'); let canvasSize; let elementSize; let indexMap = 0; let map; let mapRows; let mapCols; let posX; let posY; const playerPosition={ x: undefined, y: undefined, i: 0, }; window.addEventListener('load', setCanvasSize); window.addEventListener('resize',setCanvasSize); function setCanvasSize(){ if (window.innerHeight > window.innerWidth){ canvasSize = window.innerWidth * 0.80; } else{ canvasSize = window.innerHeight * 0.80; } canvas.setAttribute('width', canvasSize); canvas.setAttribute('height', canvasSize); elementSize = canvasSize/10; startGame(); } function startGame(){ game.font = elementSize-10 + 'px Verdana'; game.textAlign = 'end'; convertMap(); game.clearRect(0,0,canvasSize,canvasSize); mapCols.forEach((rows,rowsI) => { rows.forEach((item, itemI)=>{ if(item == 'O' && playerPosition.i==0){ playerPosition.x = elementSize*(itemI+1); playerPosition.y = elementSize*(rowsI+1); playerPosition.i = 1; //console.log(elementSize*(itemI+1), elementSize*(rowsI+1)); } game.fillText(emojis[item], elementSize*(itemI+1), elementSize*(rowsI+1)); }); }); movePlayer(); /* for(let i=1; i<=10;i++){ for(let j=1; j<=10; j++){ game.fillText(emojis[mapCols[i-1][j-1]], elementSize*j, elementSize*i); } } */ } function convertMap(){ map = maps[indexMap]; mapRows = map.trim().split('\n'); mapCols = mapRows.map(row=>row.trim().split('')); } function movePlayer(){ game.fillText(emojis['PLAYER'], playerPosition.x, playerPosition.y); } window.addEventListener('keydown', moveByKeys); btnUp.addEventListener('click',moveUp); btnLeft.addEventListener('click',moveLeft); btnRight.addEventListener('click',moveRight); btnDown.addEventListener('click',moveDown); function moveByKeys(event){ if(event.key=='ArrowUp'){ moveUp(); } else if(event.key=='ArrowDown'){ moveDown(); } else if(event.key=='ArrowRight'){ moveRight(); } if(event.key=='ArrowLeft'){ moveLeft(); } } function moveUp(){ if(playerPosition.y>elementSize){ playerPosition.y-=elementSize; detectarObjeto(); } setCanvasSize(); } function moveLeft(){ if(playerPosition.x > elementSize+1){ playerPosition.x -= elementSize; detectarObjeto(); } setCanvasSize(); } function moveRight(){ if(playerPosition.x<elementSize*10){ playerPosition.x+=elementSize; detectarObjeto(); } setCanvasSize(); } function moveDown(){ if(playerPosition.y < elementSize*10){ playerPosition.y+=elementSize; detectarObjeto(); } setCanvasSize(); } function detectarObjeto(){ posX = Math.round(playerPosition.x/elementSize)-1; posY = Math.round(playerPosition.y/elementSize)-1; if(mapCols[posY][posX]==='X'){ playerPosition.i=0; } else if(mapCols[posY][posX]==='I'){ if(indexMap< (maps.length-1)){ indexMap++; } } }
    David Fernando Estacio Quiroz

    David Fernando Estacio Quiroz

    student•
    hace 3 años

    Yo decidí hacer algo diferente, en lugar de guardar la posición del jugador en pixeles guardé la posición por filas y columnas:

    if(!playerPosition.X && key == 'O'){ playerPosition.X = i; playerPosition.Y = j; }

    Uso esta funcion para renderizar al jugador:

    function renderPlayer() { console.log(playerPosition) game.fillText( emojis['PLAYER'] , Math.floor(elementSize * playerPosition.X+1), Math.floor(elementSize * playerPosition.Y+1) ); }

    Y luego, para detectar las colisiones, uso un condicional en cada movimiento que invoca a una función:

    function isColliding(newRow, newColumn) { switch (columns[newRow][newColumn]) { case 'O': console.log('Returned to the start'); (nivel-1 < 0) ? nivel=nivel : nivel-=1; break; case 'X': console.log('Collided'); return true; case 'I': console.log('Finished level'); (nivel+1 == maps.length) ? nivel=nivel : nivel+=1; break; default: break; } return false }
    David Ochoa

    David Ochoa

    student•
    hace 3 años

    Para evitar el problema de que las posiciones no coincidan, lo solucione almacenando los indices de cada bomba y del regalo en lugar de su valor "x" y "y"

    if (col == "I") { giftIndex = { ix: colI, iy: rowI, }; } if (col == "X") { bombIndexes.push({ ix: colI, iy: rowI }); }

    De igual manera mi objeto de player position almacena el indice en el que se encuentra

    if (col == "O") { if (!playerPosition.x && !playerPosition.y) { playerPosition = { x: posX, y: posY, ix: colI, iy: rowI, }; } }

    Y finalmente se realiza la comparación con los índices

    function checkCollision() { const giftCollitionX = giftIndex.ix == playerPosition.ix; const giftCollitionY = giftIndex.iy == playerPosition.iy; const isGift = giftCollitionX && giftCollitionY; if (isGift) { console.log("Ganaste!"); return; } const isBomb = bombIndexes.find((bomb) => { const collitionX = bomb.ix == playerPosition.ix; const collitionY = bomb.iy == playerPosition.iy; return collitionX && collitionY; }); if (isBomb) { console.log("Colision"); return; } }

    Espero haber ayudado a alguien 😉

    Ivo Zayas

    Ivo Zayas

    student•
    hace 3 años

    Hola amiguitos!!! comparto mi solución que creo que es más práctica:

    if (col == 'O') { if (!playerPosition.x && !playerPosition.y){ playerPosition.x = posX playerPosition.y = posY } } else if(col == 'I'){ checkGiftCollision(posX, posY) } else if(col == 'X'){ checkBombCollision(posX, posY) }

    Cuando estoy fijándome dentro del forEach() si el indice actual es una bomba o un regalo, llamo a las funciones 'chackBombCollision()' o 'checkGiftCollision()' dependiedno el caso. Estas reciben como parámetro la posición actual de 'col' (posX y posY).

    function checkGiftCollision(giftX, giftY){ if (giftY.toFixed(3) == playerPosition.y.toFixed(3) && giftX.toFixed(3) == playerPosition.x.toFixed(3)) { console.log('Felicidades'); } }

    function checkBombCollision(bombX, bombY) { if (bombY.toFixed(3) == playerPosition.y.toFixed(3) && bombX.toFixed(3) == playerPosition.x.toFixed(3)) { console.log('Perdiste'); } }

    Entonces comparo 'playerPosition.x' y 'playerPosition.y' con 'giftX' y 'giftY' o 'bombX' y 'bombY' y listo el pollo pelada la gallina!!! Por ahora creo que funciona, no se si se va a complicar más adelante. Aparte puedo prescindir de los objetos 'giftPosition' y 'enemyPositions' que necesitaba el profe para su solución.

    Cristian Zapata

    Cristian Zapata

    student•
    hace 3 años

    No es que no le salga el error, es que la consola no saca el error sino que no se ejecuta el console.log

    Andres Eduardo Maneiro Antunez

    Andres Eduardo Maneiro Antunez

    student•
    hace 3 años

    mi solucion es que no cree ningun array, me aparecio mas simple realizar condicionales cuando se renderiza el mapa

    la logica es mas simple, cuando se realiza un movimiento del jugador llama la function de render, y en el renderizado compara tan la "I" que es el regalo y la "X" que es el obstaciulo, llama a una funcion de vicoria o de derrota

    function renderMap() { game.font = `${elementsSize}px Verdana`; game.textAlign = 'end'; const map = maps[level]; game.clearRect(0, 0, canvasSize, canvasSize); const mapRows = map.trim().split('\n'); //trim() es un metodo que remueve los espacios en blanco de los treing y lo devuelve en un nuevo `string` sin modificar el original, el .split, elimina el caracter que se le indique en el argumento const mapRowsCols = mapRows.map(row => row.trim().split('')); game.clearRect(0, 0, canvasSize, canvasSize); mapRowsCols.forEach((row, rowIndex) => { row.forEach((col, colIndex) => { const emoji = emojis[col]; const posX = (elementsSize * (colIndex + 1))+12; const posY = elementsSize * (rowIndex + 1); if (col == 'O') { playerPosition.initialX = posX; playerPosition.initialY = posY; }; if (col == 'I') { if(playerPosition.x == posX && playerPosition.y == posY) { win(); return } } if (col == 'X') { if(playerPosition.x == posX && playerPosition.y == posY) { lose() return } } game.fillText(emoji, posX, posY); }); }); return

Escuelas

  • Desarrollo Web
    • Fundamentos del Desarrollo Web Profesional
    • Diseño y Desarrollo Frontend
    • Desarrollo Frontend con JavaScript
    • Desarrollo Frontend con Vue.js
    • Desarrollo Frontend con Angular
    • Desarrollo Frontend con React.js
    • Desarrollo Backend con Node.js
    • Desarrollo Backend con Python
    • Desarrollo Backend con Java
    • Desarrollo Backend con PHP
    • Desarrollo Backend con Ruby
    • Bases de Datos para Web
    • Seguridad Web & API
    • Testing Automatizado y QA para Web
    • Arquitecturas Web Modernas y Escalabilidad
    • DevOps y Cloud para Desarrolladores Web
  • English Academy
    • Inglés Básico A1
    • Inglés Básico A2
    • Inglés Intermedio B1
    • Inglés Intermedio Alto B2
    • Inglés Avanzado C1
    • Inglés para Propósitos Específicos
    • Inglés de Negocios
  • Marketing Digital
    • Fundamentos de Marketing Digital
    • Marketing de Contenidos y Redacción Persuasiva
    • SEO y Posicionamiento Web
    • Social Media Marketing y Community Management
    • Publicidad Digital y Paid Media
    • Analítica Digital y Optimización (CRO)
    • Estrategia de Marketing y Growth
    • Marketing de Marca y Comunicación Estratégica
    • Marketing para E-commerce
    • Marketing B2B
    • Inteligencia Artificial Aplicada al Marketing
    • Automatización del Marketing
    • Marca Personal y Marketing Freelance
    • Ventas y Experiencia del Cliente
    • Creación de Contenido para Redes Sociales
  • Inteligencia Artificial y Data Science
    • Fundamentos de Data Science y AI
    • Análisis y Visualización de Datos
    • Machine Learning y Deep Learning
    • Data Engineer
    • Inteligencia Artificial para la Productividad
    • Desarrollo de Aplicaciones con IA
    • AI Software Engineer
  • Ciberseguridad
    • Fundamentos de Ciberseguridad
    • Hacking Ético y Pentesting (Red Team)
    • Análisis de Malware e Ingeniería Forense
    • Seguridad Defensiva y Cumplimiento (Blue Team)
    • Ciberseguridad Estratégica
  • Liderazgo y Habilidades Blandas
    • Fundamentos de Habilidades Profesionales
    • Liderazgo y Gestión de Equipos
    • Comunicación Avanzada y Oratoria
    • Negociación y Resolución de Conflictos
    • Inteligencia Emocional y Autogestión
    • Productividad y Herramientas Digitales
    • Gestión de Proyectos y Metodologías Ágiles
    • Desarrollo de Carrera y Marca Personal
    • Diversidad, Inclusión y Entorno Laboral Saludable
    • Filosofía y Estrategia para Líderes
  • Diseño de Producto y UX
    • Fundamentos de Diseño UX/UI
    • Investigación de Usuarios (UX Research)
    • Arquitectura de Información y Usabilidad
    • Diseño de Interfaces y Prototipado (UI Design)
    • Sistemas de Diseño y DesignOps
    • Redacción UX (UX Writing)
    • Creatividad e Innovación en Diseño
    • Diseño Accesible e Inclusivo
    • Diseño Asistido por Inteligencia Artificial
    • Gestión de Producto y Liderazgo en Diseño
    • Diseño de Interacciones Emergentes (VUI/VR)
    • Desarrollo Web para Diseñadores
    • Diseño y Prototipado No-Code
  • Contenido Audiovisual
    • Fundamentos de Producción Audiovisual
    • Producción de Video para Plataformas Digitales
    • Producción de Audio y Podcast
    • Fotografía y Diseño Gráfico para Contenido Digital
    • Motion Graphics y Animación
    • Contenido Interactivo y Realidad Aumentada
    • Estrategia, Marketing y Monetización de Contenidos
  • Desarrollo Móvil
    • Fundamentos de Desarrollo Móvil
    • Desarrollo Nativo Android con Kotlin
    • Desarrollo Nativo iOS con Swift
    • Desarrollo Multiplataforma con React Native
    • Desarrollo Multiplataforma con Flutter
    • Arquitectura y Patrones de Diseño Móvil
    • Integración de APIs y Persistencia Móvil
    • Testing y Despliegue en Móvil
    • Diseño UX/UI para Móviles
  • Diseño Gráfico y Arte Digital
    • Fundamentos del Diseño Gráfico y Digital
    • Diseño de Identidad Visual y Branding
    • Ilustración Digital y Arte Conceptual
    • Diseño Editorial y de Empaques
    • Motion Graphics y Animación 3D
    • Diseño Gráfico Asistido por Inteligencia Artificial
    • Creatividad e Innovación en Diseño
  • Programación
    • Fundamentos de Programación e Ingeniería de Software
    • Herramientas de IA para el trabajo
    • Matemáticas para Programación
    • Programación con Python
    • Programación con JavaScript
    • Programación con TypeScript
    • Programación Orientada a Objetos con Java
    • Desarrollo con C# y .NET
    • Programación con PHP
    • Programación con Go y Rust
    • Programación Móvil con Swift y Kotlin
    • Programación con C y C++
    • Administración Básica de Servidores Linux
  • Negocios
    • Fundamentos de Negocios y Emprendimiento
    • Estrategia y Crecimiento Empresarial
    • Finanzas Personales y Corporativas
    • Inversión en Mercados Financieros
    • Ventas, CRM y Experiencia del Cliente
    • Operaciones, Logística y E-commerce
    • Gestión de Proyectos y Metodologías Ágiles
    • Aspectos Legales y Cumplimiento
    • Habilidades Directivas y Crecimiento Profesional
    • Diversidad e Inclusión en el Entorno Laboral
    • Herramientas Digitales y Automatización para Negocios
  • Blockchain y Web3
    • Fundamentos de Blockchain y Web3
    • Desarrollo de Smart Contracts y dApps
    • Finanzas Descentralizadas (DeFi)
    • NFTs y Economía de Creadores
    • Seguridad Blockchain
    • Ecosistemas Blockchain Alternativos (No-EVM)
    • Producto, Marketing y Legal en Web3
  • Recursos Humanos
    • Fundamentos y Cultura Organizacional en RRHH
    • Atracción y Selección de Talento
    • Cultura y Employee Experience
    • Gestión y Desarrollo de Talento
    • Desarrollo y Evaluación de Liderazgo
    • Diversidad, Equidad e Inclusión
    • AI y Automatización en Recursos Humanos
    • Tecnología y Automatización en RRHH
  • Finanzas e Inversiones
    • Fundamentos de Finanzas Personales y Corporativas
    • Análisis y Valoración Financiera
    • Inversión y Mercados de Capitales
    • Finanzas Descentralizadas (DeFi) y Criptoactivos
    • Finanzas y Estrategia para Startups
    • Inteligencia Artificial Aplicada a Finanzas
    • Domina Excel
    • Financial Analyst
    • Conseguir trabajo en Finanzas e Inversiones
  • Startups
    • Fundamentos y Validación de Ideas
    • Estrategia de Negocio y Product-Market Fit
    • Desarrollo de Producto y Operaciones Lean
    • Finanzas, Legal y Fundraising
    • Marketing, Ventas y Growth para Startups
    • Cultura, Talento y Liderazgo
    • Finanzas y Operaciones en Ecommerce
    • Startups Web3 y Blockchain
    • Startups con Impacto Social
    • Expansión y Ecosistema Startup
  • Cloud Computing y DevOps
    • Fundamentos de Cloud y DevOps
    • Administración de Servidores Linux
    • Contenerización y Orquestación
    • Infraestructura como Código (IaC) y CI/CD
    • Amazon Web Services
    • Microsoft Azure
    • Serverless y Observabilidad
    • Certificaciones Cloud (Preparación)
    • Plataforma Cloud GCP

Platzi y comunidad

  • Platzi Business
  • Live Classes
  • Lanzamientos
  • Executive Program
  • Trabaja con nosotros
  • Podcast

Recursos

  • Manual de Marca

Soporte

  • Preguntas Frecuentes
  • Contáctanos

Legal

  • Términos y Condiciones
  • Privacidad
  • Tyc promociones
Reconocimientos
Reconocimientos
Logo reconocimientoTop 40 Mejores EdTech del mundo · 2024
Logo reconocimientoPrimera Startup Latina admitida en YC · 2014
Logo reconocimientoPrimera Startup EdTech · 2018
Logo reconocimientoCEO Ganador Medalla por la Educación T4 & HP · 2024
Logo reconocimientoCEO Mejor Emprendedor del año · 2024
De LATAM conpara el mundo
YoutubeInstagramLinkedInTikTokFacebookX (Twitter)Threads