Introducción
Contenido
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 breakout
en uno de sus espacios de trabajo.
Cree los siguientes archivos y carpetas en su directorio:
- Un
index.html
archivo - Un
breakout.js
archivo - Una carpeta llamada
assets
- Dentro de su
assets
carpeta, cree unaimages
carpeta
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.html
archivo:
<!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
game
elemento div que contendrá nuestro clon Breakout - Carga Phaser v3.17 a través de su CDN
- Carga nuestro
breakout.js
archivo 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.js
archivo para configurar nuestro mundo de juego.
Inicio de Phaser
Primero, necesitamos configurar Phaser y crear nuestra Game
instancia. 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 Game
instancia:
// 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 type
propiedad 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 parent
propiedad indica el ID del elemento HTML en el que se jugará nuestro juego. Definimos las dimensiones de nuestro juego en píxeles con width
y height
. El scale
objeto 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 padrediv
autoCenter
le dice a Phaser cómo centrar nuestro juego dentro de nuestro padrediv
si 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 padrediv
, 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 this
en 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.
Te puede interesar:Very best Paypal Gambling houses 2022Agregar 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 X
Coordenada para colocar el sprite. El segundo argumento es la Y
Coordenada, 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 x
aumenta, esencialmente nos movemos hacia la derecha. A medida que y
aumenta, 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 repeat
propiedad le dice a Phaser cuántas veces más crear un objeto. Cada grupo de sprites crea un sprite. Con el repeat
valor 9, Phaser creará 10 sprites en ese grupo de sprites. El setXY
objeto tiene tres propiedades interesantes:
x
es la coordenada X del primer spritey
es la coordenada Y del segundo spritestepX
es la longitud en píxeles entre los sprites repetidos en el eje x.
También hay una stepY
propiedad, 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.js
adició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.js
archivo, 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.
Te puede interesar:Hundreds of thousands Holed Up In House As Computer virus Profoundly Changes Us all LifeActualicemos 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 cursors
variable.
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 cursors
objeto para actualizar la posición de nuestro jugador. En la else
clá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 else
clá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 x
y y
de 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 x
haciendo 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 gameStarted
bandera a true
para 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.
Te puede interesar:Colorado Imagination 5 Lottery GameManejo de colisiones
Colisión mundial
Todas nuestras interacciones de sprites se definen en la create
función. Habilitar la colisión con la escena mundial es muy fácil con Phaser, agregue lo siguiente al final de la create
funció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 bounce
propiedad del sprite de la bola. La bounce
propiedad 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 create
funció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 ball
colisiona 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.js
definición a hitBrick
continuació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, ball
y 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 immovable
propiedad de nuestros grupos de sprites en true
.
Actualizar las definiciones de violetBricks
, yellowBricks
y redBricks
a 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 config
objeto de juego , dentro de la arcade
propiedad 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.js
con 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 gameStarted
declaració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 text
mé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 if
instrucció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!
Te puede interesar: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í .