Introducci贸n a Phaser 3: Construcci贸n de un Breakout

    Introducci贸n

    El desarrollo de juegos es una rama 煤nica del desarrollo de software que puede ser tan gratificante como complejo. Cuando pensamos en crear juegos, solemos pensar en una aplicaci贸n para instalar y jugar en nuestros ordenadores o consolas.

    La especificaci贸n HTML5 introdujo muchas API para permitir el desarrollo de juegos en la web, permitiendo que nuestros juegos lleguen a muchos usuarios en diferentes dispositivos inform谩ticos. Phaser es un marco de juego popular que nos permite crear r谩pidamente juegos para la web.

    La mejor forma de dominar el desarrollo de juegos es crear juegos. Usaremos Phaser para crear un clon de Breakout , una versi贸n del cl谩sico y eterno juego de Atari lanzado en 1976.

    Este tutorial contiene HTML y CSS muy b谩sicos. Deber谩 sentirse c贸modo con las funciones y los objetos de JavaScript. Tambi茅n hace un uso ligero de las funciones de ES2015.

    El bucle del juego

    Todos los juegos se ejecutan dentro de un bucle. Despu茅s de configurar nuestro mundo de juego, ingresamos al bucle del juego que realiza las siguientes tareas:

    • Entrada de procesos
    • Actualiza el mundo del juego
    • Renderiza los cambios

    Veamos c贸mo funciona el bucle del juego en un juego como Megaman. Despu茅s de examinar el men煤 para comenzar un nivel, el juego decide d贸nde colocar las plataformas y carga la m煤sica para reproducir. Esa configuraci贸n suele ocurrir durante la pantalla de carga.

    Cuando comienza el juego, tienes el control de Megaman en un mundo con plataformas, enemigos y una canci贸n particular para ese nivel. Puedes usar tu joystick para mover a Megaman y presionar un bot贸n para saltar o disparar. El bucle del juego est谩 procesando la entrada, actualizando la posici贸n de Megaman y renderizando esos cambios muchas veces en un segundo.

    驴Qu茅 es Phaser?

    Phaser es un marco de juego HTML5. Utiliza muchas API HTML5 como Canvas, WebGL, Audio, Gamepad, etc. y agrega una l贸gica 煤til como administrar el bucle del juego y proporcionarnos motores de f铆sica.

    Con Phaser, podemos crear juegos 2D con nada m谩s que HTML, CSS y JavaScript.

    Reglas de ruptura

    Antes de usar Phaser para construir nuestro clon Breakout, primero definamos el alcance del juego:

    • Este juego para un jugador tiene un nivel con 30 ladrillos, una paleta y una pelota.
    • El objetivo es conseguir que la bola destruya todos los ladrillos, asegur谩ndose de que no salga de la parte inferior de la pantalla del juego.
    • El jugador controlar谩 una paleta, que puede moverse hacia la izquierda y hacia la derecha.
    • El juego est谩 dise帽ado para usuarios web de escritorio, por lo que el teclado se usar谩 para ingresar

    Configuraci贸n de Phaser

    Phaser es una biblioteca de JavaScript, para desarrollar y jugar a nuestro juego necesitaremos algo de HTML b谩sico para cargar el JS. Cree un directorio llamado breakouten uno de sus espacios de trabajo.

    Cree los siguientes archivos y carpetas en su directorio:

    • Un index.htmlarchivo
    • Un breakout.jsarchivo
    • Una carpeta llamada assets
    • Dentro de su assetscarpeta, cree una imagescarpeta

    Los activos del juego son arte, sonido, video y otros datos utilizados por el juego. Para este simple clon de Breakout, no hay muchos activos que necesiten organizarse con carpetas. Sin embargo, es una buena pr谩ctica mantener sus activos separados de su c贸digo y separarlos por su tipo.

    Agrega el siguiente c贸digo a tu index.htmlarchivo:

    <!doctype html>
    <html>
    
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
      <title>Breakout</title>
      <style>
        html,
        body {
          margin: 0 auto;
          padding: 0;
          width: 100%;
          height: 100%;
        }
    
        #game {
          margin: 10px auto;
          padding: 0;
          width: 800px;
          height: 640px;
        }
      </style>
    </head>
    
    <body>
      <noscript>You need to enable JavaScript to run this app.</noscript>
      <div id="game"></div>
      <script src="//cdn.jsdelivr.net/npm/[email聽protected]/dist/phaser.min.js"></script>
      <script src="breakout.js"></script>
    </body>
    
    </html>
    

    Este c贸digo HTML b谩sico hace lo siguiente:

    • Elimina los m谩rgenes y el relleno del navegador de HTML y la etiqueta del cuerpo
    • Agrega un gameelemento div que contendr谩 nuestro clon Breakout
    • Carga Phaser v3.17 a trav茅s de su CDN
    • Carga nuestro breakout.jsarchivo que actualmente no hace nada pero contendr谩 nuestra l贸gica de juego

    Para desarrollar juegos de forma eficaz con Phaser, necesitaremos que estos archivos sean servidos por un servidor web. Sin un servidor web, nuestro navegador no permitir谩 que el script del juego cargue nuestros activos por razones de seguridad.

    Afortunadamente, no es necesario configurar Apache o Nginx para obtener un servidor web en ejecuci贸n. Si usa VisualStudio Code como yo, puede instalar la extensi贸n Live Server . La mayor铆a de los IDE y editores de texto tienen un complemento con una funcionalidad similar.

    Si tiene instalada la versi贸n 3 de Python, puede ir a su espacio de trabajo a trav茅s de la terminal e ingresar python3 -m http.server. Hay otras herramientas CLI que proporcionan servidores web sencillos, elige la que te d茅 el tiempo m谩s r谩pido para desarrollar tu juego.

    Por 煤ltimo, descargue los recursos de imagen que hemos creado para este juego desde este enlace . Copie y pegue los archivos PNG en la carpeta de im谩genes.

    Consejo de desarrollo : cuando est茅 desarrollando un juego, probablemente desee tener la consola JavaScript visible para que pueda ver los errores que aparecen. Si est谩 utilizando Chrome o Firefox, haga clic con el bot贸n derecho en la p谩gina y seleccione “Inspeccionar elemento”. Deber铆a aparecer un cuadro en la parte inferior o lateral de la ventana del navegador. Seleccione la pesta帽a “Consola” para ver los errores de actualizaci贸n o los registros de nuestro c贸digo JavaScript.

    Creando nuestro mundo de juegos

    Con nuestra configuraci贸n HTML y CSS, editemos nuestro breakout.jsarchivo para configurar nuestro mundo de juego.

    Inicio de Phaser

    Primero, necesitamos configurar Phaser y crear nuestra Gameinstancia. La instancia de Juego es el controlador central para un juego de Phaser, hace toda la configuraci贸n e inicia el ciclo del juego por nosotros.

    Configuremos y creemos nuestra Gameinstancia:

    // This object contains all the Phaser configurations to load our game
    const config = {
      type: Phaser.AUTO,
      parent: 'game',
      width: 800,
      heigth: 640,
      scale: {
        mode: Phaser.Scale.RESIZE,
        autoCenter: Phaser.Scale.CENTER_BOTH
      },
      scene: {
        preload,
        create,
        update,
      },
      physics: {
        default: 'arcade',
        arcade: {
          gravity: false
        },
      }
    };
    
    // Create the game instance
    const game = new Phaser.Game(config);
    

    La typepropiedad le dice a Phaser qu茅 renderizador usar. Phaser puede renderizar nuestro juego usando el elemento WebGL o Canvas de HTML5 . Al establecer el tipo en Phaser.AUTO, le estamos diciendo a Phaser que primero intente renderizar con WebGL y, si eso falla, renderice usando Canvas.

    La parentpropiedad indica el ID del elemento HTML en el que se jugar谩 nuestro juego. Definimos las dimensiones de nuestro juego en p铆xeles con widthy height. El scaleobjeto hace dos cosas por nosotros:

    • mode le dice a Phaser c贸mo usar el espacio de nuestro elemento padre, en este caso, nos aseguramos de que el juego se ajuste al tama帽o del padre div
    • autoCenterle dice a Phaser c贸mo centrar nuestro juego dentro de nuestro padre divsi queremos. En este caso, centramos nuestro juego vertical y horizontalmente dentro del div padre. Esta propiedad es m谩s 煤til cuando el juego no ocupa todo el espacio del padre div, se muestra aqu铆 como una pregunta frecuente.

    En Phaser, nuestra l贸gica de juego se define en Scenes. Piense en las escenas como varios estados en nuestro juego: la pantalla de t铆tulo es una escena, cada nivel de un juego ser铆a su propia escena, una escena de corte ser铆a su propia escena, etc. Phaser proporciona un objeto Escena pero tambi茅n puede funcionar con un objeto JavaScript normal que contiene las funciones preload(), create()y update()definidas.

    La 煤ltima configuraci贸n le dice a Phaser qu茅 motor de f铆sica usar. Phaser puede usar 3 motores f铆sicos diferentes: Arcade , Impact y Matter . Arcade es el m谩s simple para comenzar y es suficiente para nuestras necesidades de juego.

    Breakout no necesita gravedad para funcionar, as铆 que desactivamos la propiedad en nuestro motor de f铆sica. Si estuvi茅ramos construyendo un juego de plataformas, probablemente habilitar铆amos la gravedad, de modo que cuando nuestros jugadores salten caigan naturalmente al suelo.

    Para asegurarnos de que la configuraci贸n de nuestro juego funcione, debemos agregar las funciones preload(), create()y update(). Agregue las siguientes funciones en blanco despu茅s de crear nuestra instancia de juego:

    function preload() { }
    
    function create() { }
    
    function update() { }
    

    Con su servidor web en ejecuci贸n, navegue hasta la p谩gina donde se est谩 ejecutando su juego. Deber铆a ver una pantalla en blanco como esta:

    Cargando activos

    Los activos de este juego consisten en 5 im谩genes. En otros juegos que puedas crear, tus activos pueden ser enormes. Los archivos de im谩genes, audio y video de alta definici贸n pueden ocupar megabytes de espacio. Cuanto mayor sea el activo, m谩s tiempo llevar谩 la carga. Por esa raz贸n, Phaser tiene una preload()funci贸n en la que podemos cargar todos los activos antes de comenzar a ejecutar el juego. Nunca es una experiencia agradable para el usuario jugar un juego y de repente se ralentiza porque est谩 tratando de cargar nuevos activos.

    Cambie la preload()funci贸n a la siguiente para que podamos cargar nuestras im谩genes antes de que comience el ciclo del juego:

    function preload() {
      this.load.image('ball', 'assets/images/ball_32_32.png');
      this.load.image('paddle', 'assets/images/paddle_128_32.png');
      this.load.image('brick1', 'assets/images/brick1_64_32.png');
      this.load.image('brick2', 'assets/images/brick2_64_32.png');
      this.load.image('brick3', 'assets/images/brick3_64_32.png');
    }
    

    El primer argumento es la clave que usaremos m谩s adelante para hacer referencia a la imagen, el segundo argumento es la ubicaci贸n de la imagen.

    Nota: – cuando usamos thisen nuestros preload(), create()y update()funciones, nos estamos refiriendo a la escena de ejecuci贸n por la instancia de juego que fue creado anteriormente.

    Con las im谩genes cargadas, queremos colocar sprites en la pantalla. En la parte superior de breakout.js, agregue estas variables que contendr谩n nuestros datos de sprites:

    let player, ball, violetBricks, yellowBricks, redBricks, cursors;
    

    Una vez que se definen globalmente, todas nuestras funciones pueden utilizarlas.

    Agregar Sprites

    Un objeto es cualquier imagen 2D que forma parte de la escena de un juego. En Phaser, un sprite encapsula una imagen junto con su posici贸n, velocidad, propiedades f铆sicas y otras propiedades. Comencemos creando nuestro sprite de jugador a trav茅s de la create()funci贸n:

    player = this.physics.add.sprite(
      400, // x position
      600, // y position
      'paddle', // key of image for the sprite
    );
    

    Ahora deber铆a poder ver una paleta en la pantalla:

    El primer argumento del sprite()m茅todo es la XCoordenada para colocar el sprite. El segundo argumento es la YCoordenada, y el 煤ltimo argumento es clave para el activo de imagen agregado en la preload()funci贸n.

    Es importante comprender c贸mo Phaser y la mayor铆a de los marcos de juegos 2D usan coordenadas. Los gr谩ficos que aprendimos en la escuela generalmente colocan el origen, es decir, el punto (0, 0) en el centro. En Phaser, el origen est谩 en la parte superior izquierda de la pantalla. A medida que xaumenta, esencialmente nos movemos hacia la derecha. A medida que yaumenta, nos movemos hacia abajo.

    Nuestro juego tiene un ancho de 800 p铆xeles y una altura de 640 p铆xeles, por lo que las coordenadas de nuestro juego se ver铆an as铆:

    Agreguemos la pelota para que se siente encima del jugador. Agregue el siguiente c贸digo a la create()funci贸n:

    ball = this.physics.add.sprite(
      400, // x position
      565, // y position
      'ball' // key of image for the sprite
    );
    

    Como la bola est谩 por encima de nuestro jugador, el valor de la Coordenada Y es menor que la Coordenada Y del jugador.

    Agregar grupos de sprites

    Si bien Phaser facilita la adici贸n de sprites, r谩pidamente se volver铆a tedioso si cada sprite tuviera que definirse individualmente. Los ladrillos de Breakout son pr谩cticamente id茅nticos. Las posiciones son diferentes, pero sus propiedades, como el color y c贸mo interact煤an con la pelota, son las mismas. En lugar de crear 30 objetos de sprites de ladrillo, podemos crear grupos de sprites para administrarlos mejor.

    Agreguemos la primera fila de ladrillos violetas a trav茅s de la create()funci贸n:

    // Add violet bricks
    violetBricks = this.physics.add.group({
      key: 'brick1',
      repeat: 9,
      setXY: {
        x: 80,
        y: 140,
        stepX: 70
      }
    });
    

    En lugar de this.physics.add.sprite()usar this.physics.add.group()y pasar un objeto JavaScript. La propiedad de clave hace referencia a la clave de imagen que usar谩n todos los sprites del grupo de sprites. La repeatpropiedad le dice a Phaser cu谩ntas veces m谩s crear un objeto. Cada grupo de sprites crea un sprite. Con el repeatvalor 9, Phaser crear谩 10 sprites en ese grupo de sprites. El setXYobjeto tiene tres propiedades interesantes:

    • x es la coordenada X del primer sprite
    • y es la coordenada Y del segundo sprite
    • stepX es la longitud en p铆xeles entre los sprites repetidos en el eje x.

    Tambi茅n hay una stepYpropiedad, pero no es necesario que la usemos para este juego. Agreguemos los otros dos grupos de sprites restantes para ladrillos:

    // Add yellow bricks
    yellowBricks = this.physics.add.group({
      key: 'brick2',
      repeat: 9,
      setXY: {
        x: 80,
        y: 90,
        stepX: 70
      }
    });
    
    // Add red bricks
    redBricks = this.physics.add.group({
      key: 'brick3',
      repeat: 9,
      setXY: {
        x: 80,
        y: 40,
        stepX: 70
      }
    });
    

    Nuestro juego ya se est谩 juntando, tu pantalla deber铆a verse as铆:

    Ganar y perder

    Es una buena pr谩ctica de desarrollo (y programaci贸n) de juegos tener el final a la vista. En Breakout, podemos perder un juego si nuestra bola cae al fondo de la pantalla. En Phaser, para que la pelota est茅 debajo de la pantalla, la Coordenada Y de la pelota es mayor que la altura del mundo del juego. Creemos una funci贸n que verifique esto, agreguemos al final de la breakout.jsadici贸n lo siguiente:

    function isGameOver(world) {
      return ball.body.y > world.bounds.height;
    }
    

    Nuestra funci贸n toma el objeto de mundo de la propiedad f铆sica de la escena, que estar谩 disponible en la update()funci贸n. Comprueba si la coordenada Y del objeto de la bola es mayor que la altura de los l铆mites del mundo del juego.

    Para ganar el juego tenemos que deshacernos de todos los ladrillos. Todos los Sprites en Phaser tienen una propiedad activa. Podemos usar esa propiedad para determinar si ganamos o no. Los grupos de sprites pueden contar la cantidad de sprites activos que contienen. Si no hay sprites activos en cada uno de los grupos de sprites de ladrillo, es decir, hay 0 sprites de ladrillo activos, entonces el jugador gan贸 el juego.

    Actualicemos el breakout.jsarchivo, agregando una marca en la parte inferior:

    function isWon() {
      return violetBricks.countActive() + yellowBricks.countActive() + redBricks.countActive() === 0;
    }
    

    Aceptamos cada uno de los grupos de sprites como par谩metros, sumamos el n煤mero de sprites activos dentro de ellos y verificamos si es igual a 0.

    Ahora que hemos definido nuestras condiciones para ganar y perder, queremos que Phaser las compruebe al principio del ciclo del juego. Tan pronto como el jugador gane o pierda, el juego deber铆a detenerse.

    Actualicemos la update()funci贸n:

    function update() {
      // Check if the ball left the scene i.e. game over
      if (isGameOver(this.physics.world)) {
        // TODO: Show "Game over" message to the player
      } else if (isWon()) {
        // TODO: Show "You won!" message to the player
      } else {
        // TODO: Logic for regular game time
      }
    }
    

    Mover el reproductor con entrada de teclado

    El movimiento del jugador depende de la entrada del teclado. Para poder rastrear la entrada del teclado. Es hora de usar la cursorsvariable.

    Y al final de nuestra create()funci贸n:

    cursors = this.input.keyboard.createCursorKeys();
    

    Las teclas del cursor en Phaser rastrean el uso de 6 teclas del teclado: arriba, derecha, abajo, izquierda, shift y espacio.

    Ahora necesitamos reaccionar al estado de nuestro cursorsobjeto para actualizar la posici贸n de nuestro jugador. En la elsecl谩usula de nuestra update()funci贸n agregue lo siguiente:

    // Put this in so that the player stays still if no key is being pressed
    player.body.setVelocityX(0);
    
    if (cursors.left.isDown) {
      player.body.setVelocityX(-350);
    } else if (cursors.right.isDown) {
      player.body.setVelocityX(350);
    }
    

    隆Ahora podemos mover nuestro reproductor de izquierda a derecha!

    Notar铆as que el sprite del jugador puede salir de la pantalla del juego, idealmente, no deber铆a hacerlo. Nos ocuparemos de eso m谩s adelante cuando manejemos las colisiones.

    Esperando para empezar

    Antes de agregar l贸gica para mover la pelota, ser铆a 煤til que el juego esperara la entrada del usuario antes de moverse. No es una buena experiencia cargar un juego y verse forzado a comenzar de inmediato. 隆El jugador no tendr铆a tiempo suficiente para reaccionar!

    Muevamos la pelota hacia arriba despu茅s de que el jugador presione la barra espaciadora. Si el usuario mueve la paleta hacia la izquierda o hacia la derecha, la bola tambi茅n se mover谩, por lo que siempre estar谩 en el centro de la paleta.

    Primero, necesitamos nuestra propia variable para rastrear si un juego se inici贸 o no. En la parte superior de breakout.js, despu茅s de la declaraci贸n de nuestras variables de juego, agreguemos:

    let gameStarted = false;
    

    Ahora, en la elsecl谩usula de nuestra funci贸n de actualizaci贸n:

    if (!gameStarted) {
      ball.setX(player.x);
    
      if (cursors.space.isDown) {
        gameStarted = true;
        ball.setVelocityY(-200);
      }
    }
    

    Si el juego no ha comenzado, establece la coordenada X de nuestra bola en el centro del jugador. Las coordenadas de un objeto de juego se basan en su centro, por lo que las propiedades xy yde los sprites relacionan el punto con el centro de nuestros sprites.

    Tenga en cuenta que, si bien est谩 bien obtener el valor de una propiedad xhaciendo referencia a ella directamente al configurar las propiedades, siempre intentamos usar la funci贸n de establecimiento adecuada. Las funciones del setter pueden incluir l贸gica para validar nuestra entrada, actualizar otra propiedad o disparar un evento. Hace que nuestro c贸digo sea m谩s predecible.

    Como antes al mover el reproductor, comprobamos si se presion贸 la barra espaciadora. Si se presion贸, cambiamos la gameStartedbandera a truepara que la pelota ya no siguiera la posici贸n horizontal del jugador, y establecemos la velocidad Y de la pelota en -200. Las velocidades y negativas env铆an objetos hacia arriba. Para velocidades positivas, los valores m谩s grandes mueven los objetos hacia abajo m谩s r谩pido. Para velocidades negativas, los valores m谩s peque帽os mueven los objetos hacia arriba m谩s r谩pido.

    Ahora cuando movemos al jugador, la pelota sigue y si presionamos la barra espaciadora la pelota se dispara hacia arriba:

    Observar铆a algunas cosas de c贸mo se comporta nuestro juego hasta ahora:

    • La bola est谩 detr谩s de los ladrillos.
    • El jugador puede salir de los l铆mites de la pantalla.
    • La pelota puede salir de los l铆mites de la pantalla.

    La bola se representa detr谩s de los ladrillos porque se agreg贸 al juego en nuestra funci贸n de creaci贸n antes de los grupos de sprites de ladrillos. En Phaser, y generalmente con el elemento de lienzo HTML5, la imagen agregada m谩s recientemente se dibuja sobre las im谩genes anteriores.

    Los dos 煤ltimos problemas se pueden resolver agregando alguna colisi贸n mundial.

    Manejo de colisiones

    Colisi贸n mundial

    Todas nuestras interacciones de sprites se definen en la createfunci贸n. Habilitar la colisi贸n con la escena mundial es muy f谩cil con Phaser, agregue lo siguiente al final de la createfunci贸n:

    player.setCollideWorldBounds(true);
    ball.setCollideWorldBounds(true);
    

    Deber铆a darnos un resultado como este:

    Si bien el movimiento del jugador est谩 bien, la pelota parece atascada en la parte superior. Para rectificar esto, necesitamos establecer la bouncepropiedad del sprite de la bola. La bouncepropiedad le dir铆a a Phaser cu谩nta velocidad mantener despu茅s de chocar con un objeto.

    Agregue esto al final de su create()funci贸n:

    ball.setBounce(1, 1);
    

    Esto le dice al phaser que la bola debe mantener toda su velocidad X e Y. Si soltamos la pelota con la barra espaciadora, la pelota deber铆a estar rebotando arriba y abajo del mundo del juego. Necesitamos desactivar la detecci贸n de colisiones desde la parte inferior del mundo del juego.

    Si no lo hacemos, nunca sabremos cu谩ndo termina el juego. Deshabilite la colisi贸n con la parte inferior del mundo del juego agregando esta l铆nea al final de la createfunci贸n:

    this.physics.world.checkCollision.down = false;
    

    Ahora deber铆amos tener un juego como este:

    Colisi贸n de ladrillos

    Ahora que nuestros sprites en movimiento chocan correctamente con nuestro mundo de juego, trabajemos en la colisi贸n entre la pelota y los ladrillos y luego la pelota y el jugador.

    En nuestra create()funci贸n agregue las siguientes l铆neas de c贸digo al final:

    this.physics.add.collider(ball, violetBricks, hitBrick, null, this);
    this.physics.add.collider(ball, yellowBricks, hitBrick, null, this);
    this.physics.add.collider(ball, redBricks, hitBrick, null, this);
    

    El m茅todo colisionador le dice al sistema f铆sico de Phaser que ejecute la hitBrick()funci贸n cuando ballcolisiona con varios grupos de sprites de ladrillos.

    Cada vez que presionamos la barra espaciadora, la bola se dispara hacia arriba. No hay velocidad X, por lo que la pelota volver铆a directamente a la paleta. Ser铆a un juego aburrido. Por lo tanto, cuando golpeamos un ladrillo por primera vez, estableceremos una velocidad X aleatoria.

    En la parte inferior de la breakout.jsdefinici贸n a hitBrickcontinuaci贸n:

    function hitBrick(ball, brick) {
      brick.disableBody(true, true);
    
      if (ball.body.velocity.x === 0) {
        randNum = Math.random();
        if (randNum >= 0.5) {
          ball.body.setVelocityX(150);
        } else {
          ball.body.setVelocityX(-150);
        }
      }
    }
    

    La hitBrick()funci贸n acepta los dos argumentos anteriores que se usaron en el collider()m茅todo, por ejemplo, bally violetBricks. La disableBody(true, true)llamada en el ladrillo le dice a Phaser que lo ponga inactivo y lo oculte de la pantalla. Si la velocidad X de la pelota es 0, le damos a la pelota una velocidad que depende del valor de un n煤mero aleatorio.

    Si una pelota peque帽a rueda hacia su pie a un ritmo lento, al chocar se detendr铆a. El motor Arcade Physics modela el impacto que tiene la colisi贸n en la velocidad de forma predeterminada. Para nuestro juego, no queremos que la pelota pierda velocidad cuando golpea un ladrillo. Necesitamos establecer la immovablepropiedad de nuestros grupos de sprites en true.

    Actualizar las definiciones de violetBricks, yellowBricksy redBricksa lo siguiente:

    // Add violet bricks
    violetBricks = this.physics.add.group({
      key: 'brick1',
      repeat: 9,
      immovable: true,
      setXY: {
        x: 80,
        y: 140,
        stepX: 70
      }
    });
    
    // Add yellow bricks
    yellowBricks = this.physics.add.group({
      key: 'brick2',
      repeat: 9,
      immovable: true,
      setXY: {
        x: 80,
        y: 90,
        stepX: 70
      }
    });
    
    // Add red bricks
    redBricks = this.physics.add.group({
      key: 'brick3',
      repeat: 9,
      immovable: true,
      setXY: {
        x: 80,
        y: 40,
        stepX: 70
      }
    });
    

    Nuestra colisi贸n de ladrillos ahora est谩 completa y nuestro juego deber铆a funcionar as铆:

    Consejo de desarrollo : cuando desarrolle la f铆sica de su juego, es posible que desee habilitar el modo de depuraci贸n para ver los cuadros de l铆mite de sus sprites y c贸mo chocan entre s铆. En su configobjeto de juego , dentro de la arcadepropiedad donde definimos gravity, puede habilitar la depuraci贸n agregando esto al objeto:

    debug: true
    

    Colisi贸n de jugador

    Manejar las colisiones entre la pelota y el jugador es un esfuerzo similar. Primero, asegur茅monos de que el jugador sea immovable. Al final de la create()funci贸n agregue lo siguiente:

    player.setImmovable(true);
    

    Y luego agregamos un colisionador entre la pelota y el jugador:

    this.physics.add.collider(ball, player, hitPlayer, null, this);
    

    Cuando la pelota golpea al jugador, queremos que sucedan dos cosas:

    • La pelota debe moverse un poco m谩s r谩pido, para aumentar gradualmente la dificultad del juego.
    • La direcci贸n horizontal de la pelota depende de qu茅 lado del jugador golpe贸: si la pelota golpea el lado izquierdo del jugador, debe ir hacia la izquierda, si golpea el lado derecho del jugador, debe ir hacia la derecha.

    Para adaptarse a estos, actualice breakout.jscon la hitPlayer()funci贸n:

    function hitPlayer(ball, player) {
      // Increase the velocity of the ball after it bounces
      ball.setVelocityY(ball.body.velocity.y - 5);
    
      let newXVelocity = Math.abs(ball.body.velocity.x) + 5;
      // If the ball is to the left of the player, ensure the X-velocity is negative
      if (ball.x < player.x) {
        ball.setVelocityX(-newXVelocity);
      } else {
        ball.setVelocityX(newXVelocity);
      }
    }
    

    Nota: Un sprite puede chocar con otro sprite, un sprite puede chocar con un grupo de sprite y los grupos de sprite pueden chocar entre s铆. Phaser es lo suficientemente inteligente como para usar la funci贸n de colisi贸n que definimos como apropiada en el contexto.

    Ahora nuestro juego tiene una colisi贸n entre jugadores y ladrillos:

    Agregar texto

    Si bien nuestro juego es completamente funcional, alguien que juegue a este juego no tendr铆a idea de c贸mo comenzar o saber cu谩ndo termina el juego.

    Agreguemos 3 nuevas variables globales que almacenar谩n nuestros datos de texto despu茅s de la gameStarteddeclaraci贸n en la parte superior de breakout.js:

    let openingText, gameOverText, playerWonText;
    

    Texto de apertura

    Agreguemos algo de texto cuando el juego est茅 cargado para decirle al jugador que presione la barra espaciadora. En la create()funci贸n agregue el siguiente c贸digo:

    openingText = this.add.text(
      this.physics.world.bounds.width / 2,
      this.physics.world.bounds.height / 2,
      'Press SPACE to Start',
      {
        fontFamily: 'Monaco, Courier, monospace',
        fontSize: '50px',
        fill: '#fff'
      }
    );
    
    openingText.setOrigin(0.5);
    

    Los dos primeros argumentos del textm茅todo son las coordenadas X e Y del cuadro de texto. Usamos el ancho y la altura de la escena del juego para determinar d贸nde se coloca: en el centro. El tercer argumento es el texto a mostrar. El cuarto argumento es un objeto JS que contiene datos relacionados con la fuente.

    A diferencia de los objetos de texto, las coordenadas X e Y de los objetos de texto se refieren a su punto superior izquierdo del objeto, no a su centro. Por eso usamos el setOrigin()m茅todo para hacer que el sistema de coordenadas funcione como sprites, en este caso facilita su posicionamiento en el centro.

    Cuando estamos jugando, ya no queremos ver el texto de apertura. En la update()funci贸n, cambie la ifinstrucci贸n que comprueba si la barra espaciadora se presion贸 a lo siguiente:

    if (cursors.space.isDown) {
      gameStarted = true;
      ball.setVelocityY(-200);
      openingText.setVisible(false);
    }
    

    Los objetos de texto no son sprites, no podemos desactivar sus cuerpos. Podemos hacerlos invisibles cuando no necesitamos verlos. Nuestro juego ahora comienza as铆:

    Juego terminado y juego ganado

    Como antes, necesitamos agregar los objetos de texto en la create()funci贸n y hacerlos invisibles para que no se vean cuando se inicie el juego:

    // Create game over text
    gameOverText = this.add.text(
      this.physics.world.bounds.width / 2,
      this.physics.world.bounds.height / 2,
      'Game Over',
      {
        fontFamily: 'Monaco, Courier, monospace',
        fontSize: '50px',
        fill: '#fff'
      }
    );
    
    gameOverText.setOrigin(0.5);
    
    // Make it invisible until the player loses
    gameOverText.setVisible(false);
    
    // Create the game won text
    playerWonText = this.add.text(
      this.physics.world.bounds.width / 2,
      this.physics.world.bounds.height / 2,
      'You won!',
      {
        fontFamily: 'Monaco, Courier, monospace',
        fontSize: '50px',
        fill: '#fff'
      }
    );
    
    playerWonText.setOrigin(0.5);
    
    // Make it invisible until the player wins
    playerWonText.setVisible(false);
    

    Ahora que est谩n definidos, tenemos que cambiar su visibilidad en la update()funci贸n:

    // Check if the ball left the scene i.e. game over
    if (isGameOver(this.physics.world)) {
      gameOverText.setVisible(true);
      ball.disableBody(true, true);
    } else if (isWon()) {
      playerWonText.setVisible(true);
      ball.disableBody(true, true);
    } else {
      ...
    }
    

    Deshabilitamos el cuerpo de la bola para que deje de actualizarse y mostrarse, ya que ya no es necesaria.

    Si perdemos el juego, veremos esto:

    Si ganamos el juego, veremos esto:

    隆Nuestro clon Breakout est谩 completo!

    Conclusi贸n

    Phaser es un marco de desarrollo de juegos HTML5 que nos permite crear r谩pidamente videojuegos en la web. Adem谩s de abstraer las API de HTML5, tambi茅n nos proporciona utilidades 煤tiles como motores de f铆sica y gestiona el ciclo del juego: el ciclo de vida de ejecuci贸n de todos los juegos.

    La mejor manera de mejorar sus habilidades de desarrollo de juegos es seguir creando juegos. Si desea obtener m谩s informaci贸n sobre el desarrollo de juegos con Phaser, consulte el tutorial introductorio del sitio web oficial .

    Puedes ver el c贸digo fuente anotado del juego aqu铆 .

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *