Haz juegos retro animados con JavaScript | Frank Dvorak | Skillshare

Velocidad de reproducción


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Haz juegos retro animados con JavaScript

teacher avatar Frank Dvorak, Creative Coding

Ve esta clase y miles más

Obtenga acceso ilimitado a todas las clases
Clases enseñadas por líderes de la industria y profesionales activos
Los temas incluyen ilustración, diseño, fotografía y más

Ve esta clase y miles más

Obtenga acceso ilimitado a todas las clases
Clases enseñadas por líderes de la industria y profesionales activos
Los temas incluyen ilustración, diseño, fotografía y más

Lecciones en esta clase

    • 1.

      Introducción al proyecto 1

      1:02

    • 2.

      Funciones de Project 1

      1:27

    • 3.

      Configuración de proyectos

      4:24

    • 4.

      Objetos de juego y jugador

      5:36

    • 5.

      Controles de teclado

      7:26

    • 6.

      Grupo de objetos

      13:01

    • 7.

      Olas de enemigo

      13:33

    • 8.

      Detección de colisiones

      8:05

    • 9.

      Partitura y texto de estado

      12:48

    • 10.

      Método de reinicio

      5:46

    • 11.

      Clase de enemigos de beetlemorph

      4:50

    • 12.

      Explicación de animación de sprite

      10:35

    • 13.

      Temporización de animación

      10:15

    • 14.

      Animación del jugador

      13:08

    • 15.

      Características adicionales: Enemigos con armadura

      0:57

    • 16.

      Clase de enemigos de rinomorfo

      9:24

    • 17.

      Funciones adicionales: Batallas de jefes

      0:30

    • 18.

      Clase de jefe

      9:27

    • 19.

      Movimiento de jefes

      10:22

    • 20.

      Colisión de jefe contra jugador

      7:07

    • 21.

      Características adicionales: Súper armas

      0:43

    • 22.

      2 clases de láser

      8:01

    • 23.

      Daño con láser

      8:01

    • 24.

      Gestión de recursos

      7:05

    • 25.

      Clase de enemigos Eaglemorph

      6:55

    • 26.

      Proyectiles enemigos

      6:21

    • 27.

      Interacciones de proyectiles

      4:48

    • 28.

      Squidmorph - Clase de enemigos

      9:18

    • 29.

      Clase de lobstermorph

      2:30

    • 30.

      Proyecto 2: JavaScript Planet Defense

      3:18

    • 31.

      Proyecto 2: Planeta y clase de juego

      8:10

    • 32.

      Proyecto 2: Posición del ratón

      6:18

    • 33.

      Proyecto 2: Nave espacial del jugador

      4:42

    • 34.

      Proyecto 2: Un poco de matemáticas

      7:47

    • 35.

      Proyecto 2: Entiende la rotación de lienzo

      6:15

    • 36.

      Proyecto 2: Modo de depuración

      2:25

    • 37.

      Proyecto 2: Piscina de objetos

      12:22

    • 38.

      Proyecto 2: Proyectiles de jugadores

      6:10

    • 39.

      Proyecto 2: Piscina de enemigos

      7:25

    • 40.

      Proyecto 2: Detección de colisión

      2:20

    • 41.

      Proyecto 2: Eventos periódicos

      8:06

    • 42.

      Proyecto 2: Asteroide enemigo

      4:21

    • 43.

      Proyecto 2: Animación de sprites

      9:15

    • 44.

      Proyecto 2: Enemigo de morfología de langosta

      9:15

    • 45.

      Proyecto 2: Texto de juego

      5:26

    • 46.

      Proyecto 2: Vidas de jugadores

      5:09

    • 47.

      Proyecto 2: Beetlemorph Enemy

      1:36

    • 48.

      Proyecto 2: Rhinomorph Enemy

      2:43

    • 49.

      Proyecto 3: Juego para móviles con JavaScript

      1:08

    • 50.

      Configuración de Project 3

      2:02

    • 51.

      Haz que todo sea receptivo

      6:15

    • 52.

      Clase de enemigos

      7:58

    • 53.

      Patrón de diseño de piscina de objetos

      3:35

    • 54.

      Activadores periódicos

      6:50

    • 55.

      Controles de ratón

      1:31

    • 56.

      Detección de colisiones

      7:50

    • 57.

      Eventos táctiles

      1:37

    • 58.

      Texto del juego

      7:04

    • 59.

      Comienza a reiniciar

      3:34

    • 60.

      Juegos a pantalla completa

      4:11

    • 61.

      Miembros de la tripulación simples

      1:30

    • 62.

      Tipo de enemigo simple

      4:23

    • 63.

      Animación de sprite

      4:20

    • 64.

      Tiempo de animación

      4:08

    • 65.

      Modo de depuración

      2:09

    • 66.

      Variedad de enemigos

      4:29

    • 67.

      Tripulación espacial aleatoria

      3:47

    • 68.

      Gestión del estado en los juegos

      4:27

    • 69.

      Patrón de diseño de estado

      15:23

    • 70.

      Sonidos

      9:49

  • --
  • Nivel principiante
  • Nivel intermedio
  • Nivel avanzado
  • Todos los niveles

Generado por la comunidad

El nivel se determina según la opinión de la mayoría de los estudiantes que han dejado reseñas en esta clase. La recomendación del profesor o de la profesora se muestra hasta que se recopilen al menos 5 reseñas de estudiantes.

103

Estudiantes

--

Proyectos

Acerca de esta clase

Lo viejo es oro. Inspirémonos en los juegos de clásico de los 80 y agreguemos nuestros propios gráficos y características de juego. En el primer proyecto exploraremos el vacío interestelar y usaremos 3 tipos de armas diferentes para eliminar enjambres de bichos espaciales de diferentes tipos, algunos más grandes que otros.

Comenzaremos con un juego inspirado en el arcade clásico de Space Invaders y experimentaremos con diferentes características adicionales, como batallas contra jefes, súper armas y diferentes tipos de enemigos. Exploremos la programación orientada a objetos con JavaScript e

implementemos un conjunto útil de técnicas básicas de desarrollo de juegos 2D, como el patrón de diseño de grupo de objetos, animación de sprites, técnicas de sincronización y escalonamiento usando marcas de tiempo y mucho más.

No olvides descargar todos los regalos de bonificación. Los estudiantes de este curso recibirán un paquete de arte de juego 2D de calidad premium, esta vez en un tema espacial de ciencia ficción. También puedes descargar el código fuente de múltiples etapas del proyecto, a medida que agregamos más funciones gradualmente.

Implementaremos múltiples tipos de enemigos:

Beetlemorph: enemigo básico, 1 golpe es suficiente

Rhinomorph: enemigo blindado, múltiples vidas, múltiples estados de daño

Mantismorph: enemigo del tamaño de un jefe massive, aumentando el número de vidas

Eaglemorph: cuando es golpeado, sacrificará un segmento de cuerpo y lo escupirá

Squidmorph - bicho inflable de alienígena, puede absorber nuestras armas

Lobstermorph - ADN inestable, se divide en varios clones más pequeños cuando se golpea

¡Diviértete!

Conoce a tu profesor(a)

Teacher Profile Image

Frank Dvorak

Creative Coding

Profesor(a)

Hello, I'm Frank. I'm a front-end web developer, owner of Frank's Laboratory YouTube channel. Come explore creative coding, game development and generative art with me.

Ver perfil completo

Level: Intermediate

Valoración de la clase

¿Se cumplieron las expectativas?
    ¡Superadas!
  • 0%
  • 0%
  • Un poco
  • 0%
  • No realmente
  • 0%

¿Por qué unirse a Skillshare?

Mira las galardonadas Skillshare Originals

Cada clase tiene lecciones cortas y proyectos prácticos

Tu membresía apoya a los profesores de Skillshare

Aprende desde cualquier lugar

Ve clases sobre la marcha con la aplicación de Skillshare. Progresa en línea o descarga las clases para verlas en el avión, el metro o donde sea que aprendas mejor.

Transcripciones

1. Introducción: Viajemos en el tiempo a la edad de oro de los videojuegos arcade. En esta clase, usaremos HTML, CSS y Javascript para construir un juego inspirado en los clásicos, pero le daremos nuestro propio estilo artístico y características personalizadas. Controlaremos una nave espacial que tiene que abrirse camino a través oleadas cada vez más grandes de enemigos con un busto ocasional en el medio A medida que avanzamos más en lo Desconocido, desbloqueamos más armas y nos encontramos con especies exóticas aún más peligrosas. La clase no es para principiantes completos, pero si ya entiendes los conceptos básicos como variables, matrices y para bucles, ven a explorar desarrollo de juegos y la animación de guiones de chat conmigo. Los alumnos de esta clase obtendrán una tonelada de arte de dos juegos de calidad premium completamente gratis. puedes usarlo para tus propios proyectos personales más tarde y construir tus propios juegos con él. También puedes descargar el código fuente completo de múltiples etapas del proyecto a medida que lo expandimos y agregamos más y más funciones. 2. Características del proyecto: Primero implementaremos tres tipos de armas, un ataque básico, un láser de largo alcance y un súper láser. Lo vamos a necesitar porque tendremos que abrirnos camino a través de cinco tipos diferentes de enemigos. Al principio nos encontramos con solo un espacio regular atrás. Un golpe de nuestra arma básica es suficiente. A medida que exploramos más a fondo, nos encontramos con enemigos blindados. Tenemos que pegarles cinco veces. Podremos ver el daño gradual a su grueso exoesqueleto tras Si no tenemos cuidado, podríamos encontrar un enemigo de tamaño masivo con un peligroso conjunto de garras Cada vez que aparezca uno nuevo, tendrá más vidas que la anterior antes de aumentar lentamente el chal***ge para A medida que avanza el juego, algunas especies exóticas están en vías de venganza. Mira éste. Cada vez que lo golpeamos, sacrifica un segmento corporal y nos lo arroja como un proyectil mutado hecho de hielo, baba También tendremos que luchar contra un enemigo que absorbe nuestros proyectiles e infla hasta que Con esa, te daré una versión de tamaño regular y ambos tamaños de las hojas extendidas para que tu juego sea aún más diverso y divertido de explorar. Vamos a codificar todo desde cero sin frameworks y sin bibliotecas como de costumbre. Espero que te diviertas en esta aventura espacial Java Script. Vamos. 3. Configuración del proyecto: En el índice HTML, creamos una página web sencilla en blanco. Le doy algún título. Enlazo hoja de estilo CSS, creo HTML cinco elemento canvas con un ID de canvas uno. Y vinculo script el archivo GS en estilo CSS. Apunto mi elemento lienzo y le doy un borde. Quiero centrarlo en medio de la página web vertical y horizontalmente. Una forma de hacerlo es usar estas cuatro líneas de CSS. Podemos usar transform translate, o también podemos usar simplemente la propiedad translate de esta manera. Tenga cuidado con la sintaxis, no hay coma entre los valores Todo lo demás en esta clase estará escrito en Javascript simple vainilla. Sin marcos y sin bibliotecas. En la programación moderna, podemos estructurar nuestro código de muchas maneras diferentes. Hay algunos paradigmas de programación comunes y establecidos desde algo muy sencillo, como la programación procedimental, hasta la programación funcional y orientada a objetos La programación orientada a objetos es probablemente el paradigma más popular. El concepto central es que organizamos nuestros datos en objetos. Por lo general, se basa en gran medida en clases que nos dan una forma de crear muchos objetos similares basados en un blueprint compartido En la clase de hoy, mantendremos nuestro código limpio y organizado convirtiendo todo en un objeto. Y haremos que esos objetos hablen entre sí. Por ejemplo, cuando un objeto proyectil golpea un objeto enemigo, se eliminará el objeto enemigo Uno de los principios principales de programación orientada a objetos es la encapsulación, lo que significa que tenemos datos y métodos que operan sobre esos datos en paquetes separados en objetos También podemos restringir el acceso a esos datos desde fuera del bundle us cierres, campos privados y así sucesivamente. Encapsularemos nuestros datos en cuatro clases separadas que se comunican y trabajan juntas para crear el juego final clase de jugador manejará el movimiento y la animación del personaje principal del jugador, la nave espacial robótica Esta clase sólo tendrá una instancia. Las clases se utilizan como planos para crear muchos objetos similares En este caso, el jugador no tiene que ser una clase, solo podemos declararlo como un objeto simple. Lo convertiré en una clase para mantener la estructura de código igual a través de la base de código. Quiero que esto sea fácil de leer para principiantes. clase de proyectiles manejará los láseres que el jugador va a disparar Aprenderemos a convertir esto en un grupo de objetos reutilizables para mejorar masivamente el rendimiento Porque crear y eliminar miles de objetos proyectiles desperdiciaría mucha memoria La clase enemiga dibujará y animará a los invasores del espacio. Aprenderemos a organizarlos en cuadrículas y cómo hacerlos venir en olas cada vez más grandes A medida que avanza el juego, la clase Game contendrá la lógica principal de la base de código. Este es el cerebro principal que envía comandos a todas partes y mantiene todo esto unido. Estaremos usando arte de juego profesional pulido que te estoy dando completamente gratis. Y necesitamos asegurarnos de que Javascript se ejecute solo después de que todos nuestros activos artísticos estén cargados y disponibles. Solo comenzaré y configuraré mi proyecto después el evento load on window object se dispare cuando se haya cargado toda la página, incluidos todos los recursos dependientes como hojas de estilo, scripts, imágenes, etc. Dentro de la función callback en load event listener, podemos configurar nuestro elemento canvas Como de costumbre, utilizo cuatro líneas simples de código. Primero, apunto el script Java hacia mi elemento canvas usando su ID. Guardo esa referencia en una variable, CTX context canvas variable dot get context y lo paso a la Esto creará una instancia de construido en la API de lienzo, dándonos acceso a todos los métodos de dibujo y propiedades del lienzo, como el estilo de relleno o el ancho de línea. Porque queremos asegurarnos de que los dibujos de lienzo no estén distorsionados. No estamos configurando el tamaño del lienzo con CSS porque eso solo establecería el tamaño del elemento si configuro el ancho y alto del lienzo así. Con Javascript, estamos estableciendo tanto el tamaño del elemento como el tamaño de la superficie de dibujo en los mismos valores. Html canvas tiene dos tamaños, necesitan ser establecidos el mismo valor para evitar distorsiones. 4. Objetos de juegos y jugadores: El objeto del juego deberá ser consciente del tamaño del lienzo. Entonces constructor esperará una referencia apuntando hacia el elemento canvas como argumento. En su interior lo convertimos en una propiedad de clase. Tome el lienzo pasado como argumento y conviértalo a esta propiedad de lienzo de puntos en esta instancia de clase de juego. A partir de ahí, podemos extraer el ancho del lienzo de 600 píxeles y el alto del lienzo de 800 píxeles. Esto es muy importante. Y estos valores serán necesarios en toda nuestra base de código. Porque por ejemplo, el jugador y el proyectil necesitan estar atentos si están dentro o fuera del área visible del lienzo El objeto de juego tendrá un método personalizado. Yo llamo por ejemplo, render. Este método se ejecutará 60 veces por segundo, dibujando y actualizando todo. Primero, pongamos ahí un consolo. Creo una instancia de clase de juego usando la nueva palabra clave en la línea 14. Puedo ver que espera lienzo como argumento. Lo paso lienzo de la línea 25. Si consolo esta nueva variable de juego, puedo ver una instancia de mi clase de juego Si la abro, puedo ver estas propiedades perfectas. También puedo llamar a ese método de renderizado personalizado que escribimos en la línea 19. Y ahora puedo ver que es correctamente consolo en ancho de 600 pixeles y alto de 800 pixeles Nuestro juego está listo y funcionando. Quiero dibujar y animar El constructor de la clase jugador jugador esperará una referencia al objeto principal del juego de la línea 13 como argumento. Debido a que necesitamos acceso a ancho y alto y muchas otras cosas que estarán sentadas en la clase principal del juego, necesito acceder a todas estas propiedades desde dentro de la clase de jugador. Ten en cuenta que al hacer esto, no estoy creando una copia del objeto principal del juego. Solo estoy apuntando al espacio en la memoria donde se asienta el objeto del juego desde dentro de la clase de jugador. Estoy guardando esa referencia ya este ancho de propiedad de juego de puntos del jugador será de 100 píxeles. La altura también será de 100 píxeles. Por ahora, la coordenada x horizontal será 200 y la coordenada Y vertical también será de 200 píxeles. Vamos a darle un método de dibujo personalizado. Se esperará una referencia al contexto para especificar en qué lienzo queremos dibujar. Lo hago así para mantener cada clase lo más autónoma posible dentro de ella tomaremos ese contexto. Llamamos rectángulo de relleno construido y le damos x, y, ancho y alto. La posición y el tamaño actuales del jugador. Estructuraré mi código de una manera donde todo se asiente en el objeto principal del juego para poder dibujar y actualizar todo desde allí. Dentro del constructor de clase de juego, creo una propiedad de jugador dist personalizada y la configuro igual a una instancia de clase de jugador. Usando la nueva palabra clave, jugador espera una referencia al objeto principal del juego. Pasé esta palabra clave porque aquí estamos dentro de esa clase de juego. Ahora que creamos el jugador, podemos dibujarlo llamando al método draw desde la línea nueve. Este jugador de la línea 27 empate. Puedo ver que el método de dibujo espera contexto y voy a estar llamando a este método de rectángulo de Phil desde contexto. También sé que el método del rectángulo Phil está sentado en mi variable CT X aquí abajo. Paso CTX a render, render esperará ese valor como argumento y lo guardará como una variable llamada context Lo pasaré a lo largo de Player Draw como este método ya espera contexto. Y así es como se puede llamar a este rectángulo de relleno. Aquí en la línea diez, bonito, estamos dibujando un cuadrado negro que representa al jugador. Puedo cambiar las coordenadas x e y para mover al jugador alrededor. ¿Y si quiero que el jugador esté en el medio horizontalmente? Este juego ancho de punto veces 0.5 Entonces necesitamos compensar ese valor por la mitad del ancho del jugador. Ahora está perfectamente centrado verticalmente. Quiero que el jugador se siente en la parte inferior del área de juego. La altura del juego menos la altura del jugador. Perfecto. Tenemos un objeto de juego de trabajo y un objeto de jugador. Quiero que el jugador se mueva a la izquierda y a la derecha. Al presionar las teclas de flecha en el teclado, le doy al jugador esta propiedad de velocidad de punto. Quiero que se mueva, por ejemplo, cinco píxeles por fotograma de animación. Le doy un método de actualización que actualizará posición horizontal del jugador por su velocidad. Para ejecutar este código, tengo que llamarlo desde aquí abajo. Realmente no pasa nada porque este código se ejecuta solo una vez. Para crear algún movimiento, necesito dibujar al jugador actualizar su posición, dibujarlo de nuevo, actualizarlo de nuevo, y así sucesivamente. Vamos a crear bucle de animación. Función personalizada. Yo llamo animar. Puse el render de punto de juego dentro. Entonces llamamos request animation frame sits on the main window object and I pass it animate the name of its parent function to create a loop Si elimino el punto de ventana aquí, seguirá funcionando. Javascript sabe dónde encontrar este método. Llamamos animate. Dibujamos y actualizamos el juego, y disparamos animar de nuevo. Esto se repetirá una y otra vez. Solo necesito llamar a animate para activar el primer bucle así Vemos pintura vieja. Solo quiero ver el fotograma de animación actual. Entre cada bucle, limpiaré todo el lienzo desde la coordenada 002. Lona con altura de lona. Agradable, el jugador se mueve hacia la derecha, cinco píxeles por fotograma de animación. 5. Controles de teclado: Si configuro la velocidad en menos uno, el jugador se mueve hacia la izquierda un píxel por fotograma de animación. Si la velocidad es cero, no hay movimiento. Agreguemos controles de teclado. Todo el código dentro de un constructor de clase se ejecuta automáticamente en el punto en que creamos una instancia de la clase usando la palabra clave new. Normalmente, solo usamos el espacio para dar propiedades a nuestro objeto, pero en realidad podemos poner cualquier código allí que queramos ejecutar en este punto cuando se crea este objeto en particular. Por ejemplo, si hago este evento se aplicarán automáticamente los oyentes Cuando se crea una nueva instancia de objeto de juego, creo un oyente de eventos para el evento key down La función de devolución de llamada tiene acceso al objeto de evento autogenerado que contiene todas las diferentes propiedades e información sobre el evento clave abajo que acaba Para usarlo, solo tenemos que darle un nombre de variable aquí y Javascript sabrá qué hacer. Lo llamaré para evento dentro. Voy a consolo. Guardo mis cambios, selecciono el elemento Canvas, y presiono algo en mi teclado. incendios de eventos Keydown y objeto de evento autogenerado se muestran en la consola del navegador Si lo inspecciono, puedo ver todo tipo de información. Hoy en día, nos preocupamos solo por esta propiedad clave. Aquí mismo se puede ver presioné letra en mi teclado, en este caso si consolo tecla Ahora cuando presione teclas aleatorias, se mostrará el nombre de la tecla que se presionó. Toma nota de estos valores. Observe cómo deletrea las teclas de flecha, porque necesitaremos estos valores Ahora tenemos que asegurarnos usar exactamente la misma ortografía, incluyendo la carcasa inferior y superior. En mi objeto principal del juego, tendré una matriz llamada estas teclas de punto. Cuando se presiona una tecla en el evento key down, agregaré el nombre de la tecla que se acaba de presionar en esta matriz. Cuando se suelta la llave, la retiramos. Tomo este punto claves aquí y llamo método push. Voy a presionar tecla punto el nombre de la tecla que se presionó en la matriz. Lo guardo y lo pruebo. Cuando empiece a escribir en mi teclado, obtendré un error de consola. Es porque estamos colocando un oyente de eventos que se asienta en un objeto de ventana dentro de nuestro objeto de juego personalizado Aquí, estoy tratando de acceder a esta propiedad de teclas de punto que se encuentra en el objeto del juego. Pero esta función de devolución de llamada en realidad no recuerda en qué parte del código se declaró originalmente, no recuerda su alcance léxico Necesitamos vincular la palabra clave para que apunte hacia el objeto del juego desde dentro de esta función de devolución O alternativamente, podemos usar sintaxis de la función ES seis flechas. Las funciones de flecha heredan automáticamente esta palabra clave del ámbito padre Ahora bien, este punto usado aquí apunta correctamente al objeto del juego, y mi código funcionará. Los gabinetes de alcance son temas avanzados de programación. Si eres principiante, no tienes que entenderlo completamente en este punto. Tendrá más sentido eventualmente. Sólo sigue escribiendo código. Estoy contras registrando este dokey y podemos ver que crece cada vez más grande a medida que presiono más y más teclas También recargo y noto que si sigo presionando flecha hacia arriba, agregará una y otra vez Solo quiero agregar una entrada por cada clave. Si la flecha arriba ya está en la matriz, no la agregue de nuevo. En Java Script, index off método puede ser llamado en una matriz. Devuelve el primer índice en que se puede encontrar un elemento dado en la matriz. Lo importante a recordar es que devuelve menos uno si el elemento no está presente. Sabiendo eso, podemos decir si este índice de teclas de dokey es igual a menos uno, si la tecla que se presionó no está presente en esta matriz de teclas de punto Solo entonces presione la clave en la matriz. Ahora puedo presionar la flecha hacia abajo una y otra vez, y solo se agregará a la matriz una vez perfecta. También quiero eliminar la clave de la matriz cuando se suelta la clave. Cuando se dispara el evento key up, copio este bloque de código. Para hacerlo más limpio, creo una variable constante de ámbito de bloque , aquí llamada índice Y será índice de la clave que fue liberada dentro esta matriz de teclas de punto si index es más de menos uno. Es decir, que si la clave que se liberó está dentro de esta matriz de teclas de punto, que siempre debe ser. Por cierto, usaremos el método splice para eliminar esa entrada, esa clave, de esta matriz de teclas de punto. Construido en el método de empalme de script Java se puede utilizar para reemplazar o eliminar elementos existentes de una matriz. Si se encuentra clave en la matriz que llamo empalme, encuentro el índice de ese elemento en la matriz y lo elimino. El empalme se puede utilizar para eliminar múltiples elementos. Tenemos que pasarle uno aquí porque queremos eliminar solo ese elemento que encontramos ahí dentro. Bien, entonces presiono la tecla Enter en mi teclado. La entrada no está presente en esta matriz de teclas de punto. Lo empujamos ahí, libero Enter. Encontramos el índice de esa entrada en esta matriz de teclas de punto y lo eliminamos usando el método de empalme. Presiono flecha hacia arriba, Se agrega, lo libero. Se ha quitado. Escribo un poco más. Funciona bien. Genial. Es una buena práctica eliminar siempre el consolo cuando ya no los necesitamos Ahora en el objeto principal del juego, tenemos esta matriz de teclas de punto que realiza un seguimiento de las teclas que actualmente se están presionando hacia abajo. Tener acceso a todo en el objeto de juego desde dentro del objeto jugador debido a esta propiedad de juego de puntos aquí dentro del método de actualización en la clase de jugador. Puedo decir si este juego dokey índice de flecha izquierda es más de menos uno, es decir, que la flecha izquierda fue presionada, seguir produciendo posición horizontal del jugador por distorsionar la velocidad de la línea ocho, haciendo que se mueva hacia la Otra declaración if flecha a la derecha y decimos plus hizo velocidad haciendo que se mueva hacia la derecha. Ahora necesito establecer la velocidad de distorsiones a algo que no sea cero Aquí, por ejemplo, cinco píxeles por fotograma. Ahora puedo presionar las flechas izquierda y derecha, y el jugador se mueve a izquierda y derecha. La ventaja de esta estructura de código es que podemos aumentar fácilmente la velocidad del jugador. O podemos ralentizar al jugador cambiando esta propiedad de velocidad en la línea ocho. El problema es que el jugador puede moverse todo el camino fuera del área de juego. Voy a establecer la velocidad a diez píxeles por fotograma. Por ahora, aquí es donde manejamos el movimiento horizontal. Voy a introducir aquí algunos límites horizontales. Si esta x la posición horizontal del jugador, la esquina superior izquierda del rectángulo del jugador es menor que cero, x es cero, esto creará un límite izquierdo duro L. Si porque el jugador no puede estar tocando izquierda y derecha el área de juego en el mismo marco de animación, podemos usar L si aquí, si x es mayor que el ancho del juego menos el ancho del jugador, x es igual al ancho del juego menos el ancho del jugador. Esto creará un límite duro en el lado derecho. Lo pruebo, el jugador ya no puede moverse fuera del área de juego en ambos lados. Bonito. 6. Grupo de objetos: Ahora que tenemos controles de teclado, quiero que el jugador dispare láseres cuando presionemos algo La clase proyectil se encargará de eso. En un juego como este, dispararemos una gran cantidad de proyectiles Podríamos crear un nuevo objeto proyectil cada vez y descartarlo cuando choca con algo o cuando vuela fuera Pero como expliqué en la lección anterior, crear y descartar grandes cantidades de objetos crea problemas de rendimiento relacionados con la memoria En este juego, solo tendremos diez objetos proyectiles en total Y volveremos, usaremos esos mismos diez objetos y otra vez, mejorando masivamente el rendimiento El beneficio de rendimiento se hará especialmente evidente en partes más animadas del juego. Créeme, este juego puede ponerse muy ocupado y animado cuando empiezan a llegar oleadas de enemigos. Tenemos que asegurarnos de optimizar rendimiento de nuestros juegos. Lo lograremos usando el patrón de diseño de pool de objetos. Cada proyectil tendrá propiedades de ancho, alto, x, y y velocidad Debido a que queremos convertir esto en un miembro del grupo de objetos reutilizables, miembro del grupo de objetos reutilizables, también tenemos que darle esta propiedad libre de puntos. Cuando este punto libre es cierto, proyectil está sentado en la piscina listo para ser usado Actualmente no está jugando un papel activo en el juego. No es visible. Cuando este punto libre es falso. Significa que lo sacamos de la piscina y lo estamos usando. Ya no es gratis, ya no está disponible. El patrón de diseño del grupo de objetos es un patrón de diseño creacional Nos permite evitar problemas de rendimiento relacionados con la asignación automática de memoria y los procesos de recolección de basura que se activan cuando creamos y destruimos grandes cantidades de objetos Javascript. En cambio, crearemos un pequeño charco de diez objetos proyectiles Y los sacaremos de la piscina cuando los necesitemos. Cuando los proyectiles chocan con enemigos o cuando vuelan fuera de la pantalla, simplemente los devolvemos a la piscina Reiniciamos sus propiedades y las ponemos a disposición para ser sacadas nuevamente, son necesarias. Esto eliminará por completo la desaceleración causada por asignación de memoria cuando creamos objetos uno por uno También el proceso de recolección de basura que borra de memoria de manera automática y periódica los objetos que ya no son los objetos que ya no son referenciados en nuestro juego no se activará en absoluto porque no estaremos descartando esos Hay más para objetar el patrón de diseño de la piscina que esto, pero esta clase es para principiantes. hoy te mostraré cómo hacer una implementación muy sencilla pero poderosa en un proyecto de trabajo real. Si quieres leer más al respecto, te enlazaré algunos buenos artículos en la descripción a continuación. Le damos un método de dibujo, pero solo queremos dibujar proyectiles activos sólo si el proyectil actualmente no está libre Lo mismo ocurre con la actualización. Cualquier código dentro del método de actualización se ejecutará solo para los miembros activos del grupo de objetos. Sólo si libre es falso, exclamación significa falso si los proyectiles no están activos, si solo están sentados en la piscina esperando ser utilizados, los dibujaremos y no los actualizaremos Si objeto proyectil fue sacado de la piscina de objetos y no es libre Ahora mismo, dibujaremos un rectángulo que representa al proyectil, el método de actualización para todos los proyectiles activos Haremos que se muevan hacia arriba en dirección negativa sobre el eje y vertical. Quiero que se muevan relativamente rápido. Intentemos 20 píxeles por fotograma de animación y veamos cómo se ve eso. Cada miembro del grupo de objetos necesita tener dos métodos de ayuda. Un método que se ejecutará cuando el objeto se tome del grupo de objetos. Yo lo llamaré inicio. Otro método que se ejecutará cuando el objeto ya no sea necesario y se devuelva al conjunto de objetos disponibles. Cuando empezamos a usar el objeto que ponemos libre a cara, el objeto se está utilizando, ya no está disponible en el pool. El método Reset lo devolverá a la piscina estableciendo su propiedad gratuita en true. Lo que significa que debido a estas dos comprobaciones, el objeto dejará de ser dibujado y actualizado después de que se ejecute el método reset. Espero que tenga sentido dejar un comentario si lo entiendes o si necesitas una explicación más a fondo. Simplemente puedes dejar un comentario con un simple emoji de pulgar hacia arriba Si lo entendiste bien, sabré a qué te refieres y a todos demás se confundirán con ese comentario. Esta es la clase proyectil. Es un miembro del grupo de objetos y tiene todo lo que necesita para crear un patrón de diseño de grupo de objetos en nuestra base de código. Ahora solo necesitamos una forma de crear ese pool de objetos usando esta clase de proyectiles Lo haremos dentro del cerebro principal de nuestra base de código. Aquí en la clase de juego, le doy una propiedad llamada este pool Proyectiles, y lo configuré en una matriz vacía El número de proyectiles será de diez. Quiero usar solo un número muy pequeño aquí. También funcionará bien para el juego en este caso. Te voy a mostrar por qué quiero sólo diez proyectiles cuando tenemos alguna animación El patrón de diseño de la piscina de objetos necesita un método que llene la piscina con diez objetos proyectiles inactivos al principio De esta manera preasignamos la memoria para objetos antiguos de una sola vez, lo cual es bueno para el rendimiento. Es más rápido hacerlo de esta manera y crear todos los objetos de la piscina a la vez en lugar de hacer que el script Java busque espacio libre en la memoria para cada proyectil por separado, uno por uno como y cuando los necesitemos Esta es una forma muy básica de explicar cómo y por qué patrón de diseño de pool de objetos resuelve problemas de asignación de memoria reutilizándolos en lugar de eliminarlos, también evitamos completamente activar el proceso de recolección de basura Otro método personalizado de beneficio de rendimiento al que llamo crear Proyectiles Se ejecutará diez veces. Cada vez que corre, toma este tiro de proyectiles y empujará un nuevo objeto proyectil en su interior En la clase que escribimos en la línea 23, necesitamos un último método helper. Un método que nos permitirá sacar un objeto proyectil libre de la piscina cuando lo necesitemos Yo llamo al método, por ejemplo, conseguir proyectil dentro. Vamos a recorrer el pozo de proyectiles uno por uno. Tan pronto como encontramos un elemento con tres propiedades establecidas en true, devolvemos ese elemento y dejamos de buscar. La palabra clave return detendrá la ejecución de este bucle de cuatro. Quiero crear proyectiles automáticamente. Aquí, cuando se crea una instancia de la clase de juego, se crea pool Proyectiles y se llena automáticamente con diez objetos proyectiles inactivos y listos para ser utilizados Comprobemos si funcionó por consolo. En esta piscina de proyectiles, puedo ver que es una matriz con diez Solo abro uno y verifico para asegurarme de que no veo ningún valor indefinido o no que indicara un problema dentro de mi clase de proyectil. Todo está bien aquí. Proyectil tiene un método de inicio que se ejecutará cuando obtengamos el proyectil de la piscina Y queremos empezar a usarlo en el juego porque proyectil va a estar volando fuera del jugador El método de inicio esperará las coordenadas X e Y del jugador como argumento, fijará este punto y esta y del proyectil a las coordenadas x e y actuales del jugador Por último, para completar este gran bucle lógico, le damos al jugador un método personalizado llamado disparar Dentro creo una variable constante de alcance de bloque llamada proyectil Y será igual para obtener método de proyectil que acabamos de escribir en la clase de juego principal aquí en la línea 92 Buscará a través de la piscina de proyectiles si alguno de los diez proyectiles es gratuito y disponible, nos lo dará si los diez proyectiles se están utilizando actualmente en juego, no encontrará Entonces obtendríamos un error aquí. Sólo si encontramos el proyectil y no está indefinido, llamaremos al método start a ese proyectil inactivo en la piscina para activarlo Sabemos que el método de inicio espera la posición x e y del jugador. Lo paso punto x punto y. estamos pasando la posición actual x e y del jugador donde quiera que esté el jugador cuando se ejecute el método start, start method lo espera. Establece las coordenadas del jugador x y el jugador Y las coordenadas x e y del proyectil Y lo activa estableciendo su propiedad libre en caídas, lo que significa que el código dentro de los métodos de sorteo y actualización pasará esta comprobación. Empezará a correr dibujando y actualizando el proyectil ¿Cómo activamos este método de disparo personalizado que acabamos de escribir? Podría por ejemplo venir aquí y dentro de key down event listener Yo digo que si la tecla que se presionó es enter, tenga cuidado con la ortografía. Las letras minúsculas y mayúsculas aquí son muy importantes. Puedes consolo key desde aquí para ver cómo se deletrea cualquier valor de clave en particular A lo mejor quiero múltiples ataques diferentes. El disparo básico se activará cuando presionemos el número uno en el teclado. En este caso, los números están en número en este contexto es un tipo de datos de cadena. Cuando se presiona el número uno en el teclado, ejecuta el método de disparo del jugador. Para verlo realmente, necesito recorrer los diez objetos de proyectiles dentro matriz de carrete de proyectiles y dibujarlos y actualizarlos Lo hago desde aquí abajo. Este carrete de proyectil para cada objeto de proyectil en la matriz, llamamos su actualización y llamamos a su método de dibujo Sabemos que el sorteo espera contexto. Simplemente lo pasamos a lo largo del valor de contexto de la línea 84. Ese valor de contexto se espera aquí. Si se activa proyectil, su propiedad libre se establece en false El contexto se utilizará para llamar al método del rectángulo phil que dibujará el proyectil para nosotros. Guardo mis cambios y presiono uno en el teclado. El método de disparo se ejecuta. Obtenemos un objeto proyectil libre del charco de objetos y disparamos su método de inicio Los proyectiles siempre comenzarán desde posición x e y actual de los jugadores, incluso cuando el jugador se mueva Porque estamos pasando esas coordenadas actuales del jugador al método de inicio. Aquí sólo puedo disparar diez veces. Y luego gastamos todos los objetos en la piscina de objetos y ya no puedo disparar. Quiero que los proyectiles se reinicien y vuelvan a estar disponibles en la piscina cuando salgan volando Si la coordenada y vertical del proyectil es menor que cero menos la altura del proyectil, si todo el proyectil se ha movido por detrás del borde superior de la pantalla, llamamos método de reinicio desde El método Reset establecerá la propiedad libre en ese proyectil en true devolviéndola de nuevo a la piscina, haciéndola disponible nuevamente Ahora puedo disparar sin parar. Si disparo muy rápido, me va a dar diez proyectiles en una ráfaga corta Al salir de la pantalla, se reinician y podrán volver a salir. Los rectángulos perfectos sobre lienzo se dibujan desde la esquina superior izquierda El jugador X e Y es esta esquina superior izquierda. En realidad, quiero que salgan de la mitad del jugador horizontalmente. Le pasamos x del jugador más la mitad del ancho del jugador. Todavía no está perfectamente centrado, lo que se vuelve aún más obvio si hago los proyectiles A lo mejor mi ataque secundario se verá algo así. Necesito que esto esté centrado sobre el jugador, sin importar la forma que tenga el proyectil Lo haré desde aquí, dentro de la clase proyectil Porque aquí tenemos acceso al ancho del propio proyectil Podemos compensar la posición horizontal que viene del jugador por la mitad del ancho del proyectil así Ahora bien, no importa cuán estrechos o anchos sean los proyectiles actuales, siempre vienen exactamente de la mitad del jugador horizontalmente Hagamos ocho píxeles por ahora. Increíble trabajo. Tenemos al jugador que puede moverse y disparar proyectiles Las técnicas que acabamos de aprender son sumamente útiles y poderosas y puedes usarlas para hacer cientos de juegos diferentes si el jugador que tenemos es muy amplio, por alguna razón, si detengo al jugador cuando toca los bordes así, lucharemos por disparar enemigos que estén más cerca de los bordes del área de juego. No podremos alcanzarlos con nuestro ataque básico. Esto puede crear todo tipo de chal***ges en cómo diseñamos nuestro juego Lo arreglaré aquí en área de límites horizontales. De hecho permitiré que el jugador se esconda detrás del borde de la pantalla hasta que se oculte la mitad. Sólo entonces introduciremos stop boundary. Ahora puedo llegar a los enemigos todo el camino a la izquierda. También haré esto por el borde derecho del área de juego. Ahora no importa cuán estrecho o ancho sea el jugador, nuestro sencillo ataque láser básico puede llegar a todas partes y los enemigos no tienen dónde esconderse. Perfecto. 7. Ondas de enemigos: Hablando de enemigos, esto va a ser interesante. Vamos a crear enemigos que nos lleguen en una formación de cuadrícula. Y cada vez que derrotemos a una ola, vendrá una ola mayor de enemigos. Antes de hacer eso, vamos a darle algunos antecedentes a nuestro juego. Te estoy dando un conjunto completo pulido y profesional de alta calidad a los activos artísticos para este proyecto. Puedes descargarte todo en la sección de recursos a continuación. Toma la imagen PNG de fondo que puedes encontrar ahí y ponla dentro de la carpeta de tu proyecto, luego úsala como propiedad de fondo. De hecho, podemos hacer esta parte con CSS. Css es GPU acelerado. Hacerlo de esta manera es más rápido en cuanto al rendimiento y luego volver a dibujar la misma imagen una y otra vez en Navas a medida que nuestro juego anima Ahora mis dibujos en lienzo son demasiado oscuros contra ese nuevo fondo de aquí abajo. Cuando todo está cargado y navas está configurado, establecí el estilo de relleno para todo lo dibujado en navas demasiado Por ahora esto está bien. Crearé enemigos individuales usando nuestra clase enemiga personalizada. Necesitarán acceso a algunos métodos y propiedades que pongo en la clase principal del juego. Tendremos una referencia a la clase de juego a partir de aquí también. Así. algunos retos que tenemos que resolver aquí. Porque queremos que los enemigos se muevan en una grilla y queremos que se muevan en la misma dirección. Quiero que toda la ola de enemigos siga el mismo patrón de movimiento. Tendremos que detenernos y pensar en cómo organizar nuestro código. Para lograr que cada enemigo, realidad todos los objetos en nuestro juego necesitan tener propiedades de ancho, alto, x e y, principalmente para fines de detección de colisiones. El método de dibujo personalizado tomará el contexto como argumento y acariciamos el rectángulo que representa a cada enemigo. Por ahora, iba a convertir a los enemigos en un grupo de objetos reutilizable también. Pero quiero que esta clase sea amigable para principiantes y combinar un grupo de objetos con la lógica de cuadrícula que estamos a punto de escribir podría llegar a ser difícil de seguir. Por ahora, los enemigos serán objetos básicos muy simples, no hay funcionalidad de pool de objetos aquí. Por ahora, el método de actualización moverá al enemigo. Pero si quiero que todos los enemigos se muevan en una formación de cuadrícula, en el mismo patrón de movimiento, no podemos tener velocidad individual aquí y actualizar a cada enemigo por separado. valor de posición de toda la ola de enemigos tendrá que almacenarse en algún lugar de nuestro código en algún lugar. Y cada enemigo sabrá dónde se sienta en relación a ese valor. A medida que la ola se mueva, todos los enemigos se moverán con ella. Déjame mostrarte exactamente a lo que me refiero. Crea una clase envolvedora llamada Wave Constructor. Nuevamente, necesitará acceso a propiedades y métodos en el objeto principal del juego. Sabemos que todos los objetos en nuestro juego necesitan tener posición de ancho, alto, x e y. Pongamos la posición a 00 esquina superior izquierda del área de juego por ahora. Aquí dentro del constructor de clase de juego, elimino este consolo Cada ola tendrá un conjunto de enemigos organizados en una grilla. La primera ola será una cuadrícula de nueve enemigos organizados en tres columnas y tres filas. También necesito que el tamaño del enemigo, el tamaño de cada enemigo individual esté disponible a nivel mundial, toda mi base de código. Porque necesitamos ser conscientes de ello en múltiples lugares en el código. Por ahora, cada enemigo será un cuadrado de 60 veces 60 píxeles. Quiero que toda la cuadrícula de enemigos rebote entre los bordes izquierdo y derecho del área de juego. Cada vez que la ola de enemigos toque el borde, saltará un paso hacia abajo, más cerca del jugador. El ancho de toda la ola de nueve enemigos organizados en tres filas y tres columnas será este juego de puntos. Columnas de punto de la línea 95 veces este juego tamaño enemigo desde la altura de la línea 97 será este juego de puntos hacer rosa veces tamaño enemigo. Al igual que este método de render dibujará y actualizará toda esa ola de enemigos. Necesitará contexto como de costumbre, entonces usaremos ese contexto. Y por ahora vamos a trazar rectángulo representando toda el área de onda. Sólo tengan que aguantar conmigo, va a tener más sentido en un minuto. Cada ola contendrá un cierto número de enemigos dependiendo de la rosa didot e hizo el valor de las columnas Estamos empezando por tres veces tres. La primera ola tendrá nueve enemigos. Cada vez que destruimos a todos los enemigos en una ola. La siguiente ola se activará automáticamente. Mantendremos todas las ondas activas dentro de la matriz de ondas didot en la clase principal del juego Inmediatamente cuando creamos la clase de juego, la primera ola de tres veces 39 enemigos se creará automáticamente y se empujará creará automáticamente y se empujará dentro de esta matriz de ondas de puntos. Pasé esta palabra clave para representar el objeto principal del juego en el que estamos dentro en este momento. Se espera aquí y convierte a esta propiedad de clase de juego de puntos. Desde aquí tenemos acceso a columnas, filas y tamaño enemigo. Ahora puedo tomar la matriz de ondas didot, empujamos una ola dentro Ya llamo por cada objeto Wave dentro de esta matriz de ondas. Actualmente solo hay uno dentro. Lo llamaremos método de rendición. Le pasamos contexto. Podemos ver la onda que se dibuja aquí, estilo de trazo D a ancho, así es más fácil verla línea con, por ejemplo, 35 píxeles. tamaño de la ola se ajustará para adaptarse a todos los enemigos que contiene. Si tenemos cinco columnas y ocho filas de enemigos, 40 enemigos en total, se dispondrán dentro de esta zona. Toda la ola de enemigos se moverá como una sola unidad. La velocidad será sobre el objeto de onda. Por cada fotograma de animación, aumentamos la posición horizontal por velocidad si x es menor que cero o si x es mayor que el ancho del juego menos el ancho de la ola. Lo que significa que siempre que el rectángulo de onda toque el borde izquierdo o derecho del área de juego, voltee su velocidad horizontal a su valor opuesto. Esto hará que la ola rebote sin cesar a izquierda y derecha. Eso funcionó. Hagámoslo tres veces tres. La velocidad vertical será cero cada vez que toque uno de los bordes. Quiero que salte por tamaño enemigo hacia abajo verticalmente hacia el jugador. Corté esta línea y la pego aquí abajo. Y también aumento la posición vertical y por velocidad y. cuando tocamos la H así, velocidad y se mantiene alta y la ola sigue bajando. Solo quiero que salte por tamaño enemigo en un fotograma de animación y luego regrese a velocidad y cero. Sí, este es el patrón de movimiento que estaba buscando. Tenemos una ola que se mueve a izquierda y derecha y hacia abajo sobre el área de juego. Contendrá una grilla de enemigos. Cada enemigo se moverá con la ola usando las posiciones x e y de esa ola, más la posición relativa horizontal y vertical del enemigo individual dentro de esa cuadrícula. Dentro de la ola, llamaré a esos valores esta posición x y esta posición de punto Y. posición X y la posición Y es la posición del enemigo dentro de la ola. Cada enemigo tiene que saber exactamente dónde se encuentra dentro de la parrilla. Ancho del enemigo va a estar viniendo de este valor de la línea cien y 11, lo puse a 60 pixeles. Este juego de punto tamaño enemigo. Para ancho y alto también. Los enemigos tendrán forma cuadrada. X e y serán 00. Inicialmente, cuando creamos un enemigo, lo pasamos como argumento posición relativa entre el enemigo y toda la ola. Los valores de posición x y posición y determinarán en qué la onda se encuentra en relación con la esquina superior izquierda del rectángulo de onda. Este enemigo en particular se sienta en la posición x y la posición Y más allá, ya que los argumentos se convertirán en propiedades de clase. Aquí el método de actualización tomará la posición x e y de la onda como argumentos. Y establecerá la posición x e y del enemigo en relación con ese valor actual. exposición del enemigo es la posición de la ola más la posición horizontal relativa del enemigo en esa ola posición Y del enemigo será vertical, posición Y de la ola más posición vertical relativa de este enemigo en particular en la ola. Podría haber una mejor manera de estructurar este código. Simplemente se me ocurrió esto. Veamos cómo funciona para nosotros. Cada ola tendrá su propia matriz de enemigos. Le damos un método de creación personalizado en su interior, tendremos un bucle de cuatro. Queremos organizar algo en una cuadrícula, lo que significa que necesitaremos dos bucles anidados de cuatro bucles dentro de otro bucle Vamos a escribirlo primero y luego explicamos cómo funciona. El bucle exterior de cuatro manejará filas a medida que bajemos fila por fila. La coordenada y vertical irá en aumento. El bucle for interno manejará columnas a medida que vayamos de izquierda a derecha. Dentro de cada fila, la coordenada x horizontal está aumentando. Digamos que estamos aquí. Este es el cuadrado enemigo actual 60 veces la fila de 60 píxeles es cero. En el bucle externo, ingresamos al bucle interno y recorremos todas las posiciones horizontales Dentro de esa fila, llegamos a la última columna. Salgamos del bucle interno, entramos en el bucle exterior, aumentamos la posición vertical Y y saltamos a la siguiente fila. Nuevamente, entramos en el bucle in, hacemos un ciclo a través de posiciones horizontales a través de las columnas de izquierda a derecha. Salimos del bucle interno, entramos en el bucle externo, aumentamos y y saltamos a la siguiente fila. Entramos en el ciclo inter loop a través de todas las posiciones horizontales. Ahí repetimos esto una y otra vez hasta que creamos toda la cuadrícula de toda la ola. Necesito tener en cuenta el tamaño del enemigo a la hora crear propiedades enemigas X y enemigas Y. También podría haber saltado alternativamente por el valor del tamaño del enemigo aquí y aquí. El resultado será el mismo. Cada vez que saltamos a una nueva celda en la cuadrícula. Tomamos esta ola de enemigos y empujamos un nuevo objeto enemigo dentro. Podemos ver que el constructor de clase enemiga espera juego y posición relativa X e Y de ese enemigo en la parrilla. Le paso este juego de la línea 78. Lo paso enemigo X y enemigo Y. Bien, Cuando creamos una nueva ola, echamos un vistazo al interior del constructor de clases de juego para ver el valor actual de filas y columnas. A partir de eso creamos una grilla de enemigos. Quiero crear método para activar automáticamente cuando creamos una nueva ola. Yo lo llamo aquí, eso significa que tan pronto como creamos una instancia de clase ola, matriz de este enemigo se llenará automáticamente de objetos enemigos. Y sus coordenadas X e Y se organizarán en una cuadrícula dentro del render. Ahora puedo tomar esa matriz y por cada objeto enemigo que hay dentro, llamo a su método de actualización y es un método de dibujo. Paso el contexto del método de dibujo como de costumbre. Aquí podemos ver que el método de actualización espera la posición X e Y de la ola para que pueda usarla para posicionar a los enemigos en una cuadrícula como formación. Siempre que llamo actualización enemiga, tengo que pasarle la posición X e Y de su objeto de onda envolvente para asegurarme de que todos los enemigos se muevan por la pantalla. Con él estamos dibujando tres veces tres enemigos. Quita este rectángulo que recorre toda la onda. Eso fue sólo para ayudarnos a visualizar lo que estaba pasando. Todavía estamos dibujando a los enemigos. Tenemos una ola de tres veces tres enemigos y ellos se mueven por la pantalla de manera sincronizada. Perfecto. Quiero que los enemigos vengan ola por ola. Y necesito que cada ola se ponga en marcha fuera de la pantalla y flote en el área de juego. Dije que la coordenada y inicial de la ola cero menos la altura de la ola, era cero menos la altura de la ola, lo que significa que inicialmente se ocultará detrás del borde superior del área de juego. La ola se creará fuera de la pantalla, y quiero que flote rápidamente hacia abajo hasta que sea completamente visible. Entonces iniciará su patrón de movimiento izquierda-derecha. Si esta Y es menor que cero, aumente Y en cinco píxeles por fotograma de animación. Flotará así y comenzará a rebotar a izquierda y derecha cuando todas las filas y columnas de enemigos, toda la ola sea completamente visible dentro del área de juego Puedo probarlo cambiando el número de filas y columnas. Se puede ver que sigue funcionando. Todavía flota hacia abajo hasta que todos los enemigos en esa ola sean visibles y puedan ser atacados por los láseres de los jugadores Intento cinco columnas y siete filas, lo que me dará una ola de 35 enemigos. 8. Olas enemigas: Esto va muy bien. Tenemos al jugador, proyectiles y enemigos. Quiero que los proyectiles y enemigos interactúen. Cuando un enemigo es alcanzado por un proyectil, enemigo es destruido, el objeto proyectil es devuelto a la piscina de objetos Cada objeto de nuestro juego tiene propiedades x, y, ancho y alto. Vamos a crear un método de detección de colisiones re utilizable, que comparará dos objetos rectangulares le pasemos como argumentos. Y devolverá verdadero o falso dependiendo de si estos dos rectángulos chocan o no A los efectos de dos de, detección de colisiones, los rectángulos deben alinearse con los ejes Si se rotan de alguna manera, tendríamos que tratarlos como polígonos La detección de colisiones tendría que ser una técnica más compleja como la fórmula del teorema del eje de separación Hoy en día, todos los rectángulos estarán alineados con ejes, no girados Por lo tanto, podemos usar la fórmula de detección de colisiones simple y más directa entre dos ejes alineados, lo que significa rectángulos no girados método de colisión de verificación personalizado reutilizable Tomaremos dos argumentos, rectángulo A y rectángulo B. Hay muchos artículos en línea sobre esta técnica. Puedo hacer referencia a ellos. Dejaré algunos enlaces en la sección de recursos a continuación. La fórmula siempre comprueba si el rectángulo A está dentro los límites del rectángulo B horizontal y también verticalmente. Para eso, necesitamos realizar cuatro comprobaciones comparando los cuatro bordes del rectángulo en. Si las cuatro comprobaciones son verdaderas, tener una colisión más, aunque una de ellas sea falsa, no hay colisión. Déjame mostrarte cómo funciona eso. Por cierto, esta fórmula es muy barata a la hora rendimiento porque los cálculos son muy sencillos. Si nuestro diseño de juego lo permite, deberíamos intentar usarlo. Algunos juegos, por supuesto, exigen técnicas de detección de colisiones más complejas. No siempre es adecuado para nuestro proyecto. Hoy es perfecto. Tenemos que revisar cuatro cosas. Primero, verificamos si la posición horizontal del rectángulo que pasamos como argumento a aquí es menor que la posición horizontal del rectángulo B más el ancho del rectángulo. Estamos comprobando si esto está a la izquierda de esto. Al mismo tiempo, operador del doble por ciento. También necesitamos verificar si la posición horizontal del rectángulo a más el ancho del rectángulo a es más que la posición horizontal del rectángulo. Estamos comprobando si esto está a la derecha de esto. Si ambas comprobaciones son ciertas, sabemos que los rectángulos chocan horizontalmente, pero aún pueden estar lejos el uno del otro Tenemos que hacer las mismas dos comprobaciones, pero esta vez para el eje y vertical. Compruebo si la posición vertical del rectángulo A es menor que la posición vertical del rectángulo B más su altura. Si esto está por encima de esta última comprobación, Comprobamos si la posición vertical del rectángulo A más su altura es mayor que la posición vertical del rectángulo B. Si ésta está por debajo de esta. Sólo si las cuatro comprobaciones son ciertas, tenemos una colisión. Si todos son falsos o al menos uno de ellos es falso, sabemos que no hay colisión. Sabemos que los dos rectángulos están muy alejados el uno del otro, al menos en una dirección De hecho, puedo simplificar esto y puedo hacer que este método evalúe automáticamente esta expresión y devuelva verdadero o falso directamente. De esta manera solo puedo llamar cheque colisión pasado dos objetos rectangulares, por ejemplo proyectil y enemigo y directamente devolverá verdadero o falso Para mí este es nuestro método de detección de colisiones reutilizable. Ten en cuenta que cualquier objeto que pasemos como rectángulo A o rectángulo necesita tener propiedades x, y, w e hyde. Para que este código funcione, quiero usar un método personalizado de verificación de colisión. Ahora tenemos los diez objetos de proyectiles reutilizables Aquí dentro de la matriz de piscinas de proyectiles, los enemigos se almacenan dentro de la matriz del enemigo por separado Para cada ola aquí, esos enemigos se crean usando la clase, el plano que escribimos aquí en la línea 57 Entonces tiene sentido poner ese cheque de colisión aquí. Cada vez que se ejecuta el método de actualización de cada objeto enemigo, compararemos x, y dentro de la altura de ese enemigo en particular con los diez objetos de proyectil Sólo comprobaremos contra la piscina de proyectiles de proyectiles activos Para cada uno, para cada proyectil llamamos a nuestro método personalizado de verificación de colisión Sabemos que espera el objeto rectangular a y el objeto rectangular como argumentos. Sabemos que ambos objetos necesitan tener propiedades x, y, width y height para verificar el método de colisión para poder trabajar con ellos. Le paso este objeto enemigo como rectángulo A y lo paso los diez proyectiles uno por uno mientras circulamos a través del charco de proyectiles Para cada método escribimos check collision method para devolver directamente true o false. Así que sólo puedo decir si y envolverlo entre paréntesis así. Si este enemigo y proyectil chocan, este bloque de código correrá Eliminamos al enemigo. Dije que está marcado para propiedad de eliminación a true. También necesito declarar esa propiedad en constructor de clase enemiga Arriba aquí dentro del método render en el objeto de onda, vamos a filtrar a los enemigos que fueron destruidos fuera de la matriz enemiga. Construido en el método de filtro de matriz método de filtro creará una copia de una matriz dada y esa copia filtrada contendrá solo los elementos que pasaron una determinada condición aquí básicamente estoy diciendo crear una copia de esta matriz de enemigos de punto y solo permitir dentro de los elementos que han marcado para la propiedad de eliminación establecida en false. Luego anula esta matriz original de enemigos de dis dot con esa nueva matriz filtrada. De esta manera estoy quitando a todos los enemigos que colisionaron con proyectiles porque esos enemigos han marcado para su eliminación la propiedad establecida Estoy recibiendo un error de consola. Es porque he escrito mal tiro de proyectil aquí. Sí, se supone que son proyectiles deletreados tiran con una S. Cuando llega una nueva ola de enemigos, se puede ver que el enemigo de abajo izquierda ya está destruido Es porque todos mis proyectiles inactivos están inicialmente sentados en esa esquina en la coordenada 00 No los estamos dibujando y actualizando, pero seguimos calculando colisión entre esos proyectiles inactivos y enemigos Lo arreglo solo comprobando colisión si el proyectil libre es falso Si el proyectil está actualmente activo, queremos ignorar los proyectiles inactivos a la hora de detectar colisiones, también quiero reiniciar ese proyectil y devolverlo a ese proyectil y devolverlo Choca con un enemigo para que cada proyectil pueda destruir solo a un Tenemos un método de reinicio aquí para eso que establecerá la propiedad libre verdadero proyectil que regresa a la piscina de objetos disponibles no utilizados Cuando ocurre una colisión entre enemigo y proyectil, llamo al método de reinicio en ese proyectil para desactivarlo Sí, esta es la jugabilidad que estaba buscando. Cuando ocurre una colisión entre el enemigo y el proyectil, enemigo se elimina y objeto proyectil vuelve a la piscina de objetos 9. Detección de colisiones: Quiero dibujar textos de estado como las vidas de los jugadores de contador de ondas de puntuación o cuánta energía tenemos actualmente para láseres y ataques especiales Voy a hacer todo eso dentro del método draw status text, llamo built in fill text method que toma al menos tres argumentos, el texto que queremos dibujar y las coordenadas x e y del punto de anclaje en relación con el cual se posicionará el texto. Yo lo llamo aquí dentro de Render, y quiero dibujarlo detrás de los enemigos. Entonces lo dibujo primero. Es sólo mi elección. Puedes dibujarlo en la parte superior si quieres. Pasé el contexto , y aquí vamos. Configuré la fuente a, por ejemplo, 30 píxeles de impacto para letras bonitas, grandes y fáciles de leer. Mantendremos la puntuación actual como variable en el objeto principal del juego. Comenzará en cero. Dentro del texto de campo, agregaré ese valor de variable dinámica junto al texto así. Ahora quiero que el valor de puntuación aumente uno cuando destruimos a un enemigo aquí. Cuando proyectil y enemigo chocan, tomamos esta puntuación de juego y la aumentamos en una . Así. Bien, estoy disparando enemigos y estoy aumentando mi puntaje. Perfecto. El juego se perderá si algún enemigo toca la parte inferior de la pantalla. Por cada enemigo, voy a comprobar si su posición vertical Y más su altura. El límite inferior del rectángulo enemigo es más que esta altura de punto de juego de puntos, el límite inferior del área de juego visible. Si eso sucede, configuré este juego de juego en true y destruyo a ese enemigo estableciendo su propiedad marcada para eliminación en true. Por ahora, necesito asegurarme de declarar ese juego sobre propiedad aquí. Inicialmente será falso. Si el juego terminado es cierto, quiero dibujar algún texto. Como puede ver por defecto el texto del lienzo está alineado a la izquierda. Quiero que este juego sobre mensaje esté alineado al centro. La fuente será de 100 píxeles. El texto de impacto dirá el juego terminado en mayúsculas. Y será exactamente en medio de la pantalla. Ancho del juego veces 0.5 y altura del juego veces 0.5 Niza, tenemos juego grande sobre texto en medio de la pantalla. Tan pronto como el enemigo toque el fondo del área de juego. El problema es que esta línea de texto y las propiedades de fuente cambian todo el estado del lienzo. Y ahora el mismo estilo de texto se aplica al texto de relleno de partitura en la línea 194. Envolveré todo este bloque de código entre safe y restore construido en métodos canvas. El método seguro guarda el estado del lienzo, incluida la fuente de línea de texto de estilo de relleno y todas las demás propiedades del lienzo. Crea un punto seguro, como en un videojuego. Se acuerda qué valores tienen todas las propiedades del lienzo. En este punto, entonces podemos alterar la configuración del lienzo de la manera que queramos. Entonces cuando llame a restore, devolverá todas las propiedades de canvas a lo que eran cuando se llamaba método seguro. ¿Qué está pasando? Ahora guardamos el estado del lienzo, dibujamos puntaje a tamaño de 30 píxeles y alineado a la izquierda, establecemos texto 200 píxeles alineados al centro, dibujamos juego sobre y restablecemos esos ajustes. De vuelta en el siguiente bucle de animación, cuando se vuelve a dibujar la partitura, se vuelve a alinear a la izquierda de 30 píxeles Es un bucle de animación sin fin que tenemos aquí. Espero que tenga sentido, podamos llevarlo aún más lejos y como envolvimos todo el texto de estado entre safe y restore, lo separamos del resto de las formas dibujadas en lienzo. También le puedo dar algunas sombras aquí. Las sombras solo se aplicarán al texto, pero no a los enemigos, proyectiles y formas de jugador Para mostrar sombras de lienzo, necesitamos definir al menos tres de cada cuatro propiedades disponibles. Distancia horizontal entre la forma y la sombra. Distancia vertical, color de la sombra. Y también podemos aplicar desenfoque usando propiedad de desenfoque de sombra. Pero es opcional. El desenfoque es caro en el rendimiento. Implica calcular valores de opacidad y subpíxeles. Lo voy a evitar hoy. Estamos contando puntaje y estamos dibujando juego sobre mensaje agradable. Cuando destruyo una oleada entera de enemigos, quiero que una nueva ola más grande venga automáticamente. contaremos y los jugadores pueden chal***ge ellos mismos para ver cuántas oleadas de enemigos pueden derrotar El conteo de olas comenzará con una ola número uno. Así, vamos a dibujar el número de onda aquí, así. Dentro de nuestro método de texto de estado de dibujo verticalmente, establecí el texto en 60 píxeles. No, a 80 pixeles de la parte superior. Sí, la primera ola será una pequeña cuadrícula de dos veces dos enemigos. Cuando destruya a estos cuatro enemigos, quiero que aparezca una nueva ola más grande. Manejaremos esa lógica dentro de un método de cliente. Yo llamo por ejemplo, nueva ola. Se puede pensar en esta técnica como una mecánica de nivel muy simple. Cuando derrotes a una ola, una ola de enemigos más grande y más difícil llegará a un nuevo nivel. Podemos agregar cualquier condición y propiedades de esa nueva ola aquí. Primero, puedo aumentar el tamaño de la onda una columna y en una fila. Vamos a probar eso. Aumentaré columnas y valores de fila en uno y empujaré nuevo objeto de onda en esta matriz de ondas. Le paso una referencia al objeto principal del juego como argumento porque lo espera como podemos ver aquí en la línea 91. Debido a que aumentamos el valor de columnas y filas, estas líneas de código asegurarán que la siguiente onda sea más grande. También necesito asegurarme de que cada ola active la siguiente ola solo una vez. Entonces le doy una bandera que indica si ya activó o no la siguiente ola. Al principio, lo dije a falso. Cada ola contiene una matriz enemiga que contiene todos los objetos enemigos activos actualmente en esa ola. Mientras corremos para cada método en todos los objetos de onda en esta matriz de ondas, verifico si el ***gth de la matriz del enemigo en esa ola es menor En ese mismo tiempo si la bandera que llamé, gatillo de la siguiente onda aún no se ha activado. Y sólo si el juego terminado es falso. Porque cuando se acabe el juego, quiero que dejen de llegar nuevas olas. Si toda esta expresión se evalúa como verdadera, ingresamos el bloque de instrucción if dentro Yo llamo método de la nueva ola que acabamos de escribir. También aumentaremos el recuento de ondas en uno. Finalmente me senté al siguiente disparador de onda a true, evitando que esta ola en particular desencadene más olas nuevas Lo pruebo, tenemos una ola de dos veces dos enemigos. Si los derroto a todos, inmediatamente conseguimos la siguiente ola de tres por tres enemigos. Perfecto es porque aquí en el método new wave, siempre agregamos una columna más y una fila más cada vez que se crea nueva ola de enemigos. Si quiero que las olas crezcan un poco más lentas, quiero aleatoriamente en el 50% de los casos para agregar una nueva columna, o en el otro 50% de los casos, quiero agregar una nueva fila. Eso lo podemos hacer por ejemplo. De esta manera las matemáticas al azar llamadas por sí solas, así, nos darán un valor aleatorio 0-10 incluido, uno excluido Puedo decir que si las matemáticas al azar llamadas por sí mismas son menos 0.5 aproximadamente en 50% de de 0.5 aproximadamente en 50% de los casos aumentan la cuadrícula enemiga en una columna, lo contrario agregue una fila en su lugar. De esta manera las olas seguirán creciendo pero un poco más lentas. Pasará más tiempo antes de que llenen gran parte de la pantalla y el juego se vuelve más difícil después. También agregaré batallas de jefes, características al azar. En lugar de una nueva ola, podríamos simplemente conseguir un gran jefe con mucha salud. Cuando derrotemos al jefe, las olas comenzarán a llegar como de costumbre. También tendré que asegurarme de que solo estamos agregando nuevas columnas a cierto punto. Digamos hasta que el ancho de toda la onda sea inferior al 80% del ancho de la pantalla. Si permitimos que vaya más que eso, el juego puede volverse rápidamente demasiado rápido y completamente injugable Puedes intentar jugar el juego en esta etapa donde no tenemos limitación en columnas y filas. A ver a lo que me refiero. También solo agregaré filas si la altura de toda la ola enemiga es inferior al 60% de la altura del juego. Probé configurando la cuadrícula de inicio a cinco veces cinco enemigos nueva ola. Y puedo ver que agregó una nueva columna. Déjame ocuparme rápidamente de ellos. Necesito agregar algunas armas más poderosas después. Ahora agregó una nueva fila. Tenemos una grilla de seis por seis. Ahora agregó una fila más. Tenemos una grilla de seis por siete. Cuando lleguemos a esta etapa, quiero que los enemigos reaccionen de alguna manera cuando toquen al jugador. El primer paso en esa interacción será detectar una colisión entre los enemigos y el jugador. Si revisas colisión entre este enemigo y el jugador es verdadero conjunto marcado para su eliminación en ese enemigo a true para destruirlo. Dentro de esa declaración if, hacemos otra comprobación si game over es falso. Si la puntuación es superior a cero, reducimos la puntuación en uno. Castigaremos al jugador por no poder disparar a distancia al enemigo. También voy a reducir la vida de los jugadores en uno. Entonces verifico si la variable de vidas del jugador es menor que una. Si es así, pusimos el juego a la verdad. Buen jugador empezará con digamos tres vidas. Para verificar si funciona, visualizaremos las vidas dentro del método de texto de estado de dibujo. Pero déjame mostrarte una manera diferente de cómo mostrarlos. En lugar de simplemente dibujar un número que diga tres, voy a crear un bucle de cuatro que correrá una vez por cada vida de jugador. Cada vez que se ejecuta, llenará rectángulo en las coordenadas 1010 dentro de la altura del rectángulo también será diez veces diez píxeles. Ahora estoy dibujando tres cuadrados uno encima del otro. Quiero que esto sea una barra de carga. Acabo de multiplicar posición horizontal índice de tams de los cuatro bucles Ahora las tres casillas que representan vida de los jugadores se dibujan una al lado de la otra. Este valor es efectivamente espaciado entre ellos. Si el ancho del rectángulo es menor que el espaciado, los veremos dibujados como formas separadas como esta altura, tal vez 20 píxeles. También puedo agregar margen izquierdo así verticalmente. Los coloco a 80 píxeles de la parte superior, 100 píxeles de la parte superior. Si dejo que los enemigos golpeen al jugador, podemos ver que perdemos vidas y luego nos sale juego sobre mensaje. Cada vez que derrotamos con éxito a una ola de enemigos, se crea una nueva ola. Le daremos al jugador un bonus de vida más. Lo pruebo, tengo tres vidas. Yo derroté la ola, sí, ahora tengo cuatro vidas. Pongo la cuadrícula en cinco columnas y 11 filas. Necesito probar algo. Cuando termine el juego, quiero evitar que el jugador pueda obtener más puntos de puntuación. Aquí estamos dando estos puntos de puntuación. Sólo lo haré si game over es falso. Ahora bien, si termino el juego y destruyo a más enemigos, puntuación del juego sigue siendo la misma. Aquí abajo, quiero agregar una línea más de texto. Copio estas dos líneas de código. La fuente será de 20 píxeles. El texto dirá presionar R para reiniciar. Me muevo un poco abajo bajo el juego sobre mensaje. Ahora cuando aparece el mensaje game over, obtenemos otro mensaje que dice presionar R para reiniciar. De hecho, implementemos esa característica. 10. Método de reinicio: Reiniciar un juego se puede manejar de muchas maneras diferentes. Normalmente lo hago cuando finalizo mi estructura básica de código como lo hicimos aquí En este punto entendemos bastante bien nuestra base de código, y entendemos qué variables, propiedades en toda nuestra base de código necesita restablecerse a un valor predeterminado cuando se reinicia el juego Sí, también puedo reiniciar el juego simplemente recargando la ventana del navegador, pero hagámoslo de la manera correcta esta vez Primero, necesito reiniciar el jugador. Y puedo hacerlo aquí desde la clase de jugador. Voy a darle al jugador un método de reinicio personalizado aquí. Sé que cuando reiniciemos, quiero que el jugador vuelva a la mitad de la pantalla. Quiero restablecer las vidas de los jugadores a tres. Podría haber más cosas aquí para reiniciar más tarde después de que realmente animemos la nave espacial del jugador Pero por ahora, esto es todo lo que tenemos que hacer en esta materia. En la clase de juego principal, creamos el método de reinicio personalizado principal. A partir de aquí, llamaré al método de reinicio que acabamos de definir en el jugador. También necesito restablecer las propiedades del juego. Voy hasta aquí dentro del constructor de clases de juego y tengo que pensar qué propiedades de aquí cambian los valores a medida que jugamos el juego. Por lo tanto, habrá que restablecerse a los valores predeterminados cuando reiniciemos el juego. Copo todo este código y lo pego aquí abajo. El tamaño del enemigo seguirá siendo el mismo. Yo borro eso. Cuando el juego se reinicie, quiero volver a una pequeña cuadrícula de enemigos, tal vez dos columnas y dos filas Eliminaré todos los objetos de onda de esta matriz de ondas en caso de que todavía haya alguno. Automáticamente empujaremos una nueva ola de dos veces dos enemigos dentro. Reiniciamos el contador de ondas a uno, restablecemos la puntuación a cero y terminamos el juego a caídas. Debo asegurarme de que las columnas y los valores de fila sean los mismos aquí también. Porque a medida que juego el juego, el número de columnas y filas aumenta a medida que derroto a los enemigos. Ola tras ola. Tenemos que asegurarnos de que el método de reinicio restablece la cuadrícula enemiga de nuevo a este pequeño tamaño Aquí abajo tenemos key down event listener dentro digo si la tecla que se presionó es R minúscula, llamamos al método de reinicio personalizado Acabamos de escribir el juego. Destruyo a algunos enemigos. Presionamos R y vuelve a salir la primera ola. Yo juego, la parrilla enemiga se está haciendo más grande. Presiono R y reiniciamos el juego y los enemigos están de vuelta a la grilla básica de dos por dos. Sí, esto claramente funciona. Puedes usar u operador aquí y puedes tener múltiples teclas activando el método de reinicio Por ejemplo, R. minúscula y mayúscula Si quieres, no quiero que el jugador pueda reiniciar el juego a menos que veamos el juego por encima de la pantalla. Solo ejecutaremos el método restart si se presionó R inferior. Al mismo tiempo si el juego terminado es cierto, así, ahora estoy presionando R y no se está reiniciando Aumento los roles a diez solo para terminar un juego rápido, cuando los enemigos tocan la parte inferior del área de juego o cuando las vidas de los jugadores se agotan Veo juego sobre pantalla ahora Cuando presiono R en mi teclado, realidad reiniciamos el juego Perfecto. Cuando mantenga pulsado el número uno para disparar, jugador disparará los diez proyectiles una y otra vez muy rápidamente en una sola secuencia Y en realidad hace que el juego sea demasiado fácil y trivial. A lo mejor quiero evitar que el jugador haga eso. Y quiero asegurarme de que cada vez queramos disparar el ataque láser básico, proyectil básico, tengamos que volver a presionar el botón Si lo sujetamos, sólo sale un proyectil láser adelante agregaremos ataques más potentes, que dispararán un rayo láser masivo siempre y cuando mantengamos presionada una tecla. Pero ese ataque fuerte tendrá un costo de energía unido a él. No podemos tener un súper ataque Spam Mable como este, lo contrario el juego es demasiado fácil y apuntar menos. Voy a crear una simple bandera aquí llamada disparada. Significa que se presionó el botón de ataque y se disparó un proyectil láser dentro del evento Key down Tomo esto, lo pongo aquí. Después de eso, puse este punto disparado a verdadero. Y sólo vamos a llamar jugador disparar. Si disparado es falso, significa que solo disparará a un par. Evento clave abajo. Entonces en clave arriba dije este punto disparado a falso otra vez. Bien, ahora una pulsación de tecla significa un proyectil. Tenemos que soltar la tecla y volver a presionarla para disparar otro proyectil. Perfecto. Bien hecho olvidando hasta ahora. Si estás viendo la versión extendida, hay un código de esta etapa disponible para descargar. En la sección de recursos, tenemos un esqueleto de juego muy sólido. Nuestro juego tiene enemigos que vienen en oleadas cada vez más grandes. Utilizamos el pool de objetos para reutilizar proyectiles y mejorar el rendimiento y la gestión de la memoria de nuestro juego Tener un jugador controlado por teclado que obtenga una bonificación de vida extra por cada ola enemiga que derrotemos. Y ya implementamos muchas otras características más pequeñas. Es hora de que hablemos de gráficos y animación. En este proyecto, nos estamos enfocando en optimizar el rendimiento. Déjame mostrarte cómo hacer que el juego se vea genial, pero al mismo tiempo, cómo usar un sistema de animación muy ligero que asegurará que nuestro juego funcione sin problemas incluso en las máquinas más antiguas. 11. Clase de enemigos beetlemorfos: El primer tipo de enemigo que quiero implementar es un error espacial básico. Un simple enemigo con una vida que explota cuando lo golpeamos Con un proyectil, puedes descargar la hoja de cálculo en la sección de recursos a continuación, Coloca la imagen en la carpeta de tu proyecto e incluirla dentro del archivo HTML índice Así. Fuente es escarabajo morph, Png ID es Beetle MoorphScriptGS Declaramos nuestra clase enemiga, quiero tener múltiples tipos de enemigos. Pondré propiedades y métodos que se comparten entre todos los tipos de enemigos en la clase enemiga madre. Aquí voy a crear una subclase más pequeña para cada tipo de enemigo por separado. Ahí declararemos propiedades y métodos que son específicos sólo de ese tipo enemigo. Lo haremos usando la clase de palabra clave extends. Beetle morph, extiende enemigo. La palabra clave extense utilizada aquí hace que Beetle Morph sea una clase hijo de la clase padre enemiga También podemos llamar a esto una subclase y enemigo es una superclase le daré a esta subclase un constructor cuando cree una instancia de esta clase Primero, quiero ejecutar el constructor de superclase. Primero, quiero activar todo el código dentro del constructor de clase enemiga padre aquí. Para llamar a este constructor, puedo ver que necesito pasarlo juego position x y position y . constructor de subclase esperara los mismos argumentos Y llamaremos constructor de superclase activando el código dentro del constructor en la clase enemiga Sabemos que necesitamos pasarle estos valores para que ese código funcione realmente. Ahora activé el constructor de superclase y una vez que se ejecuta todo el código dentro de aquí, el código que contiene propiedades compartidas entre todos los tipos de enemigos, finalmente puedo usar esta palabra clave dentro de aquí y darle a esta subclase algunas propiedades que son específicas solo para este tipo de enemigo en particular Cada tipo de enemigo tendrá una imagen de hoja de sprites diferente. Esta imagen de punto aquí será documento punto obtener elemento por ID. Le dimos una identificación de escarabajo morph de nuevo en el archivo HTML índice. Si la ventana de tu navegador es más grande que la mía, también puedes ver la hoja de sprites aquí Quiero ocultar ese elemento IMG. Le doy display none con CSS, solo queremos dibujarlo con Java Script sobre lienzo. Dibujaremos esa imagen aquí dentro del método de sorteo en clase enemiga. Cualquiera que sea el código de dibujo que pongamos dentro de este método determinará cómo se ve cada enemigo individual. Voy a utilizar construido en dibujar el método de imagen que espera al menos tres argumentos. La imagen queremos dibujar imagen de punto y coordenadas x e y donde dibujarla. Ahora cuando guarde los cambios, obtendré un error. Este mensaje de error significa que script Java no puede encontrar la imagen que estoy tratando de dibujar. Es porque estoy creando una instancia de la clase enemiga padre. Esta clase no tiene propiedad de imagen didot. Necesito instanciar la subclase y el constructor de clase enemiga también se activará a partir Cuando extendemos clases como esta, Javascript también creará la herencia habitual basada en prototipos detrás de escena. Si por ejemplo, llamo al método draw y update desde una instancia de la clase Beetle Morph de la subclase, Javascript no encontrará ese método en esta clase hija y automáticamente seguirá la cadena prototipo hasta la clase enemiga padre Se activará ese dibujar y actualizar los métodos a partir de ahí. Para que Javascript realmente encuentre esta propiedad de imagen sentada en la clase infantil aquí, necesito crear una instancia de Beetle Morph En cambio, como acabamos de explicar, clase infantil Beetle morph también tiene acceso a todas las propiedades y métodos todas las propiedades y métodos de la clase enemiga padre Le pasamos juego y la posición relativa x e y de ese enemigo en particular dentro de la ola dentro de la cuadrícula. Estas propiedades se esperan aquí se pasan a lo largo de constructor de clase enemiga. Todo el código dentro del constructor de clase enemiga será ejecutado. Encima de eso, agregaremos cualquier código que tengamos aquí que sea específico solo de la clase Beetle Morph Beetle morph es una clase infantil de subclase, enemigo es una clase padre de superclase 12. Explicación de la animación de sprite: Nos sale esta extraña situación. Es porque estamos pasando una imagen de dibujo sólo tres argumentos. Entonces está dibujando toda la imagen, toda la hoja de cálculo que le pasamos aquí en su tamaño original. En estas coordenadas x e y, podemos pasar draw image opcional cuarto y quinto argumento para ancho y alto. Y cuando hagamos eso apretará o estirará esa imagen para que se ajuste a esa área especificada. Ahora estoy apretando los 12 fotogramas de la hoja de sprites en un área de un enemigo, de un Queremos recortar fotogramas individuales de la hoja de sprites Para ello, necesitaremos la versión más larga del método draw image que nos dé el mayor control sobre la imagen que estamos dibujando. Vamos a pasar dibujar método de imagen nueve argumentos. La imagen que queremos dibujar fuente Y, ancho de origen y altura de origen de un área. Quiero recortar de la imagen de origen, el destino X, el destino Y, el ancho destino y la altura de destino para definir en lugar del lienzo de destino queremos colocar qué lugar del lienzo de destino queremos colocar ese trozo de imagen de recorte, digamos que quiero recortar el área a partir de coordenadas 002, coordenadas 2050. Quiero estirar esta pieza recortada de imagen fuente sobre un espacio de 60 veces 60 píxeles en el lienzo de destino. Esto es lo que obtenemos. ¿Y si recortamos un área de 00-5050 píxeles? Idealmente, queremos usar el mismo tamaño, así que voy a usar, con altura actualmente eso es 60 veces 60 píxeles. Creé los marcos de la hoja de cálculo a un tamaño de 80 veces 80 píxeles Dibujemos las imágenes del mismo tamaño sobre lienzo. El tamaño del enemigo será de 80 píxeles. estamos estableciendo el ancho y la altura del Aquí estamos estableciendo el ancho y la altura del enemigo al tamaño del enemigo. Y entonces estamos usando ese ancho y alto en el método de imagen de dibujo lateral aquí. Ahora estamos recortando exactamente un fotograma de la imagen de origen Y lo estamos dibujando al mismo tamaño de 80 veces 80 píxeles sobre lienzo en lugar de coordenada de codificación dura 00. Aquí los convertiremos en variables. Los pondré en la subclase, porque cada tipo de enemigo podría tener un número diferente de marcos En el marco de la hoja de cálculo X manejará la navegación horizontal alrededor de la hoja de sprite En este caso, por cómo estructuré esta particular lámina spry, podemos ver que esto nos dará una animación de distorsión y explosión que se reproducirán cuando golpeemos el marco enemigo Y manejará la navegación vertical alrededor de la hoja de sprites En este caso, eso nos dará diferentes variaciones del morfo del escarabajo Puedes ver que creé alguna variedad. Creé cuatro versiones con diferentes alas, diferentes armaduras y diferentes garras Es sólo para hacer que el juego sea un poco más interesante visualmente. Se puede ver que cada enemigo se distorsionará de manera ligeramente diferente Y mostraremos distintos fotogramas de explosión cuando los golpeemos con el proyectil Podría haber suavizado la animación de explosión muchos fotogramas de animación como siempre lo hago en mis otras clases. Pero hoy quiero mostrarte cómo mejorar rendimiento con esta técnica de animación de sprites, donde utilizamos solo un número limitado de fotogramas en cada hoja de sprites Nos va a dar un poco de efecto retro. Y se verá bien porque las animaciones reaccionarán a las prensas de teclas y a los eventos del juego. Déjame mostrarte a lo que me refiero. Estos son argumentos fuente X y fuente Y pasados para dibujar el método de imagen. Determinan las coordenadas X e Y del área de cultivo. Actualmente, estamos recortando un rectángulo de 80 veces 80 píxeles A partir de las coordenadas 00, puedo saltar alrededor de la hoja de sprites por w. y altura como esta, cero veces ancho, cero veces alto sigue siendo este fotograma desde la coordenada 00. cero veces ancho, cero veces alto sigue siendo este fotograma desde la coordenada 00 Si digo una vez altura, aquí estamos cayendo desde la coordenada 080 Aquí podemos ver la segunda fila en la hoja de sprites con una variación enemiga diferente, dos veces la altura recortará a partir de aquí Al ver a otro escarabajo transformarse, tres veces la altura nos dará el último Puedo reemplazar este valor con marco Y. Fuente X se moverá alrededor de la hoja de sprites horizontalmente Una vez ancho nos dará esta área de recorte. Dos veces el ancho nos dará este marco. Si lo aumento a tres, estamos fuera de la hoja cálculo y estamos viendo un marco en blanco. Seguirá funcionando y nada se romperá, pero simplemente no vemos nada. Voy a usar frame x variable aquí. Ahora sabemos ese argumento fuente y, este código determina un rol. Bueno, si quiero que cada objeto enemigo sea asignado aleatoriamente, una de las cuatro imágenes disponibles, una de las cuatro variaciones disponibles de transformación de pétalos. Puedo hacer eso simplemente configurando frame y en valor aleatorio 0-4 No hay Road 3.5 por ejemplo, así que voy a necesitar enteros Lo envuelvo en piso de máscara para redondear el número hacia abajo. Ahora bien esta línea de código nos da cero o uno, o dos, o tres. Podemos ver que estamos recibiendo morfos de escarabajos aleatorios en la ola Perfecto, Eso es navegación vertical. Ahora quiero usar también la navegación horizontal alrededor de la hoja de cálculo Y quiero tocar los frames así. Cuando el enemigo es alcanzado por un proyectil, subimos a la clase enemiga padre Porque este comportamiento será compartido entre todos los tipos de enemigos. Le daremos un método personalizado. Yo llamo por ejemplo hit que tomará como argumento el valor del daño. Yo lo hago así porque a lo mejor vamos a tener armas que golpeen por más daño. Posteriormente, tendremos tipos de enemigos con más de una sola vida que necesitan ser golpeados varias veces. En su interior disminuyo vidas del enemigo por los daños. Cada tipo de enemigo tendrá un número diferente de vidas. Beetle Morph es un enemigo sencillo, básico, suave y con una sola vida Pongo esa propiedad aquí en la subclase. También necesitamos definir Max Frame. En este caso tenemos marcos 01.2 Max Frame aquí es dos. También crearé una propiedad a la que llamo Max Lives y la pongo en el valor inicial de vidas lejanas. Lo hacemos porque aunque la vida del enemigo actual pudiera ser cero y el enemigo fuera destruido, quiero que el objeto enemigo recuerde su número máximo original de vivos, para su número máximo original de vivos, que podamos recompensar la misma cantidad de puntos de puntuación una vez que derrotemos a ese enemigo. Escribiremos ese código en un minuto. Aquí arriba, cuando proyectil activo choca con un enemigo, establecemos marcado para su eliminación en ese enemigo a true Quiero cambiar esta lógica. En lugar de eliminar inmediatamente al enemigo, llamaremos a este método de golpe de punto que acabamos de definir. Le paso un daño de uno así. Por ahora. Dentro del método de golpe, estamos reduciendo vidas de este enemigo por este valor de daño. Si recuerdas, después de eso creo una declaración si separada y digo si esta vida es menos de una, una vez golpeamos al enemigo suficientes veces como para agotar todas sus vidas, que en este caso fue muy fácil porque cada morfo de escarabajo tiene una sola Quiero activar la navegación horizontal alrededor de la hoja de sprites para reproducir esta animación Este marco de punto X plus plus. Cada vez que aumentamos el fotograma X en uno, verificamos si el fotograma actual X es mayor que el cuadro máximo. Comprobamos si jugamos todos los fotogramas horizontales en la hoja de sprites, solo después de que golpeamos al enemigo, agotamos todas sus vidas Y después de que jugamos todos los fotogramas horizontales de la hoja de sprites, sólo entonces estamos listos para establecer marcado para su eliminación en true Lo que filtrará al enemigo fuera de la matriz del enemigo. Debido a la lógica que escribimos anteriormente, proceso automático de recolección de basura eventualmente lo eliminará como lo hace con todas las variables a las que ya no se hace referencia en ninguna parte del código. También cortaré esta línea de código sólo una vez que destruyamos al enemigo. Si el juego terminado es falso y el juego sigue funcionando, aumentamos la puntuación por la cantidad original de vidas enemigas. Por esta vida máxima, los enemigos que tenían una vida darán un punto de puntuación. Tendremos otro tipo de enemigo con tres vidas después, esa nos dará tres puntos al ser destruidos. Aquí abajo podemos ver que le damos una vida a Beetle Morph, así que recompensará solo un punto de puntuación El código, puedo ver que los fotogramas horizontales están jugando, pero están jugando muy rápido. Apenas podemos darnos cuenta de lo que está pasando. Estamos animando probablemente a 60 fotogramas por segundo por cada fotograma de animación Estamos aumentando el fotograma X en uno, y solo tenemos tres cuadros. Toda esta animación se desarrolla en una fracción de segundo. Necesitamos una forma de controlar la velocidad de la animación de sprites. Puedo comentar los rectángulos alrededor de los enemigos, Esto se ve mejor Quiero que las ondas se muevan más despacio, tal vez un píxel por fotograma de animación. También quiero que las olas comiencen desde el medio horizontalmente. La coordenada x inicial de la ola será el ancho del juego por 0.5 menos la mitad del ancho de la ola. Quiero que cada onda se mueva aleatoriamente hacia la izquierda o hacia la derecha desde esa posición media. Voy a hacer un pequeño truco aquí. La velocidad horizontal es igual a una expresión, llamado operador ternario, el único operador Java Script con tres operandos Lo usaremos aquí como una simple declaración L de una línea. Si método aleatorio es menor que 0.5 aproximadamente en 50% de los casos establece la velocidad x a menos una L establecer la velocidad x dos más un operador ternario usado como una declaración L va como esta expresión para evaluar si es cierto signo de interrogación, haga esto L dos puntos, haga eso También podríamos haber usado una sintaxis FL regular aquí, pero esto es más rápido. Ahora bien, si recargo el proyecto algunas veces, puedo ver que las ondas comienzan desde el medio, y aleatoriamente se moverán hacia la izquierda o hacia la derecha a una velocidad de un píxel por fotograma de animación Cuando golpeamos a un enemigo, los marcos horizontales juegan, pero juegan muy rápido. ¿Cómo podemos controlar esa velocidad de animación? Déjame mostrarte. 13. Temporización de animación: Construido en solicitud. El método de marco de animación que estamos usando aquí tiene dos características especiales. El primero es que ajusta automáticamente la velocidad a la que sirve el siguiente fotograma de animación. ajusta a la frecuencia de actualización de la pantalla porque no tiene sentido calcular y dibujar más fotogramas de los que la frecuencia de actualización del monitor de su computadora le permite mostrar. Dibujar esos marcos extra sería una pérdida de rendimiento porque de todos modos no podríamos verlos. La segunda característica especial es que genera automáticamente una marca de tiempo que simplemente es un valor de milisegundos que pasaron desde que se al primer fotograma de animación de solicitud llamó al primer fotograma de animación de solicitud en la secuencia Usaremos esas marcas de tiempo generadas automáticamente para activar eventos periódicos en nuestro juego. Por ejemplo, puedo actualizar el marco de sprite horizontal en hoja de sprites enemigos solo una vez cada 500 milisegundos, que serían dos fotogramas Podremos establecer cualquier velocidad de fotogramas que queramos. Déjame mostrarte cómo hacer eso. Vamos a necesitar un help er let variable que llamo la última vez. Esta variable contendrá un valor de una marca de tiempo del bucle de animación anterior. Para que podamos compararlo con la marca de tiempo de este bucle de animación. Y podemos calcular cuántos milisegundos pasaron entre los dos fotogramas de animación Normalmente llamamos a este valor Delta Time request. Animation frame auto genera una marca de tiempo y pasa ese valor para animar la función aquí Todo lo que necesito hacer para usar ese valor es asignarle un nombre de variable. Yo lo llamaré marca de tiempo aquí. Ahora bien, si consolog esta marca de tiempo desde dentro de Animate, verás cómo cuenta milisegundos desde que comenzó el primer bucle 1 segundo son 1,000 milisegundos. tiempo delta es el número de milisegundos que tarda nuestra computadora en servir un fotograma de animación Es la diferencia entre la marca de tiempo de este bucle de animación menos la marca de tiempo del bucle de animación anterior. Una vez que calculamos el tiempo delta, estableceremos la última hora en la marca de tiempo de este bucle de animación para que pueda usarse como la antigua marca de tiempo en el siguiente bucle de animación. A medida que el bucle de animación se ejecuta y el cuadro de animación de solicitud está generando nuevas marcas de tiempo, siempre estamos comparando la marca de tiempo actual y la antigua marca de tiempo del bucle anterior y esa diferencia entre ellas que nos da delta time. Vamos a necesitar ese valor de tiempo delta dentro del método render. Lo paso como argumento aquí en la clase de juego principal dentro del render, me aseguro de que se espera el valor. Voy a consulado delta tiempo de aquí para asegurarme de que funcionó. Si me desplazo todo el camino hacia arriba, puedo ver que los dos primeros valores de delta time no son, no un número. Esto podría ser un problema dependiendo de cómo planeemos usar el valor de tiempo delta. Este no aquí podría romper nuestro código. Potencialmente, no estamos obteniendo ninguno porque el primer bucle de animación se activa aquí, pero ese no tiene valor de marca de tiempo autogenerado La marca de tiempo solo se genera cuando el segundo bucle se activa aquí por el marco de animación de solicitud. Para el primer bucle, la marca de tiempo es indefinida y estoy calculando delta time undefined menos uno, lo que me está dando ninguno, no un valor numérico. Puedo arreglarlo simplemente pasando cero como primer sello de tiempo. partir del siguiente ciclo, comenzaremos a obtener marcas de tiempo autogeneradas adecuadas Ahora cuando me desplace hasta aquí, obtenemos números todos llegan hasta aquí. Como pueden ver, mi tiempo delta es de alrededor de 16.6 milisegundos mi computadora le toma alrededor de 16.6 milisegundos actualizar y dibujar un fotograma de animación 1,000 milisegundos divididos entre 16.6 es alrededor de 60. Puedo ver que estoy obteniendo velocidad de animación de 60 fotogramas por segundo. Si tienes alguna pantalla de juego de alta frecuencia de actualización, posible que estés obteniendo un tiempo delta mucho menor, lo que también significa que todo tu juego corre más rápido que el mío. Porque como dijimos antes, método de fotograma de animación de solicitud se ajusta automáticamente a las capacidades de frecuencia de actualización de su pantalla. Podemos controlar el FPS de todo el juego ahora aquí simplemente diciendo solo call render para actualizar y dibujar el siguiente frame del juego. Si, por ejemplo, han pasado 50 milisegundos, lo que nos daría 1000/50 es 20, nos daría una velocidad de animación de Básicamente lo que estoy diciendo aquí es que ahora podemos usar este valor de tiempo delta para desencadenar cualquier evento periódico en nuestro código. El tiempo delta es milisegundos. Entonces podemos decir hacer algo cada diez milisegundos, hacer algo cada 100 milisegundos Podemos hacer lo que queramos. Podría ser actualizar todo el juego cada 50 milisegundos o algo más. Podemos agregar un jefe, un power up o un enemigo especial cada 15 segundos. Eso también funcionaría. Ahora te mostraré cómo usar Delta Time para actualizar periódicamente los marcos de hoja de cálculo en nuestra base de códigos. Porque queremos ralentizar la animación que se reproduce después de que golpeamos al enemigo con un proyectil También usaremos esto más adelante para animar otras cosas de nuestro juego en este estilo retro de baja velocidad de fotogramas que nos permite tener hojas de cálculo con solo unos pocos fotogramas para obtener un buen impulso de rendimiento También nos permite cronometrar las cosas por separado. En nuestra base de código, tengo movimientos de jugadores, proyectiles y enemigos animando a 60 FPS completos Pero la velocidad a la que la propia hoja de cálculo intercambia cuadros operará a su propia velocidad separada Déjame mostrarte cómo implementar eso. Subo a la clase principal del juego aquí, dentro del método render, borro este consolo Incluso un consolo como este en nuestro juego afectaría negativamente el rendimiento Es bueno usarlo para los bugging, pero no olvides eliminar siempre tu consolo, especialmente los que se ejecutan para cada fotograma de animación como este Quiero tener una variable flag aquí en mi clase de juego principal. Esta bandera de actualización de sprites siempre será falsa. Sólo cambiará a true una vez cada, digamos 500 milisegundos. Cambiará a true para un fotograma de animación. Permitirá que todas las hojas de sprites de nuestro juego se actualicen, luego volverá a cambiar para volver a caer hasta que pasen los próximos 500 milisegundos. Quiero actualizar todos los marcos de sprite en nuestro juego periódicamente Digamos que cada 500 milisegundos, podemos cambiar fácilmente ese valor de milisegundos a otra cosa Posteriormente necesitaremos dos variables de ayuda. El temporizador de Sprite contará entre cero e intervalo de sprites. Cada vez que alcanza el intervalo de sprite, establecerá la actualización de sprite en true, y el temporizador se restablecerá a cero para que pueda contar nuevamente para el siguiente disparador periódico Yo me encargaré de la lógica de sincronización de sprites. Aquí digo si el temporizador de sprites es más que intervalo de sprite, haz otra cosa Haz otra cosa. Si el temporizador de sprite acumuló suficiente tiempo delta que ahora es más de 500 milisegundos, configuramos sprite update Reiniciamos el temporizador de sprite a cero para que pueda contar de nuevo. actualización de Sprite seguirá siendo verdadera solo para un solo fotograma de animación, ya que en el siguiente bucle, cuando se ejecute la sentencia L, inmediatamente la volverá a establecer en false Esta instrucción L significa que temporizador de sprite es menor que el intervalo de sprite En ese caso, queremos que el temporizador siga acumulando tiempo delta hasta que acumulemos más de intervalo Nuevamente, debido a que el tiempo delta es la cantidad real de milisegundos, estos eventos periódicos se dispararán en el mismo En pantallas lentas y rápidas renovadas será casi lo mismo No va a ser exactamente lo mismo porque cuando restablezco el temporizador de sprite de nuevo a cero aquí, en la mayoría de los casos habrá un poco de tiempo delta extra que no estoy contabilizando aquí, lo que creará alguna diferencia me parece bien. Si desea una precisión completa, tome el tiempo delta que supere el valor del intervalo de 500 aquí y asigne ese valor sobrante a la hora de inicio En lugar de volver a poner el temporizador a cero, eso debería hacerlo aún más preciso. No creo que necesite ese nivel de precisión para mi juego en estos momentos. Quiero evitar cálculos innecesarios por ahora. Para obtener el máximo rendimiento, ahora tenemos esta propiedad de actualización de sprites que una vez cada 500 milisegundos, solo por un fotograma de animación, se establece El resto del tiempo se establece en false. Podemos usarlo para activar la animación de sprites cuando golpeamos al enemigo con un proyectil y cuando las vidas del enemigo son inferiores En lugar de aumentar el fotograma X en la hoja de cálculo inmediatamente Sólo lo haremos si la actualización de sprites es verdadera. Vamos a probarlo. Disparo a un enemigo y cada 500 milisegundos reproduce el siguiente fotograma de animación perfecto Ahora tenemos el problema de que el enemigo está deteniendo proyectiles hasta que la animación terminó Una vez que golpeamos al enemigo y su animación de explosión comience a jugar lentamente, quiero que el siguiente proyectil ignore a ese enemigo, vuele a través de él y golpee al enemigo detrás de él Lo hago solo comprobando colisión entre proyectiles y enemigos Si ese enemigo tiene más de cero vidas. Así, tenemos un control completo sobre la velocidad de animación de propagación. Ahora puedo cambiarlo aquí abajo, Digamos que quiero que se animen un poco más rápido Ahora animan rápido, pero sigue siendo lo suficientemente lento para veamos los fotogramas individuales, que es lo que estaba buscando Normalmente, cuando creo mis hojas de sprites, creo bucles de animación complejos con 20 o 30 fotogramas de animación Pero a veces si tienes muchos enemigos animando en la pantalla al mismo tiempo, puede ralentizar tu juego usando hojas de sprites con menos fotogramas y controlar qué tan rápido cambiamos de frame a frame al animar estas hojas de sprites es una forma sencilla y amigable con el rendimiento cómo lidiar 14. Animación del jugador: Quería cambiar el color de los láseres de proyectiles a, por ejemplo, oro Si pongo el estilo de Phil así, cambia todo el estado del lienzo. Ahora todas las formas sobre lienzo que usen Phil usarán ese color dorado. Cambié el ancho a cuatro píxeles, tal vez sólo tres. Sí, eso se ve mejor. Quiero restringir este estilo Phil solo a este rectángulo de Phil. Cole, voy a envolver este bloque de código entre los métodos safe y restore. De esa manera cualquier código que se asiente fuera esta área no se verá afectado por este estilo de relleno los láseres son dorados, todo lo demás se queda blanco Si vuelvo aquí a la clase de morph de escarabajo y pongo vidas en tres, tengo que golpear a cada enemigo tres veces para destruirlo Cada uno nos recompensa con tres puntos de puntuación. Todo está funcionando como se pretendía. Lo volveré a poner en uno. Tengo un plan especial para enemigos que tienen más de una vida. Porque quiero dibujar y animar ese daño paso a paso, cada vez que los golpeamos para reducir sus vidas Llegaremos a esa parte más adelante. Es momento de dibujar y animar al jugador. Lo que quiero hacer es que el jugador reaccione cuando dispara un proyectil láser Más tarde podría hacer también ataques avanzados. Estoy pensando en algo así. Y esto. Observe cómo la nave espacial del jugador se anima de manera diferente para cada tipo de ataque diferente Al mismo tiempo, también quiero animar jets de jugador, los cohetes de propulsión, cuando nos movemos a la izquierda, ¿ Y cuando estemos de pie, los cohetes de propulsión se animarán para reflejar Quiero que la animación de disparo reaccione cuando presionamos un botón caliente. Y quiero que los jets reaccionen completamente independientemente de eso. Cuando presionamos las teclas de movimiento, ¿cómo hacemos que la animación del jugador reaccione a dos conjuntos diferentes de entradas independientes? Déjame mostrarte, puedes descargar todos los activos artísticos en la sección de recursos. A continuación traeremos jugador PNG y jugador underscore jets, PN G al proyecto y les daremos identificaciones de jugador y jugador jets Así, los escondo con CSS usando sus ID's. Yo guardo los cambios que acabo de hacer en todos mis archivos. Aquí traeremos en la hoja de sprites del jugador principal como esta propiedad de imagen Y apuntamos el script Java hacia ese elemento de imagen usando su ID. Dentro de nuestro método de dibujo personalizado, puedo usar el método de imagen de dibujo incorporado. Nuevamente, para dibujar la nave espacial sobre lienzo. Ya sabemos que necesitamos pasarle al menos tres argumentos. La imagen que queremos dibujar y las coordenadas x e y. ¿Dónde dibujarlo? Esto dibujará toda la hoja de sprites, los cuatro fotogramas. Ajusto el ancho del jugador 240 píxeles y el alto 220 para que coincida con el tamaño del cuadro. Ajusto la velocidad del jugador a cinco píxeles por fotograma de animación. Necesitaremos la propiedad frame X para navegar alrededor de la hoja de sprites horizontalmente No necesitamos el marco Y para la navegación vertical esta vez porque esta hoja de sprites tiene solo una fila También ya aprendimos que podemos pasar el método draw image, opcional width y height. Y exprimirá toda la hoja de sprites, en este caso los cuatro marcos al área de un fotograma Queremos recortar solo ese fotograma a la vez. Por lo que necesitaremos usar la versión más larga del método draw image. Agrego fuente x, fuente Y, ancho de origen y altura de origen, y los valores que pasamos para dibujar el método de imagen ya que estos argumentos determinarán qué área queremos recortar de la imagen de origen. Este fotograma sería desde la coordenada 00 en la imagen hasta el ancho y alto del jugador, lo que corresponde al ancho y alto de un solo fotograma en mi hoja de sprites Me gusta crear mis activos artísticos del mismo tamaño en el que se mostrarán en el juego. Al hacer esto, asegúrate de que tus imágenes sean lo suficientemente nítidas y detalladas, pero no sean más grandes de lo que deben ser. Quiero navegar por la hoja de cálculo. Volveré a usar el argumento source x para definir el recorte horizontal en coordenada cero veces el ancho de un solo fotograma. Es este marco una vez ancho es esta área, dos es esta, y tres es esta. Usaremos algunos de estos más adelante, probablemente para súper armas que quiero implementar. Voy a comentar el rectángulo blanco. Ya no lo necesitamos a menos que queramos mostrar los límites exactos de colisión de jugadores, hit box. Yo sustituyo esto con propiedad de marco X. Y ahora puedo cambiar qué marco en los jugadores hoja de sprites se muestra aquí En esta área, manejaré la lógica de los marcos de sprites en su interior. Compruebo si se presionó el botón de ataque. En nuestro caso, comprobamos si esta matriz de teclas de punto juego de puntos, que estamos accediendo a través de la referencia del juego didot aquí Y se sienta en el objeto principal del juego aquí. Cuando se presiona una tecla, agregamos a la matriz. Cuando se libera, lo eliminamos de la matriz. Si la matriz de teclas contiene el número uno, lo que significa que estamos manteniendo presionada la tecla número uno en el teclado, fotograma X será uno. fotograma X es cero, estamos saltando entre estos dos fotogramas mientras presionamos el botón de ataque básico. Vamos a probarlo. En realidad, voy a dibujar los proyectiles detrás del jugador intercambiando aquí estas líneas de código Ahora si disparo, obtenemos un sutil movimiento y animación del cañón láser encima de nuestra nave espacial intergaláctica Usaré el fotograma dos para otro ataque especial un poco más tarde, pero podemos probar la animación aquí. Realicé el último fotograma para ataque súper láser. Realmente no funciona bien con el proyectil básico. Probablemente haremos uso de todos estos marcos en una sección bonus de la clase si quieres que lo haga. Por ahora, solo tenemos un simple proyectil láser básico. Y la hoja de cálculo del jugador reacciona y anima. Cuando nosotros como usuario presionamos el botón de ataque, animaré a los jets del jugador, los cohetes de propulsión Quiero que reaccionen ante un conjunto diferente de entradas. Se animará dependiendo de la dirección del movimiento, dependiendo de qué tecla de movimiento se presione La forma más fácil de hacerlo es dibujar esos jets como una imagen separada y podemos ponerla en capas junto con el jugador. Déjame mostrarte a lo que me refiero. Ya incluimos player jets image en index HTML y le pegamos con CSS. Lo haré referencia aquí con Javascript. Como esta imagen de guiones subrayan, copio este código de imagen de dibujo y también voy a dibujar la imagen de jets como esta Parece que Javascript no puede encontrar la imagen. Comprobemos el índice HTML. Oh sí, le di una idea de los jets de subrayado de jugador. Ahora estoy dibujando este marco. Voy a crear una propiedad. Voy a llamar por ejemplo, marco Jets. Servirá el mismo propósito que el marco X para la hoja de propagación de los jugadores. Utilizaremos el marco Jets para la navegación horizontal alrededor de la hoja de cálculo. Lo usaré aquí dentro del argumento fuente x. Ahora jets frame es cero y vemos este marco. Cuando jets frame es uno, vemos este marco. Y cuando son dos, vemos este marco. Comenzaremos desde jets frame one porque ese es el estado básico medio dentro del método de actualización. Ya estamos haciendo un seguimiento de las pulsaciones de tecla flecha izquierda y flecha derecha. Ampliamos este bloque de código aquí. Cuando nos movemos a la izquierda, jets marco será cero. Yo lo pruebo. Sí, L. Si nos movemos a la derecha, los jets del marco serán dos a la izquierda, a la derecha, a la izquierda, a la derecha. Sí, también agregaré declaración L si no se presionan la flecha izquierda o la flecha derecha. Ponemos el marco de los jets a uno. Bien, esta es una forma de cómo puedes animar un personaje para que reaccione a múltiples conjuntos independientes de entradas Simplemente divídalo en múltiples capas que reaccionen por separado. Lo que hicimos aquí también es genial para el rendimiento. Solo tenemos unos pocos fotogramas de animación, pero se siente muy reactivo y animado porque responde instantáneamente. Si estás codificando, puedes probar la jugabilidad tú mismo y ver cómo se siente. ¿Prefieres cuando hago secuencias de animación complejas como suelo hacer para mis otros juegos con 30 fotogramas de animación por bucle? ¿O te convencí de probar a veces esta técnica de animación más amigable con el rendimiento que utiliza solo un número mínimo de fotogramas? Voy a crear una ola más grande dos veces ocho enemigos. Y voy a dejar que desencadene un juego terminado al agotar la vida de los jugadores. Observe que cuando los enemigos golpean al jugador y son destruidos. No reproducen la animación de explosión como deberían. Manejamos colisiones entre enemigos y el jugador aquí, no quiero marcar de inmediato a ese enemigo colisionante para su eliminación Borro esa línea de código y también voy a desechar esto. No quiero reducir la puntuación cuando el jugador choca con un enemigo En cambio, solo pondré vidas enemigas a cero cuando golpeen al jugador. Esto activará el bloque de código en el siguiente bucle. Ahora tengo otro problema. Porque golpear a un enemigo le quita la vida a los tres jugadores, porque estamos colisionando con ese enemigo que ya tiene cero Y reduce la vida de un jugador por fotograma de animación. Ese enemigo tiene cero vidas, pero sigue ahí dentro. Es animar la secuencia de explosión y estamos colisionando con Solo quiero verificar colisión entre jugador y enemigos si ese objeto enemigo tiene más de cero vidas. Ahora los enemigos están animados y cada colisión enemiga simplemente le quita la vida a un jugador. Puedo presionar R para reiniciar el juego. Aumentaré el número de proyectiles en el pool de objetos a 15, así que no nos quedamos con tanta frecuencia ralentizaré la animación de sprites, tal vez sirva al siguiente fotograma de animación tal vez sirva al siguiente fotograma de Una vez cada 150 milisegundos aquí donde manejamos condición suelta uso u operador y digo si este juego dot player dot lives es menos de un juego de gatillo más de aquí Y en ese caso no necesitamos este código. Tampoco quiero que los enemigos sean eliminados cuando toquen el borde inferior del área de juego. También borro esto. la ola enemiga inicial a una vez una. Aquí arriba en la clase de jugador, voy a crear la propiedad max lives. Serán, por ejemplo, diez. Dentro del método de texto de estado de dibujo. Copio esto para bucle que dibuja vidas y primero dibujaré max lives. vidas potenciales máximas serán solo rectángulos de trazo vacíos como este Los hago más grandes, tal vez diez veces 15 píxeles aquí también. Espaciado de 20 píxeles aquí. Y aquí. Quiero que el ancho de línea sea solo de un píxel, que también es el valor predeterminado en lienzo. No necesito declararlo en absoluto, y este es el visual que quería. Cuando derrotamos una ola, obtenemos una vida extra y rectángulo de trazo se convierte en un rectángulo de campo. Podemos ver fácilmente que actualmente tenemos cinco de un máximo potencial de diez vidas. También necesito ir aquí a la línea 247. Cuando se activa una nueva ola, solo le daremos al jugador una vida más si las vidas actuales de los jugadores son menores que las vidas máximas de los jugadores. Implementamos muchas características y aprendimos muchas técnicas nuevas. Tenemos un juego totalmente jugable con dificultad creciente a medida que llegan olas grandes y grandes A medida que avanzamos en el juego, ¿qué debo implementar a continuación? Batallas de jefes, súper armas, tipos de enemigos. ¿Qué tal enemigos que tienen múltiples vidas y múltiples estados dañados? Hazme saber en los comentarios si lo codificas todo el camino aquí, y qué características de bonificación te gustaría que implementara a continuación de las que te sugerí, o puedes darme tus ideas originales. ¿Hay alguna característica en el juego original de Space Invaders que me olvidé y te gustaría ver en nuestra versión Haz click como si encontraste algún valor. Y echa un vistazo a la descripción del video si quieres hacer más juegos conmigo. 15. Características adicionales: enemigos con armadura: Lo que hace que los enemigos sean interesantes en un juego de dos días como este, pueden ser cosas como sus propiedades de comportamiento y también sus visuales. Las diferentes combinaciones de tipos de enemigos en nuestros juegos deberían crear diferentes situaciones y chal***ges para que el jugador pueda lidiar Hoy diseñaremos e implementaremos un tipo de enemigo único y lo haremos parte de nuestro juego. Bien, parece que estamos en algún lugar profundo en el espacio, explorando el vacío interestelar similar al Cinturón Kiper en nuestro sistema solar Esta parte desconocida del espacio tiene un enorme cinturón de asteroides. Estos asteroides son en su mayoría solo trozos sólidos de hielo y polvo Parece que estas formas de vida alienígenas están bien equipadas para sobrevivir en el espacio vacío y tienen un espeso exceso de esqueleto para protegerlas en caso de colisión con un asteroide Interesante, ¿cómo vamos a superarlos? Calibraré nuestros láseres a una frecuencia más alta. Debería poder quebrar sus conchas, pero podría necesitar múltiples golpes bien apuntados. Perfecto. Vamos a hacerlo. 16. Clase de enemigos rinomorfos: Creé cuatro variaciones visuales para la nueva clase enemiga rinomorfa Si miras de cerca la hoja de sprites, notas que todos se dañan de diferentes maneras Mi objetivo es básicamente conectar su marco de sprites con su salud Digamos que esta columna en la hoja de sprites es el enemigo en plena salud Tendrá cuatro vidas. Cuando lo golpeemos una vez, mostrará este marco. Este es el enemigo en dos vidas, una vida, y el golpe final jugará esta animación. Como de costumbre, puedes descargar todos los activos artísticos en la descripción a continuación. Si en este momento solo estás uniéndote a mí para este proyecto, primera parte también está vinculada en la descripción. Puse el archivo RhinomorphPNG en mi carpeta de proyectos. Lo incluyo aquí en mi archivo HTML índice. Y le doy una identificación de rinomorfo. Yo uso su ID para ocultarlo con CSS porque solo queremos dibujarlo con Javascript sobre lienzo. Posteriormente ya aprendimos sobre la subclase en Java Script Sabemos que el enemigo es la clase padre, también llamada súper clase. Esta clase contiene todas las propiedades y métodos compartidos entre todos los diferentes tipos de enemigos. Entonces usamos la palabra clave especial extense create Beetle Morph child class, también llamada subclase Esta clase contiene solo propiedades y métodos específicos solo para un tipo de enemigo en particular. Por ejemplo, si golpeamos Beetle morph enemigo con un proyectil, estamos disparando un método de golpe, pero el Javascript no encontrará ese método en La palabra clave extends configura la cadena de prototipos Javascript estándar detrás de escena. Javascript sabe que si no puede encontrar un método en la subclase, irá automáticamente a buscarlo en la superclase, en la clase padre, seguirá la cadena prototipo hasta que encuentre el Ese método hit en particular se encontrará aquí Tenemos toda la lógica en su lugar y entendemos cómo funcionan las clases padre e hijo en el script Java. Usemos ese conocimiento para implementar otro tipo de enemigo con propiedades e imágenes únicas Copio este bloque de código y lo renombraré a Rhinomorph Señalamos que es esta propiedad de imagen de punto a esa nueva hoja de rhinomorphspriadsheet que acabamos de incluir Esta hoja de cálculo tiene 012345 marcos horizontales, cuadro máximo será de cinco, marco Y se mantendrá igual porque esta hoja de cálculo también tiene cuatro filas, lo mismo que el esta hoja de cálculo también tiene cuatro filas, el cuadro máximo será de cinco, el marco Y se mantendrá igual porque esta hoja de cálculo también tiene cuatro filas, lo mismo que el de escarabajo morph. La morph de rinoceronte tendrá cuatro vidas. Tendremos que golpearlo varias veces para destruirlo. Lo importante aquí es esta propiedad max lives. Se crea cuando creamos este objeto enemigo y siempre se sostiene. Y recuerda el valor inicial de este punto vive incluso cuando el juego está corriendo y el enemigo Rhinomorfo tiene actualmente, por ejemplo, sólo dos vidas porque ya le pegamos antes Esta propiedad de dot max lives siempre se mantendrá. Y recuerda ese valor inicial de cuatro, ese valor máximo, necesitaremos todo esto en un minuto. Así que trata de recordar esto por favor. Bien, entonces tenemos una nueva subclase. ¿Cómo agregamos este nuevo tipo de enemigo al juego? Voy aquí abajo a nuestra clase de onda personalizada. Esta clase crea un grupo de enemigos y los organiza en una cuadrícula como formación aquí abajo en el interior del método de creación en la clase de onda. Primero solo voy a verificar si todo funciona reemplazando la morfa de escarabajo por rinomorfo Bien, podemos ver Rhinomorphow ¿ qué pasa si le pego? Le dimos cuatro vidas, así que necesito golpearlo varias veces. Y cuando agotemos todas sus vidas, reproducirán sus fotogramas de animación restantes Actualmente, este tipo de enemigo está usando la misma lógica que el primer enemigo simple. Le pegamos una y otra vez hasta agotar sus vidas. Y luego jugará el resto de la hoja de cálculo. Me gustaría que la hoja de cálculo intercambiara fotogramas cada vez que la golpeamos, para mostrar el daño gradual que le estamos haciendo por nuestros láseres de alta frecuencia Para ello, necesitamos aprender una cosa más sobre extender las clases de Javascript. Estas son mis subclases y ambas extienden esta superclase enemiga cuando golpeamos a un enemigo, este método desde la línea cien y 27 carreras Quiero que las cosas permanezcan igual que para la clase de escarabajo morph, pero quiero agregar algo más de lógica aquí para la clase rinomorfo Una cosa que puedo hacer es copiar este método y agregarlo aquí en rinomorfo De esa manera cuando se activa el método en rinomorfo, Javascript lo encontrará directamente en esta clase y ya no irá buscándolo arriba en la clase enemiga Básicamente, estoy anulando ese método para la morph de escarabajo. Seguirá siendo igual que antes. No hay método hit en esta subclase. Javascript seguirá refiriéndose a ese método de golpe original en la clase enemiga padre. Para Rinomorfo, estamos anulando. Método de golpe original aquí en la línea 154. Ahora puedo simplemente poner cualquier lógica adicional dentro del método hit en la clase rhinomorph Esta lógica sólo se aplicará a este tipo de enemigo en particular. Quiero adjuntar y conectar de alguna manera el número de fotograma en Rhinomorphspridesheet al las Cuando golpeemos a Rhinomorph, estableceremos frame para vidas máximas menos sus vidas actuales Max vive es cuatro. Como sabemos, le pegamos una vez. Cuatro menos tres es uno. El marco X es uno. Volvemos a golpearlo. Sus vidas actuales son 24, menos dos son dos. El marco X es dos. Volvemos a golpearlo. Las vidas actuales son 14, menos una es tres. El marco X es tres. Espero que tenga sentido. Yo lo pruebo. Le pegué una vez. Vemos este marco. Volví a pegarle. Vemos este marco, lo volví a golpear. Vemos este marco. Le pegué una vez más. Y el resto de la hoja de cálculo correrá perfecto. Jugué el juego por un tiempo y se puede ver que obtenemos al azar cuatro variaciones de la clase enemiga de transformación rinoceronte. Y todos se dañan paso a paso. A medida que intercambiamos marcos de hoja extendida golpeándolos, podría implementar otros tipos de armas más adelante. Y este valor de daño podría no ser siempre un entero, puede que no siempre sea un daño por golpe. Este valor de daño tiene decimales. De hecho, rompería esta lógica que intercambia marcos de sprites Para evitar eso, voy a envolver este punto vive en matemáticas punto piso para redondear hacia abajo al entero inferior más cercano. Y ahora estamos a salvo aunque tengamos algún tipo de arma láser que inflija daño en valores menores o mayores por cada golpe. Incluso en ese caso, la lógica de intercambio de marcos aquí seguirá funcionando Ahora me divertí creando las hojas de sprites, y estaba tratando de encontrar cuatro formas diferentes en que los proyectiles enemigos pueden agrietarse mientras los dañamos Paso a paso, implementamos este nuevo tipo de enemigo, y probablemente fue más fácil de lo que pensabas que sería. Me gustaría escuchar ahora tus ideas de diseño. ¿Puedes llegar a otro tipo de enemigo con una característica única si quieres? También puedes darle un nombre a ese tipo enemigo. Si se te ocurre algo realmente bueno, en realidad podría diseñarlo, implementarlo, y lo lanzaré como un episodio extra en esta clase. Ahora quiero que las oleadas de enemigos estén hechas de escarabajos y algunos morfos de rinoceronte Y quiero poder controlar qué porcentaje de la ola está hecho de qué tipo de enemigo está hecho. Para ello, voy aquí dentro de nuestra clase de onda personalizada. Aquí en la línea 193, estamos empujando solo morfos de rinoceronte Ahora mismo voy a hacer este método truco aleatorio de nuevo. Ya lo usamos para decidir aleatoriamente si la ola primero comenzará a moverse hacia la izquierda o hacia la derecha cuando se cree. Nuevamente, método aleatorio llamado por sí solo, así, devolverá un valor aleatorio 0-1 La definición completa es que método aleatorio devuelve un número pseudoaleatorio de punto flotante que es mayor o igual a cero y Si creo una expresión que dice método aleatorio es menor a 0.5 será cierto en aproximadamente el 50% de los casos. 50% de la ola estará hecha de morfos de rinoceronte, lo contrario significa que el otro 50% creará morfos de escarabajos Ahora bien, si juego el juego, estamos consiguiendo ambos tipos de enemigos y deberían distribuirse de alguna manera, igualmente 50, 50. Por supuesto que es aleatorio, siempre obtendremos más de uno u otro, pero en general promediará para darnos una distribución casi igual. Digamos que solo quiero que un 20% de los enemigos sean rinoceronte y el 80% restante sean morfos de escarabajo Yo usaría 0.2 aquí. Esto nos da una forma muy sencilla de controlar la distribución de tipo enemigo en nuestro juego. Agregar un tipo de enemigo que funcione de manera ligeramente diferente hace que nuestro juego sea un poco más interesante de explorar para nuestros jugadores. ¿Qué más debemos hacer para mejorar aún más este juego? ¿Qué otras características debemos implementar? Dame tus ideas creativas abajo y ayúdame a diseñarlas. Nos vemos en la siguiente parte. 17. Características adicionales: batallas contra jefes: No tengo datos sobre esta criatura. Su caparazón es puro polímero de telurio, imposible de escanear. No hay registros sobre esta especie. Nadie que conoce a esta criatura sobrevive para contar la historia. Estoy tratando de obtener más datos. Mientras tanto, la única estrategia que tenemos es la potencia de fuego cruda. Golpéalo con todo lo que tenemos antes de que nos golpee. Y tratemos de evitar estas masivas garras de telurio. Algo me dice que cortarían nuestros escudos como mantequilla. Sí, cualquier contacto cercano resultará en escudos inmediatos, fallas, manteniendo nuestras distancias imperativas. 18. Clase de jefe: Al escribir código personalizado para un proyecto como este, más importante es estar siempre al tanto de qué código se ejecuta solo una vez. Qué bloque de código se ejecuta cuando ocurre un evento específico. Y qué código se ejecuta una y otra vez por cada bucle de animación en nuestro juego, los enemigos vienen en olas cada vez más grandes. ¿Y si quiero que cada tercera ola sea un encuentro aleatorio de jefes Y a medida que avanza el juego, quiero que los jefes tengan cada vez más salud Poco a poco aumentando el chal***ge para el jugador. Estás viendo la versión completa extendida. El código fuente completo de cada etapa se incluye en la sección de descargas a continuación. Si estás viendo una versión gratuita de Youtube, no olvides hacer clic en el me gusta y hazme saber que quieres más proyectos como este. Como de costumbre, empiezo importando los activos artísticos. Puede descargar el archivo PNG de boss en la sección de recursos a continuación. Coloca esa imagen dentro de la carpeta del proyecto y vincúlala en un archivo HTML índice así. Le doy una idea de jefe, lo escondemos con CSS. Si inspecciona la hoja de cálculo, creé cuatro criaturas jefes diferentes. La hoja de cálculo está organizada así. Los dos primeros fotogramas jugarán cuando golpeemos al jefe con un proyectil jefe se moverá un poco para reaccionar al ser golpeado Cuando agotemos todos sus puntos de vida, toda su vida, el resto de la hoja de cálculo jugará Espero que te gusten las animaciones pop que hice. Estaba divirtiéndome un poco con esto. Aquí abajo en 960, creo una clase de jefe personalizada. El constructor se ejecutará solo una vez para crear un objeto jefe. Cada vez que llamamos a esta clase usando la nueva palabra clave, estaremos apuntando al objeto principal del juego como lo hicimos antes. Para tener acceso a todas las propiedades y métodos que se sientan ahí, quiero que el jefe sea mucho más grande que los enemigos normales. El ancho y la altura serán 200 veces 200 píxeles. Quiero que el jefe comience exactamente en el medio de la pantalla horizontalmente. Este juego de ancho de punto veces 0.5 menos la mitad del ancho del jefe. Al principio, se ocultará detrás del borde superior del área de juego visible. La coordenada y vertical será menos la altura del punto. Velocidad horizontal, la velocidad x será aleatoriamente menos uno o más uno. Para ello, utilizamos el operador ternario que evalúa la expresión mathrandom como lo hicimos antes Si mathrandom es menor a 0.5 en aproximadamente 50% de los casos la velocidad x es menos uno, el jefe comenzará a moverse hacia la izquierda, lo contrario el jefe se moverá hacia la derecha La velocidad Y será cero. En un principio, jefe siempre tendrá digamos diez vidas al principio. El próximo jefe tendrá más salud. Aumentaremos vidas cada vez que creamos una nueva ola de jefes. También le voy a dar vidas máximas. Esa propiedad recordará el valor inicial de vidas de dis dot para que pueda otorgar al jugador la cantidad apropiada de puntos cuando el jefe sea derrotado. Marcado para de lesión será falso. Al principio, esta imagen de punto apuntará a la hoja de sprites que tengo que contiene cuatro criaturas jefes diferentes El marco X manejará la navegación horizontal de sprites. Empezaremos en el fotograma cero. Por lo que el marco de la columna izquierda Y determinará cuál de estos cuatro jefes veremos manejará la navegación vertical de sprites Cada vez que creamos un nuevo jefe, quiero elegir aleatoriamente uno de estos cuatro. Frame Y será un valor aleatorio 0-4 Usamos piso Matt aquí para redondear hacia abajo al entero inferior más cercano Eso lo hacemos porque no hay fila 1.5 en la hoja de sprites Por ejemplo, queremos solo enteros. Ahora bien esta expresión nos da cero o uno, o dos, o tres. Necesitamos conocer el fotograma horizontal máximo para comprobar si toda la animación de explosión terminó de reproducirse, y podemos eliminar el objeto jefe del juego. Estableceré el marco máximo a 11. Esto me recuerda que olvidé hacer una cosa en la lección anterior. Aquí abajo, en el método de nueva ola en la clase de juego principal, empujamos objeto de nueva ola en esta matriz de ondas de puntos cada vez que la ola anterior de enemigos es derrotada. Si consolo estas olas y juego, derroté a la ola uno, a la onda dos, a la ola tres Podemos ver en la consola que la matriz de ondas didot está creciendo sin cesar y las viejas olas simplemente se quedan ahí aunque ya no contengan enemigos activos Para arreglar esto, subo aquí a nuestra clase de onda personalizada. Le doy una propiedad llamada marcada para su eliminación. Inicialmente se establecerá cae dentro del método de renderizado en la clase de onda. Sabemos que este método se ejecuta para cada fotograma de animación. Mover la ola alrededor del área de juego y filtrar a los enemigos destruidos de su matriz de enemigos distorsionados Cuando el jugador destruyó a todos los enemigos y didot enemigos, ***gth No quedan enemigos en esta ola. Vamos a establecer marcado para su eliminación en este objeto de onda en true. Cuando este objeto de ola tiene una matriz de enemigos que contiene cero enemigos, significa que los derrotamos a todos y se creó la nueva ola. Queremos eliminar esta vieja ola aquí abajo donde creamos una nueva ola. Filtraremos olas viejas cada vez que agreguemos una nueva ola al juego, revisaremos esta matriz de ondas de puntos y la filtraremos. Solo aquellos elementos que han marcado para propiedades de eliminación establecidas en cara pueden permanecer en esta matriz de ondas de punto. Si han marcado para eliminación establecido en true, se eliminarán. Ahora juego el juego y solo veo dos olas, la vieja y la nueva que se acaba de crear. En realidad, si muevo el consolo aquí y juego el juego, podemos ver que esta matriz de ondas de punto solo contiene un objeto de ondas actualmente activo Espero que esto tenga sentido. Cualquier duda al respecto, puedes dejar un comentario. Volvamos a la clase de jefes. Se necesitará un método de dibujo personalizado para dibujar al jefe en el juego, y actualizar el método para moverlo y definir su comportamiento. Tenemos una hoja de sprites con múltiples marcos entre los que queremos intercambiar que significa que necesitaremos la versión más larga del método draw image, una versión que toma nueve argumentos. La imagen que queremos dibujar la fuente x, la fuente y, la anchura de la fuente y la altura de la fuente de un área. Queremos recortar de la imagen de origen como de costumbre, destino X, destino Y, ancho de destino y altura de destino. Para definir en qué lugar del lienzo de destino queremos colocar ese marco de recorte. Quiero recortar el marco superior izquierdo de coordenada 002 con la altura. Quiero colocarlo sobre lienzo en posición x punto y punto punto alto. Así, el jefe comenzará completamente oculto detrás del borde superior de la lona. Quiero que fluya a la vista hasta que sea completamente visible. Si esta y es menor que cero, este punto Y más equivale a diez píxeles por fotograma de animación. Esta es una buena configuración básica para el jefe. Ahora vamos a dibujarlo en realidad. Entonces sabemos con qué estamos trabajando aquí abajo en la clase de juego principal. Sabemos que todo el código que ponemos dentro de un constructor se ejecuta automáticamente en el punto en que creamos una instancia de esa clase usando la nueva palabra clave, estoy empujando automáticamente la onda número uno, el primer grupo de enemigos, en el juego. Tan pronto como creamos el juego, elimino esto Por ahora. Crearé una matriz de jefes que contendrá a todos los jefes actualmente activos Probablemente solo tendremos un jefe activo en ese momento, pero podríamos tener fácilmente varios si quieres que el juego sea aún más chal***ging para Aquí abajo tenemos un método de reinicio personalizado que se ejecuta cuando terminamos un juego. Y cuando presionamos la letra R aquí puedes ver que solo estoy configurando todo de nuevo a los valores predeterminados. El problema es que si quiero hacer algún cambio en algo de esto a medida que desarrollo el juego, necesito hacer los mismos cambios dentro del constructor de clase de juego a esas propiedades. Y también aquí abajo en el método de reinicio para asegurarse de que es consistente con el momento en que el juego comienza por primera vez y cuando se reinicia Podría evitar tener que hacer eso llamando realmente al método restart desde el constructor, lo que significa que restablecerá todos los valores tan pronto como comience el juego. también podríamos llamar a ese método algo así como inicializar De cualquier manera, ahora que he hecho esto, estos valores iniciales de 11 aquí no importan realmente. Porque aquí, incluso antes de que comience el juego, método restart establecerá columnas y filas dos a dos porque esos son los valores que usamos aquí abajo cuando reiniciemos el juego. También necesitamos establecer la matriz de jefes una matriz vacía como esta para limpiar. Ahora como sabemos que el método restart se ejecuta de inmediato, podemos poner ahí cualquier código que queramos ejecutar cuando comience el juego y cuando el juego se reinicie Ya estoy empujando automáticamente la primera ola enemiga aquí. También usemos nuestra nueva clase de jefe personalizada para insertar un nuevo objeto jefe en la matriz de jefes. 19. Movimiento de jefes: Necesito subir aquí dentro del método render y tenemos que llamar a su draw y update boss array. Para cada uno, para cada objeto boss dentro de la matriz boss llame a su método draw. Sabemos que espera contexto y también llamamos su actualización. Bien, tenemos las dos olas enemigas al mismo tiempo. Ahora sabemos que podemos bajar aquí dentro del método restart. Y a lo mejor queremos comenzar el juego con una ola de enemigos. O tal vez queremos comenzar con una ola de jefes. Que es lo que voy a hacer ahora. Debido a que estamos construyendo y probando nuestra lógica de jefe, puedo cambiar este valor para hacer que el jefe flote a la vista. Más rápido o más lento. Puedes decidir tu propio valor de velocidad. Aquí iré con cuatro píxeles por fotograma de animación. Quiero darle al jefe el mismo patrón de movimiento de izquierda y derecha que le dimos a la ola de enemigos. Si la coordenada x horizontal del jefe es menor que cero, es decir, si el jefe está tocando el borde izquierdo del área de juego, o la posición horizontal del jefe es mayor que el ancho del juego menos el ancho del jefe. decir, que el jefe está tocando el borde derecho del área de juego. En ambos casos, cambiamos velocidad x valor de la línea 167 a su opuesto, lo que hará que el jefe rebote a izquierda y derecha. Ahora solo necesito aumentar este punto x por velocidad x por cada fotograma de animación, también voy a aumentar punto Y por velocidad Y. Niza. Tenemos un jefe que flota en la vista y rebota de izquierda y derecha También quiero darle velocidad y de 200 cuando toque cualquiera de los bordes. Pero eso significa que el jefe simplemente se apresurará hacia adelante así. Quiero que esa propiedad de velocidad y aumente solo para un fotograma de animación para un salto rápido. Entonces inmediatamente lo volvimos a poner a cero. Todas estas cosas de movimiento son exactamente la misma lógica que usábamos antes para el movimiento de las olas. Ahora funciona. Haré que el salto vertical sea igual a la mitad de la altura del jefe. Así. Siempre podemos ver solo este marco porque estoy bajando de 00 a ancho y alto. Si quiero fila aleatoria en su lugar, uso el argumento fuente y para eso Cero veces a la altura es este marco una vez, altura es este marco dos veces la altura nos dará esta versión de mantis morph Tres veces la altura nos dará este espacio profundo, menor artesano y monstruo Ya preparamos la propiedad del marco Y para este propósito, en la línea 174, usémosla aquí. La fuente manejará la navegación horizontal de sprites. Será marco x desde 973 veces ancho desde 963. Por ejemplo, si frame x es cinco, nos dará este marco. Si llegaste tan lejos en este curso, creo que ya entiendes esta parte. Así que sigamos adelante cada vez que recarguemos el juego o creamos un nuevo jefe usando nuestra clase de jefe personalizada, obtendremos al azar una de estas cuatro criaturas jefe perfecta El primer jefe será fácil, sólo tendrá diez vidas. De hecho, dibujemos vidas de jefes. Podemos optar por hacerlo como barra de salud o como número. Voy a envolver este código entre seguro y restaurar para asegurarme de que cualquier cambio estado del lienzo que haga aquí se limite solo a esta área llamará método de texto de relleno incorporado que toma al menos tres argumentos. El texto que queremos dibujar este punto vive de la línea 169 y x e y de un punto de anclaje en relación con el que se mostrará el texto. Empecemos con x e y del jefe, que como sabemos es la esquina superior izquierda del jefe. Rectángulo de caja de golpe. De hecho quiero mover el texto un poco hacia abajo, tal vez más 50 píxeles vertical y horizontalmente. Probemos el ancho de los dos tiempos 0.5 Haciendo esto ponemos el punto de anclaje del texto exactamente en el medio, pero por defecto el texto queda alineado a la izquierda. A partir de ahí, usaré propiedad Text Align y la estableceré centro porque envolvimos este bloque de código entre safe y restore. Eso significa que solo afectará a la vida del jefe y no a ningún otro texto que estemos dibujando sobre lienzo para hacer que el texto blanco sea más visible contra el fondo. También le puedo dar una sombra de conjunto X tres pixeles, sombra de conjunto Y tres pixeles también. Y el color de las sombras puede ser negro. Ahora que vemos ambas vidas, vamos a configurar la detección de colisiones entre ambos y proyectiles dentro del método de actualización En ambas clases, tomaré proyectiles pull array que se asienta en la clase principal del juego Hago un llamado para cada método en él. Por cada proyectil en matriz de tiro de proyectiles, revisaré lo siguiente Si verificamos el método de colisión entre este objeto jefe y el proyectil con el que estamos circulando actualmente para cada método, si la comprobación de colisión en las cajas de impacto rectangulares devuelve true al mismo tiempo operador del doble por ciento Solo quiero verificar si el proyectil está activo porque los proyectiles provienen de un charco de objetos re, utilizable solo si la propiedad libre sobre el proyectil es falsa, es decir, el proyectil se está decir, el proyectil Está activo en el juego. Una comprobación más si las vidas del jefe son más de cero. Porque cuando agotamos tanto vidas como los fotogramas de animación de explosión están sonando, quiero que los nuevos proyectiles ignoren eso y vuelen a través de él, no colisionen con él Sólo después de que todos estos tres controles sean ciertos, ambos y proyectil están colisionando con un proyectil activo y Bos tiene más Reducimos la vida del jefe en uno. También llamaremos método de reinicio en el proyectil, lo que significa que el objeto proyectil quedará inactivo y será devuelto de nuevo al fondo de objetos. Yo lo pruebo. Sí, cuando golpeé al jefe, agotamos sus vidas. Esto va muy bien. En lugar de reducir vidas directamente así, voy a crear un método de golpe que tomará como argumento el valor del daño. Y reducirá vidas por ese daño. Quiero que el jefe se mueva y se anime un poco y reaccione al ser golpeado Intercambiaremos entre cuadros 0.1 sólo si las vidas son más de uno. Cuando se golpea, estableceremos el fotograma X en uno que muestre este fotograma. Ahora lo uso método aquí y le paso daño de uno. Bien, eso sigue funcionando. Golpeamos al jefe y cambiamos marco para enmarcar uno. Este ahora quiero que vuelva al fotograma cero original, pero queremos esperar a indicador de actualización de sprite sea cierto para ralentizar la animación Igual que hicimos con los enemigos. Además, solo regresamos a ese fotograma si las vidas de los jefes son más de cero, porque cuando llegan a cero queremos jugar todo el papel de animación. Bonito. Ahora bien, si golpeamos al jefe, siempre y cuando las vidas del jefe sean más de una, cambiamos entre frame 0.1 para hacer que el jefe se mueva y reaccione para estar en hit Si recargo y consigo una criatura jefe diferente, podemos ver que se mueve de una manera diferente Le di a cada jefe una forma de montar para reaccionar ante ser golpeado así. Ayuda a darle al jugador más retroalimentación visual para que el juego se sienta más vivo. Creo que estamos usando imágenes estáticas básicas, pero reaccionan a entradas y acciones. Las hojas de sprites solo tienen un pequeño número de marcos, pero todo es reactivo Esta es una de las formas amigables con el rendimiento de animar tus juegos Diré más de cero aquí. En realidad, cuando ambas vidas llegan a cero, queremos animar el resto de la hoja de sprites donde el jefe está siendo inflado y destruido Si las vidas del jefe son menos de una. Si la bandera de actualización de sprites es verdadera, comenzaremos a aumentar lentamente los huevos de marco por uno viajando a través de la hoja de sprites horizontalmente comenzaremos a aumentar lentamente los huevos de marco por uno viajando a través de la hoja de sprites horizontalmente. Vamos a probarlo. Le pegué diez veces al jefe. Bonito. Reproduce la animación. Se puede ver que el jefe sigue ahí. Es solo mostrar un marco de sprites en blanco. En realidad, no necesito dibujar cero vidas. Sólo sacaremos vidas si esta vida es más que cero. Ahora destruimos jefe. Ya no estamos dibujando sus vidas, y los proyectiles ya no chocan con él, sino que el jefe invisible sigue Moviéndonos por la pantalla, necesitamos eliminarlo usando Marcado para propiedad de eliminación que definimos aquí en la línea 171. A medida que actualizamos los marcos horizontales, marco X es más que el cuadro máximo. Establecemos el marco máximo a 11 aquí en 975. En este punto, sabemos que el jefe fue destruido y que se exhibieron todos los fotogramas de animación de explosión. Sabemos en este punto que es seguro eliminar completo el objeto jefe del juego. Voy a establecer que está marcado para la propiedad de eliminación en true. También voy a aumentar la puntuación del juego por esta vida máxima que sostiene el número inicial de vidas, el jefe jefe en este caso diez jefe con diez vidas otorgará diez puntos de puntuación. Al ser derrotados, establecemos marcado para su eliminación en verdadero aquí. Y voy a bajar al método de render principal y voy a filtrar matriz de jefe. Esta línea de código se ejecuta para cada fotograma de animación, lo cual es un poco desperdiciado. Para ser honestos, fácilmente podríamos simplemente ponerlo en otro lugar, por ejemplo, en el método new wave y solo limpiar todos los objetos boss periódicamente, no tenemos que verificarlo para cada fotograma de animación. Ya cubrimos cómo funciona el método filter y cómo filtra todos los objetos de la matriz que han marcado para la propiedad de eliminación establecida en true. A ver si funciona por consolo. En esta matriz de jefes, tenemos un jefe. Dentro de la matriz, agotamos todas sus vidas. Juegos de animación de explosión. Ahora puedo ver en consola que el array boss está vacío. Genial, borro el consolo. 20. Colisión de jefe contra jugador: Ahora tenemos que decidir qué pasa si jefe consigue todo el camino hacia abajo y choca con el jugador Compruebo si el método de colisión de verificación personalizado entre este objeto jefe y el objeto jugador devuelve verdadero al mismo tiempo. Sólo ejecuta este código cuando jefe tiene más de cero vidas. Porque podría haber un escenario donde el jugador choca con fotogramas de animación de explosión Lo cual estaría bien porque el jefe ya fue destruido. No queremos registrar colisión en ese caso. Por dentro puse el juego a la verdad. Tenemos que destruir al jefe antes de que toque al jugador. De lo contrario, sus garras pasan directamente por nuestros escudos. El jefe es muy peligroso. Al mismo tiempo, puedo poner vidas del jefe a cero para activar la secuencia de animación de explosión si quiero. Digamos que nuestros escudos también destruirán al jefe. No lo sé. Crea tu propia historia. Vamos a probarlo. Voy a acelerar esto. Cuando el jefe toca al jugador, terminamos el juego y el jefe explota Eso funcionó perfecto. Otra condición floja sería si jefe llega hasta aquí y toca la parte inferior de la pantalla. Si la posición vertical del jefe más su altura es mayor que la altura del juego. Lo que significa que el borde inferior del jefe toca la parte inferior del área de juego visible. Ponemos el juego a la verdad. Cuando destruya así al jefe, quiero que el juego continúe y quiero que sigan llegando oleadas regulares de enemigos. Jefe fue destruido aquí. Si el juego terminado es falso, llamamos a nuestro método personalizado de nueva ola. Vamos a probarlo. Destruyo al jefe, obtenemos una ola de enemigos. Destruyo a todos los enemigos en la ola, me sale otra ola más grande. Este es un bucle de juego bastante bueno ya. ¿No se hace si lo sigues todo el camino hasta aquí? Cada vez que llega una nueva ola, estamos aumentando el valor del conteo de ondas y lo estamos mostrando. Aquí estamos llamando a un método de nueva ola que definimos en la línea 393 aquí. Cada vez que el recuento de ondas es divisible por dos sin ningún resto, ejemplo, cuatro ondas, 02468, Empujaremos a un nuevo jefe, lo contrario crearemos una ola regular, cada vez más grande de enemigos. Tengo que tener cuidado con los corchetes aquí. Digo esta matriz de jefe, empuje nuevo jefe. Pasé esto para señalar el objeto principal del juego en el que estamos dentro ahora mismo. No quiero aumentar el conteo de ondas aquí, borré. En cambio aumentamos el recuento de ondas aquí dentro del método de onda nueva. De esa manera, tanto las olas de jefe como las enemigas aumentarán el valor del conteo de olas. Ahora vamos a probarlo. Comenzamos la primera ola con un jefe como definimos dentro del método de reinicio. En la primera carga derrotamos al jefe. contador de ondas aumenta a dos, el cual es divisible por dos con un resto de cero Este bloque corre y volvemos a conseguir un jefe. Derrotamos ola es tres. Esta es falsa declaración else en la línea 396 corre y obtenemos una ola de enemigos. Cuatro es divisible por dos con un resto de cero. Ahora conseguimos un jefe. Puedes cambiar la frecuencia con la que quieres conseguir un jefe ajustando este valor aquí. Si quieres que la primera ola ola cero sea jefe o enemigos, aquí definimos ese método de reinicio interno. Cuando empieza a reproducirse la animación de explosión, no quiero que rebote desde ya no quiero que rebote desde los bordes porque eso se ve extraño. Si la explosión comienza a reproducirse, solo déjala fluir fuera de la pantalla si está cerca de los bordes. Una condición más aquí que dice que si este punto vive es más de cero, solo hacen el rebote regular izquierdo y derecho en movimiento si el jefe no está animando la explosión final Ahora nos estamos acercando al borde y cuando le disparo al jefe, la explosión ya no rebota. Simplemente fluye y continúa en la dirección en la que el jefe ya se estaba moviendo. Creo que esto se ve mejor. Me doy cuenta de que cuando derrotamos a una ola de jefes, no conseguimos vivo a un jugador bonus para arreglarla. Corté esta línea de código, pego aquí abajo. Dentro de un método de nueva ola, derroté a un jefe. Sí, me sale un bono de vida Bueno. Cada vez que creamos un nuevo jefe, quiero que tenga, digamos cinco vidas más. Para hacer el juego poco a poco cada vez más chal***ging. Almacenaré el valor de las vidas del jefe aquí en el objeto principal del juego. Empezaremos con diez vidas. Cuando reiniciamos, también necesitamos volver a diez. Voy a pasar vidas de jefe como el segundo argumento al constructor de clase jefe para el jefe en la primera ola inicial y luego por cada otro jefe consecutivo que creamos, me aseguraré de que ese valor se espera aquí en la línea 161. Y lo usamos como valor para esta propiedad de vidas cuando derrotamos a un jefe y fue destruida antes de que llamemos a la siguiente ola, aumentamos la propiedad de vidas de jefe en el objeto principal del juego digamos cinco, haciendo que el siguiente jefe sea un poco más chal***ging Este jefe tiene diez vidas, yo la destruyo. El siguiente jefe tiene 15 vidas que funcionaron. Tenemos oleadas de enemigos que se hacen cada vez más grandes a medida que avanza el juego. Y tenemos jefes con cada vez más salud. Gracias a todos los que comentaron los videos y me ayudaron a diseñar este juego. Si tienes alguna idea más y quieres que agregue más funciones, házmelo saber en los comentarios, Si se te ocurre algo realmente bueno, lo haré y lo liberaré como lección extra. Más tipos de enemigos, súper armas, súper jefes más grandes que en realidad nos disparan proyectiles. ¿Qué quieres que haga a continuación? Házmelo saber. Es más divertido para mí si lo diseñamos juntos. Mientras juego, noto que el jefe ya pierde algunas vidas antes de que sea incluso completamente visible en la pantalla desde los proyectiles que están volando cerca del borde superior Digamos que solo quiero que una colisión entre jefe y proyectiles ocurra una colisión entre jefe y proyectiles después de que el jefe flotara hasta su Y es completamente visible en pantalla cuando la posición vertical del jefe es mayor o igual a cero. Bien, estoy contento con esto. ¿Qué opinas? Nos vemos en la siguiente parte. 21. Características adicionales: súper armas: A medida que llenamos nuestro juego de enjambres de enemigos peligrosos, necesitamos una mejor manera de lidiar con ellos. Tiempo para súper armas. Déjame mostrarte algo. Recogí suficiente plutonio para mejorar nuestro reactor. Puedo enfocar su energía en una poderosa columna de luz y usarla como arma. Parece que las cosas pueden ponerse muy calientes. Muy rápido. Sí, nuestros escudos solares nos darán una fuente de energía renovable, pero ten cuidado de no sobrecalentar el cañón El bloqueo de seguridad deshabilitará la válvula. Si superamos la temperatura máxima de seguridad señalada, manejaremos nuestra energía y evitaremos el sobrecalentamiento. Suena bastante fácil. El láser puede quemar a través múltiples enemigos al mismo tiempo. Puede destruir toda la ola enemiga en segundos si se cronometra correctamente. Feliz caza. 22. 2 clases de láser: En esta clase de desarrollo de juegos de script Java, agregamos administración de recursos con una barra de energía recargable. Y vamos a implementar dos armas más, un láser de largo alcance y un súper láser. Estas nuevas armas serán fundamentalmente distintas de nuestro ataque básico, porque golpearán a todos los enemigos en una columna frente a ellos, incluso a los que están en la parte de atrás. Puede ser muy efectivo, sobre todo más adelante en el juego, cuando enfrentamos olas enemigas cada vez más grandes. Tendremos la clase láser padre que definirá las principales propiedades y métodos de ambas armas láser. Lo extenderé en dos subclases, láser pequeño y láser grande Yo lo hago porque cada láser tendrá diferente tamaño y va a hacer daño diferente. Esas cosas específicas se definirán por separado en cada subclase Constructor necesitará acceso al objeto principal del juego para darnos acceso a todos los métodos y propiedades importantes que anteriormente pusimos en la clase de juego. Desde el interior del método de renderizado de clase láser dibujará y actualizará el arma láser. Se necesitará contexto como argumento. Extenderemos el constructor en ambas subclases. Vamos a hacer primero todo en una pequeña subclase láser para que sea simple Cuando creamos una instancia de láser pequeño, queremos ejecutar todo el código dentro de su constructor de clase padre. Sabemos que a la clase padre también se le puede llamar superclase Llamamos constructor de superclase así. Y sabemos que espera una referencia al objeto del juego. Lo paso aquí así. Aquí se espera. Y se convierte en esta propiedad de juego de puntos. Solo después de ejecutar el código dentro del constructor Superclass, podemos usar este punto aquí y definir propiedades específicas solo para la subclase láser pequeña Eso lo haremos en un minuto. También quiero extender, no quiero anularlo esta vez. Quiero que el código desde interior render de la línea cinco se ejecute realmente. Este bloque de código contiene lógica compartida para las subclases Small Laser y Big Laser Después de eso, agregaré un poco de código que es específico solo para la subclase Small Laser Aquí. La sintaxis es un poco diferente. Es así. Aquí, estoy llamando constructor de superclase Aquí, estoy llamando a un método que se sienta en la súper clase, en la clase padre. Espero que tenga sentido. Constructor es un tipo especial de método. Por eso la sintaxis es diferente aquí. En estos dos casos, aunque ambos solo están llamando a un método en la clase padre, hay una razón por la que estoy poniendo método render en la subclase, pero llegaremos a eso más adelante El láser necesitará propiedades x e y de ancho y alto para que Javascript sepa exactamente dónde dibujarlo. Además, estaremos comprobando la detección de colisiones entre el rayo láser y los enemigos y jefes. Sabemos que todos los objetos involucrados en la detección de colisiones necesitan x y con propiedades de altura. X, y y la altura pueden permanecer en la clase padre, pero cada tipo de láser tendrá un ancho diferente. Tengo que ponerlo en la subclase aquí. Queremos que el súper láser grande sea mucho más ancho que el láser pequeño, el pequeño. Intentemos cinco píxeles. Se animará la hoja de sprites de los jugadores. Con estas armas, obtendremos ancho para coincida con el sprite de la nave espacial en un minuto Esto, la coordenada horizontal del láser se moverá con el jugador. A medida que el jugador se mueve a izquierda y derecha. Inicialmente lo puse a cero. coordenada vertical y en ambos láseres siempre será cero porque quiero que el rectángulo del rayo láser comience desde la parte superior de la pantalla La altura del láser siempre será la misma también. Quiero que el rayo láser venga de aquí a algún lugar por aquí. Supongo que a partir de la coordenada cero altura del rayo láser será esta altura de punto punto menos digamos 50 pixeles para cubrir esta área. Sabemos que todo el código dentro del constructor se ejecuta sólo una vez. En el punto en que creamos una instancia de la clase, usar la nueva palabra clave render method será muy diferente. El código dentro del método de renderizado se ejecutará una y otra vez para cada fotograma de animación. Tenemos que tenerlo en cuenta cuando decidamos dónde poner la lógica. Cuando el láser esté activo, quiero que su coordenada horizontal siga actualizándose para que coincida con la posición del jugador. Empecemos con esto y veamos qué obtenemos. Ahora dibujaré el rayo láser como un simple rectángulo. Le voy a dar dos colores. Envolveré este bloque Cod en caja fuerte y restauraré los métodos de lienzo incorporados para asegurarme que estos colores de estilo de relleno afecten solo al rayo láser y no a las otras formas dibujadas en el lienzo. Dije que es estilo de relleno al oro construido en método de rectángulo de relleno. Dibujaremos el láser. Simplemente lo paso x, y, w de aquí y altura. Si algo de esto no tiene sentido, deja un comentario y ya podemos hablar de ello. Espero que esté todo claro. Contamos con la versión básica de láser pequeño. Vamos a dibujarlo para que podamos verlo en juego y podamos retocarlo y ajustarlo. Voy a crear una instancia de clase láser pequeña aquí. Se creará en el punto en que creamos una instancia de la clase de jugador usando la nueva palabra clave. También podría haber puesto pequeño láser en la clase de juego principal, pero creo que aquí tiene más sentido ya que estará tan estrechamente empatado con múltiples propiedades en el jugador. Aquí estamos comprobando si se presiona la tecla y estamos intercambiando fotogramas del jugador Voy a crear un bloque LF. Tengo que tener mucho cuidado con los brackets a la hora de hacer esto. Un solo soporte incorrecto puede romper todo el código del juego. Si se presiona a uno, hacemos el ataque básico. Cuando se presiona dos activaremos pequeño jugador láser hoja de cálculo ya está preparada para esto de antes Solo necesito poner el marco X a dos así. Estamos dentro de un método de dibujo que se extiende una y otra vez para cada fotograma de animación. Siempre y cuando se presione la tecla dos, estaremos mostrando el fotograma X dos y estaremos llamando al render en un pequeño dibujo láser y actualizándolo. Tan pronto como liberemos el fotograma clave x se pondrá a cero y dejaremos de renderizar láser pequeño. Si presionamos al número uno, hacemos el ataque básico. Cuando presionamos el número dos, dibujamos láser pequeño. Se mueve con el jugador. Quiero moverlo a la mitad del jugador. Ajusto el código en la línea nueve. posición del láser es la posición del jugador más la mitad del ancho del jugador. Ahora está casi centrada. Para centrarlo completamente, tengo que compensarlo por la mitad del ancho del láser. Ahora está exactamente en el medio. Como puede ver, cuando disparamos el pequeño láser, los brazos de la nave espacial se mueven hacia adentro para ayudar a enfocar el rayo láser delgado en un área pequeña Y el hocico pequeño salta un poco hacia atrás. Pondré el ancho del láser a 50 píxeles, solo para que podamos verlo mejor. Y le voy a dar color adicional porque la mitad del laser esta tan caliente, quiero que aparezca blanco puro. Copio este bloque de código, lo puse en blanco. Será digamos 60% del ancho del rayo láser, lo que nos deja con 40% para centrar el haz blanco en medio del haz dorado. Tenemos que empujarlo un 20% del ancho, el borde izquierdo, empujándolo exactamente en el medio. Ahora la mitad del rayo láser es blanca, caliente. Creo que darle dos colores hace que se vea mucho mejor. Dado que usamos valores relativos, el haz blanco se centrará sobre el haz dorado. Independientemente del ancho del láser, diez píxeles siguen siendo demasiado anchos. Quiero que el ancho del haz coincida con el hocico de nuestra pistola láser Cinco píxeles parecen buenos. 23. Daño con láser: Cada subclase láser también tendrá un valor de daño. Esto definirá la cantidad de daño a los enemigos que hará por tick. De hecho, implementemos ese daño y golpeemos a los enemigos con láser. Ahora mismo, todavía no interactúa con ellos en absoluto. Puedo poner la siguiente lógica en múltiples lugares diferentes en mi base de código. Decidí ponerlo dentro del método de renderizado en clase láser porque me gusta mantener el código organizado y fácil de navegar. Tiene sentido porque solo quiero comprobar colisión entre láser y enemigos cuando en realidad estamos dibujando el láser. Si recuerdas, tenemos una matriz que contiene todas las olas enemigas actualmente activas en nuestro juego. Siempre hay una sola onda activa en esa matriz. Pero puedes dificultar el juego y lanzar la nueva ola antes de que la anterior quede completamente derrotada. Por ejemplo, cada objeto de onda dentro de la matriz de ondas tiene una matriz de enemigos dentro de él. Como definimos aquí en la línea 280, esa matriz contiene a todos los enemigos actualmente activos en esa ola. Para recorrer todos los enemigos activos, voy a recorrer la matriz de ondas. Por cada objeto en array de ondas, solo habrá uno. En nuestro caso, accedemos a su matriz de enemigos, también llamamos a cada uno en eso, para cada objeto enemigo en esa matriz, llamamos a nuestro re, método de colisión de verificación utilizable que escribimos antes de pasarlo enemigo y este objeto láser. Si enemigo y laser chocan, voy a llamar método de golpe sobre enemigo Y le paso este daño del laser Lo definimos aquí en la línea 32. Antes de probarlo, también recorreré todos los jefes dentro de la matriz de jefes Nuevamente, generalmente solo hay un jefe activo, pero si quieres, puedes empujar múltiples jefes al mismo tiempo O puedes empujar al siguiente jefe cuando al anterior solo le quedan unos cuantos puntos de golpe. Deja los detalles del diseño del juego sobre ti. Te estoy dando todas las herramientas, Diseña tu propio juego para cada jefe en matriz de jefes. También llamamos método de colisión de verificación entre ese jefe y este objeto láser. Si chocan, llamamos método de golpe en el jefe y le pasamos daño del láser como argumento Tenemos nuestro método de renderizado que se extenderá una y otra vez siempre y cuando estemos presionando la tecla número dos en nuestro teclado. Cuando lo pruebe, notará que el láser es muy potente. Es divertido poder destruir a todos los enemigos fácilmente, pero le quita todo el chal***ge del juego. Queremos darle al jugador armas divertidas y poderosas. Pero no queremos hacerlos dominados así. La razón por la que es tan fuerte es que golpea a los enemigos por 0.3 vidas por fotograma de animación. Y estamos ejecutando esto a 60 cuadros por segundo o incluso más rápido. Quiero que el daño láser marque más lento y quiero asegurarme de que el daño sea el mismo o similar para personas en computadoras viejas y personas en computadoras de juegos nuevas con pantallas de alta frecuencia de actualización. Ya tenemos una variable llamada sprite update que establecemos true solo para un fotograma de animación cada 150 milisegundos Lo estamos usando para ralentizar la rapidez con la que recorremos los fotogramas de animación en las hojas de sprites Aquí podemos usar la misma variable para ralentizar la rapidez con que el láser marca cuando quema a través de los enemigos. Sólo cuando esta actualización de sprite del juego de puntos es verdadera, solo una vez cada 150 milisegundos más o menos. Queremos verificar la colisión y de hecho golpear los enemigos y al jefe con nuestros láseres de largo alcance Ahora podemos ver que el láser tiene un poco menos de potencia. Creo que esto es mucho mejor. Siempre podemos ajustar esto ajustando este valor de daño por puntos en una subclase láser pequeña Hasta ahora nuestro ataque básico era batear de una vida por golpe. Pero ahora tenemos láseres que pueden infligirle daño no es exactamente uno Podemos terminar en un escenario como este, cuando el jefe tiene menos de una vida, ya debería tener 0.8 vidas. No cuenta. En este juego, quiero que el jefe explote en cuanto sus vidas lleguen por debajo de una Voy a la clase jefe y necesito mirar este bloque de código. Dice si las vidas son más de cero, lo que significa que tiene 0.1 de salud todavía se considera un vivo. Lo voy a cambiar aquí, aquí, aquí, aquí, y aquí en todos estos lugares. Yo sustituyo ese cheque en todas partes con este punto, las vidas son más o iguales a una. Asegúrate de cambiarlo en todos estos lugares. Aquí. También aquí. Perdón por esto. Ahora intentemos usar láser pequeño en el jefe. Voy a quemar lentamente a través de su caparazón de telurio. Y fíjate que en cuanto lleguemos por debajo de una vida, el jefe explota Perfecto. Podemos ajustar el daño del láser aquí. Bueno, sigue funcionando bien. Lo volví a poner a 0.3 creo que ese es el valor correcto para el láser pequeño. El gran láser será mucho más potente. Por supuesto, no queremos que las vidas del jefe muestren tantos decimales. Voy aquí abajo a dibujar método en ambas clases. Aquí dibujamos ambas vidas. Lo envuelvo en piso mate para redondearlo hacia abajo y quitar los números pequeños que vienen después del punto decimal. Vamos a probarlo de nuevo. El jefe debería comenzar a explotar de inmediato Tan pronto como su salud cae por debajo uno y nunca deberíamos ver vidas cero. Sí, estamos progresando mucho. Bien, súper láser. Vengo aquí y copio todo el código dentro. Lo pego por dentro. Láser grande, el ancho de subclase será de 15 píxeles. El daño será de 0.7 por tick. Más del doble del daño del láser pequeño en la clase de jugador. Creamos una instancia de láser grande como esta. Entonces podemos agregar un bloque Lf más aquí. Si la tecla que se presionó es tres, cuadro del jugador es tres. Lo que nos dará el marco final donde los brazos abren y hacen espacio para el masivo rayo láser. Y también para asegurarse de que las puntas no se derritan por el calor. Gran hocico con líneas láser en el interior sale para disparar nuestra arma definitiva, un súper láser Dibujamos y actualizamos el láser llamando a su método de renderizado láser pequeño y láser grande. Se puede ver que inflige más del doble de daño. En realidad es demasiado pequeño. Quiero que el rayo láser llene completamente el cañón, tal vez 25. Observa cómo se anima el jugador para que coincida con las armas. Ataque básico, láser pequeño y láser grande. ¿Qué crees que podemos destruir a los enemigos fácilmente? Ahora las nuevas armas láser son especialmente efectivas contra grupos de enemigos, ya que dañan toda la columna de enemigos de una vez. Quiero poner al jugador y al rayo láser detrás de los enemigos. Pero frente al texto del juego, lo corté y lo pego aquí después de que dibujemos proyectiles, pero antes de dibujar al jefe, lo que primero se dibuja está atrás En casos como este, cuando dibujamos todo en un solo elemento canvas, intenta jugar un poco el juego, y hazme saber lo que piensas. ¿Tienes alguna otra idea de armas, algo completamente diferente a esto? ¿Y si enviamos un enjambre de pequeños robots? ¿O qué tal el arma eléctrica? 24. Gestión de recursos: El juego se ha vuelto trivial y fácil. Ahora que usamos láseres sin límites y en realidad no hay razón para usar el láser pequeño si podemos usar súper láser sin parar Sería mucho mejor si el jugador tuviera un charco de energía que necesita para manejar. Antes de hacer eso, quiero asegurarme de que el jefe solo sea golpeado cuando esté completamente visible. No quiero que tenga vidas reducidas ya por el punto que flote en pantalla, comprobaremos colisión entre el jefe y los láseres solo cuando la posición vertical del jefe sea mayor o igual a cero Una vez que el jefe haya entrado completamente en el área de juego, lo pruebo. Sí, eso funciona. Agreguemos mecánico de gestión de recursos. El jugador tendrá una barra de energía. La energía de arranque será, por ejemplo, 50 energía máxima será de 100. Si no prestamos atención a nuestros niveles de energía, también podemos sobrecalentar nuestro láser que lo desactiva por un corto tiempo Voy a llamar a esta propiedad, por ejemplo, refrescarse. Mostraremos la barra de energía aquí abajo dentro del método Draw Status Text. Nuevamente, utilizaremos un bucle de cuatro que correrá una vez por cada punto de energía. Para cada punto de energía, dibujaremos un rectángulo. Los vamos a espaciar dos pixeles separados, verticalmente, supongo a 50 pixeles de la parte superior. Cada rectángulo de energía tendrá solo dos píxeles de ancho y 15 píxeles de alto. Ya que los estamos dibujando uno al lado de otro, ellos crean una barra. Este valor es el espaciado entre las barras, y este es el ancho de cada barra. Si son del mismo valor, vemos una forma continua. Si el ancho es menor que el espaciado, vemos rectángulos individuales Puedes jugar con estos valores y tendrá sentido cuando veas cómo reacciona a diferentes valores que le des. Si hago 15 más aquí, ese será margen desde el borde izquierdo verticalmente. Yo lo quiero probablemente a 130 pixeles del margen superior izquierdo. El espaciado de 20 es de dos píxeles, y el ancho de cada rectángulo de energía también es de dos píxeles. Estamos recibiendo una barra continua. Ahora cuando disparemos los láseres, nos va a estar costando energía. Aquí arriba digo que este jugador del juego hacer energía menos equivale a este daño. Lo que significa que ahora el láser pequeño es más barato que el láser grande, a veces el láser pequeño es la mejor opción. Ahora cuando uso láseres, puede ver que en realidad agota La velocidad a la que agota la energía es igual al daño del láser. El láser pequeño cuesta 0.3 de energía por grosor, láser grande cuesta 0.7 de energía. Quiero que la energía se recargue lentamente. Si la energía actual es menor que la energía máxima, aumente la energía en 0.05 por fotograma de animación, pruebo, la energía se está recargando Si lo agoto, comenzará a recargarse automáticamente. Nuevamente, bueno, necesitamos evitar que la energía entre en valores menos. El jugador tiene que manejar la energía y ver la barra de energía. Si lo dejan tanto tiempo que la energía se pone por debajo en E, activará el enfriamiento. Nosotros sobrecalentamos el cañón y ahora tenemos que esperar. Es un pequeño castigo por no prestar atención. Si queremos jugar de manera óptima, nunca agotamos la energía por debajo de Nunca activamos el enfriamiento que puedo hacer más aquí, porque la energía nunca puede estar por debajo uno y más del tiempo máximo de energía 0.2 en el mismo fotograma de animación. Esto nos ahorrará un poco de rendimiento. Si la energía cae por debajo de uno, se activa el enfriamiento, la energía se vuelve a llenar al 20% del tiempo de reutilización máximo de energía está configurado para El cañón láser tuvo tiempo suficiente para refrescarse y podemos usar nuestros láseres. Nuevamente, quiero dejar claro visualmente al jugador cuando entramos en un enfriamiento, cuando sobrecalentamos nuestros cañones aquí abajo donde dibujamos esa barra energética, vamos a cambiar su color Envolveré esto a salvo y restauraré para asegurarme de que estos colores de relleno no afecten a ninguna otra forma que dibujemos en lienzo. Estamos bastante metidos en esta clase. En lugar de una declaración F L estándar, usaré la sintaxis del operador ternario aquí como una simple línea única Si L, si el jugador se enfríe es cierto. interrogación, establece el estilo de relleno en rojo L. Colon establece el estilo de relleno en dorado, fácil. Vamos a probarlo. La barra energética es de oro. Por defecto eso significa que todo está bien y podemos usar nuestros láseres cuando uso mis láseres sin prestar atención a la barra de energía y la dejo deslizarse por debajo de una, activamos la barra de energía enfriada se vuelve roja, lo que significa que nuestros láseres están sobrecalentados y solo podemos usar el ataque básico Cuando alcanzamos más del 20% de la energía máxima. Ahora la barra vuelve a cambiar a color dorado y sabemos que podemos volver a usar los láseres, tengo la sensación de que seguimos bajando por debajo de cero energía Puedo hacer esto muy limpio haciendo esto. Aquí está la clase de láser padre y es el método de renderizado. Nunca llamamos a este método de renderizado directamente. Siempre lo estamos llamando desde una de las subclases aquí. Asegurémonos de que solo renderizamos los láseres cuando la energía del jugador es más de uno Al mismo tiempo, si el enfriamiento es falso solo entonces dibujamos y actualizamos el láser pequeño. Copio esto y hago lo mismo por el gran laser. Ahora por la razón por la que estoy poniendo render en subclases. Hasta el momento estos métodos son idénticos y sabemos cuando se comparten las cosas, pueden sentarse en la clase padre. Las subclases contienen solo propiedades y métodos específicos de esa subclase Si agoto mi energía y trato de disparar los láseres, la nave del jugador sigue animando como si estuviéramos disparando Pero como estamos en baja energía y en enfriamiento, el rayo láser no está saliendo. Podría estar bien para tu juego. No estoy seguro de lo que prefieres, pero en mi juego solo quiero que la hoja de sprites de los jugadores se animen así cuando en realidad estamos disparando los láseres en lugar de cambiar el marco del jugador aquí. Aquí borro estas líneas. De hecho vamos a intercambiar jugadores hoja de sprites desde aquí. Lo que significa que el jugador solo animará estos ataques con armas cuando en realidad estemos renderizando esas armas Esos rayos láser. El láser pequeño es marco x dos, láser grande es marco x tres. Igual que antes. Ahora cuando estoy en enfriamiento, no sale ningún láser y los jugadores Sprite Sheet no anima ese ataque con arma 25. Clase de enemigo Eaglemorph: Comenzamos importando hojas extendidas al proyecto. Coloca ambas imágenes en tu carpeta de proyecto, y las hacemos referencia aquí imagen con una fuente. Eaglemorphngid será Eaglemorph. También necesitaremos sus proyectiles puntiosos, limosos, mutados, que vendrán como una Yo los llamo Proyectil Enemigo. Como de costumbre, los esconderé con CSS en la primera carga del juego. En lugar de empujar al primer jefe. Empezaremos con ola enemiga. Porque hoy vamos a estar agregando un nuevo tipo de enemigo a esa ola. Copio este bloque de código que usamos para crear la clase enemiga Rhino Morph Le renombro a Eagle Morph y apunto su propiedad de imagen hacia esa nueva hoja de cálculo que acabamos de incluir en el proyecto Le di a este enemigo una animación de explosión más detallada con algunos fotogramas adicionales. Max Frame será de ocho. Contamos desde el fotograma cero a la izquierda en la clase de olas donde creamos cada ola enemiga organizando a los enemigos en una grilla. Añado un bloque más de sentencia L if. Tengo que tener mucho cuidado con los corchetes aquí. Cuando estoy haciendo esto, quiero que la mayor parte de la ola esté hecha de morfos de águila porque los estaremos implementando y probando. Digamos que el 80% serán morfos de águila, el 10% serán morfos de rinoceronte, el 10% restante serán morfos el 10% restante serán Ya estamos obteniendo morfos de águila, y debido a que copiamos y reutilizamos todo el bloque de código que usamos para los morfos de rinoceronte antes ya funcionaba Se puede ver que cada vez que lo golpeo, pierde un segmento corporal. Cuando el último segmento se ha ido, se reproduce la animación de explosión. Hasta el momento funciona igual que la morph de rinoceronte. Es un enemigo con múltiples vidas. Y la hoja de cálculo refleja su estado actual de daños. Queremos que nos escupe esos segmentos del cuerpo mutado que faltan de nuevo a nosotros como proyectiles que el jugador necesita destruir o evitar Empezaré haciendo que el águila morph se contraiga un poco. Cada vez que se golpea, se verá bien y tendrá sentido en un minuto. Cuando le damos proyectiles, parece que está empujando hacia adelante y escupiendo el proyectil hacia el jugador Es sólo un pequeño twitch. A mí me gusta de esta manera. Todo bien. Le voy a dar un método de tiro. Podemos ejecutar ese método al azar, hacer que los enemigos disparen y escupirnos los proyectiles a intervalos Pero lo que voy a hacer hoy, porque me gusta que el juego reaccione ante las acciones de los jugadores y sea interactivo. El método de disparo se ejecutará cuando este tipo de enemigo sea alcanzado por un arma de jugador. Cuando le pegamos, trata de devolvernos el golpe, reaccionará al ser golpeado. Los proyectiles enemigos serán un grupo de objetos. Como ya sabemos administrar esa estructura de código simple desde antes en esta clase, en realidad voy a copiar toda esta clase de proyectiles Lo estamos usando para crear un grupo de proyectiles de jugador, los ataques básicos de nuestra Copié todo el bloque de código, lo pegué aquí abajo entre mis gafas Eagle Morph y boss Voy a renombrarlo a Proyectil enemigo con 50 pixeles de altura 35 pixeles Los proyectiles enemigos viajarán hacia abajo de los enemigos hacia el jugador La dirección positiva en la velocidad vertical del eje Y será un valor aleatorio, 2-5 píxeles por fotograma de animación. Este es un miembro del grupo de objetos. Crearemos un fondo de objetos reutilizables de digamos 15 proyectiles enemigos Porque es más eficiente reutilizar objetos en lugar de crear constantemente nuevos y descartar los viejos. Cada miembro del grupo de objetos necesita tener una propiedad que indique si está libre y disponible para ser extraída del grupo de objetos o si el objeto está ocupado actualmente. Activo en el juego. Solo dibujamos y actualizamos Proyectil enemigo si se está utilizando Si es propiedad gratuita se establece en caídas. Cada miembro del grupo de objetos también necesita un método para ejecutarse cuando lo tomamos del grupo y lo hacemos activo en el juego. En este caso llamamos a ese método inicio y le pasamos coordenadas x e y del enemigo que va a disparar este proyectil Y fijamos su propiedad libre a caídas. Debido a que se activa, ya no es gratuito y está disponible para ser utilizado. También necesita un método de reinicio que lo desactivará. devolverá al grupo de objetos estableciendo su propiedad libre en true, lo que significa que ya no será dibujada y actualizada. Esta es nuestra clase de proyectil enemigo, Un miembro del grupo de objetos Gestionaremos este pool de objetos desde la clase principal del juego. Funcionará igual que la piscina de proyectiles de jugador que creamos al inicio de este proyecto Copié estas tres líneas de código. Tengo que tener cuidado con la tripa de camello. Aquí tendremos una matriz que contendrá todos los objetos. Yo lo llamaré alberca de proyectiles enemigos. El número de proyectiles enemigos también será de 15. Crearemos y volveremos a usar los mismos 15 objetos una y otra vez para mejorar rendimiento y mejorar la gestión de la memoria de nuestro proyecto Java Script. A medida que se crea el objeto del juego, vamos a disparar automáticamente, crear proyectiles enemigos método. Escribamos ese método. Ahora aquí tenemos dos métodos que manejan proyectiles de jugador Copia las dos y las pego aquí. Como dijimos, vamos a tener un método que va a crear proyectiles enemigos piscina de objetos, crear proyectiles enemigos Establecemos el número de proyectiles enemigos, 215, por lo que el bucle correrá 15 veces Cada vez que se ejecuta, toma matriz de piscinas de proyectiles enemigos y empujará un nuevo objeto de proyectil enemigo la matriz usando la clase que acabamos También necesitamos un método que siempre que lo llamemos, obtendrá un objeto proyectil enemigo libre de la piscina para que el enemigo pueda dispararle Obtener método de proyectil enemigo ciclo sobre proyectiles enemigos conjunto de piscinas que acabamos de llenar con Tan pronto como encuentre el primero que tenga tres propiedades establecidas en true, tan pronto como encuentre el primer objeto de proyectil enemigo disponible inactivo, dejará de buscar y simplemente nos devolverá ese objeto Bien, tenemos toda la lógica en su lugar ahora podemos usar pool de objetos de proyectiles enemigos para disparar al jugador. Vamos a probarlo. 26. Proyectiles enemigos: Yo subo a mi clase enemiga Eagle Morph. Aquí creamos un método de disparo personalizado. Si recuerdas dentro, sacaremos un nuevo objeto de proyectil enemigo de piscinas de proyectiles enemigos Usando nuestro método personalizado get enemigo proyectil, verificamos si realmente logramos obtener un proyectil gratis porque podría ser que los 15 estén usados y no estén Pero si logramos conseguir uno de la piscina, llamaremos a su método de inicio. Sabemos que el método de inicio necesita las coordenadas X e Y para decirle al proyectil de dónde sobre lienzo debería comenzar a animarse Le pasamos las coordenadas X e Y del enemigo Eagle Morph que acaba de disparar este proyectil Ahora tengo que bajar aquí al método de render principal. Igual que dibujamos y actualizamos todos los proyectiles del jugador. Copio el bloque de puntuación y corro para cada método, por cada objeto dentro de proyectiles enemigos Tire de matriz, Cuidado con la P mayúscula aquí. Por cada proyectil enemigo en la matriz, llamo a sus métodos de actualización y dibujo y le pasamos contexto Vamos a intentarlo. Disparo y golpeo al enemigo. Aún no nos está disparando. Bien, voy aquí arriba a clase de proyectiles enemigos. Aquí queremos que los proyectiles animen hacia abajo en la En el eje y vertical. Este y plus equivale a esta velocidad. Aún así no disparan. Cambio el estilo de relleno a rojo. Los proyectiles viajan hacia abajo. Esta comprobación tiene que ser más de, digamos 200 píxeles. Bien, en realidad vamos a comentar esta línea por ahora. Consolo siempre nos ayuda. Nuestro proyectil consolo, sacamos de los proyectiles enemigos tiramos sacamos de los proyectiles enemigos tiramos Cuando disparo y golpeo al enemigo, puedo ver que este bloque de código no está funcionando en absoluto. No hay consolo. Bien, ya lo veo. Cuando golpeamos al enemigo con proyectil jugador, queremos llamar así a su método de disparo Ahora va a reaccionar. Eso es. Cuando golpee al enemigo, nos va a disparar. Ahora funciona perfecto. Queremos que el proyectil comience desde la mitad del enemigo, no desde su esquina superior izquierda Le paso X del enemigo más la mitad de enemigo con enemigo, más la mitad de la altura enemiga. Ahora vienen de la mitad. Eso es mejor. No queremos dibujar rectángulos rojos Queremos dibujar proyectiles enemigos mutados a nuestra imagen de dibujo de hoja de cálculo personalizada Le pasamos la imagen que queremos dibujar y las coordenadas x e y donde dibujarla, que dibujará toda la imagen en su tamaño original. Necesitamos crear esta propiedad de imagen de punto y apuntamos hacia la imagen a la que referenciamos en el índice HTML. Antes, le di una idea de proyectil enemigo. Estamos dibujando toda la hoja de sprites. Ahora bien, si le paso ancho y alto así, exprimirá toda la hoja de sprites en un área de un marco Como siempre, también necesitamos pasarla fuente X, fuente Y, ancho de origen y altura de origen para definir un cultivo en el área. Marco X definirá recorte horizontal en posición, Será cero, o uno, o dos, o tres. marco Y manejará el recorte vertical en área y será cero o uno. El argumento de origen X pasado para dibujar el método de imagen será fotograma X multiplicado por el ancho de fotograma de 50 píxeles. La fuente Y será fotograma Y veces la altura del fotograma de 35 píxeles. Lo pasamos con altura como ancho de fuente y alto de origen. Ahora cada proyectil es uno de los bebés alienígenas mutados al azar Supongo que cada transformación del ego tiene cuatro segmentos corporales en total. Cada vez que lo golpeamos con el ataque básico, pierde un segmento y nos devuelve un proyectil Si lo golpeamos con el láser, libera más de cuatro porque el láser marca más rápido y dispara este método de disparo con más frecuencia, queremos limitarlo a máximo de cuatro proyectiles por enemigo Yo lo llamo por ejemplo, esto tiros. Simplemente contará cuántos disparos ha sido disparado por este morfo águila cada vez que disparemos Aumentamos este hacer disparos por uno cuando el enemigo recibe daño de los ataques del jugador. Si este hacer disparos es menor a cuatro, disparará, disparará cuatro veces cuando este tiro de punto sea 012.3 Ahora los láseres liberan los cuatro proyectiles muy rápido, pero el enemigo no podrá disparar más proyectiles de vuelta a nosotros Después de eso, con el ataque básico, será un golpe, un 012.3 Ahora los láseres liberan los cuatro proyectiles muy rápido, pero el enemigo no podrá disparar más proyectiles de vuelta a nosotros Después de eso, con el ataque básico, será un golpe, un proyectil enemigo. ¿Hay algún respaldo? Con esta implementación, la probaré a fondo un poco más tarde. Ahora mismo tenemos 15 proyectiles enemigos en la piscina. En algún momento, me quedaré sin ellos. Necesito asegurarme de que se reinicien y regresen a la piscina cuando flotan fuera de la pantalla. Si la coordenada vertical del proyectil es mayor que la altura del área de juego, restablecerlo y devolverlo a la piscina de objetos disponibles Para ello, necesitaremos acceso a este juego. Lo pasamos como argumento y lo convertimos en una propiedad de clase. Esto es solo una referencia que apunta al lugar en la memoria donde se asienta el objeto principal del juego. Esto no es hacer una copia del objeto del juego. Cuando creamos proyectil enemigo, necesitamos pasar el juego como argumento Estamos dentro del objeto principal del juego. Aquí le paso esta palabra clave para probarla. Voy a tener sólo dos proyectiles enemigos en la piscina. Quiero verificar si están reiniciados en Debería solo conseguir dos liberados y piscina de proyectiles enemigos está vacía Tan pronto como se mueven fuera de la pantalla, se reinician y se pueden utilizar de nuevo. Esto funciona. Lo puse de nuevo a 15. 27. Interacciones de proyectiles: mismo Ahora mismo, proyectiles enemigos no interactúan con el jugador Comprobemos colisión entre proyectiles enemigos y el jugador Si nuestro método personalizado de verificación de colisión entre este proyectil enemigo, el jugador es verdadero Ten en cuenta que este bloque de código solo funciona para proyectiles enemigos activos, lo cual es ideal Si el proyectil enemigo activo choca con el jugador, reiniciamos el proyectil devolviéndolo de nuevo a También reduciremos la vida de los jugadores en uno. Comprobamos si las vidas de los jugadores son menos de una. Si lo son, pusimos el juego a la verdad. Disparo al enemigo dispara hacia atrás. Nos golpean 123. acabó el juego. Esto funciona bien. Quiero que el jugador pueda disparar a los proyectiles enemigos, pero sólo con el ataque básico proyectiles enemigos mutados son pequeños y tienen ojos grandes Pueden evitar fácilmente los rayos láser masivos. Sólo podemos golpearlos con el ataque básico. Esta es solo mi elección. Solo estoy tratando de darle cierta complejidad al juego y darle al jugador más razones para usar el ataque básico. Somos método de actualización de perspicacia en la clase de proyectil enemigo. Este código sólo se ejecuta cuando el proyectil enemigo está activo. Por cada objeto en el jugador, los proyectiles tiran matriz de verificación de colisión entre este proyectil enemigo y el proyectil Además, solo ejecutamos este código si el proyectil jugador está activo, si es propiedad libre es falso Si el proyectil enemigo activo y el proyectil jugador activo chocan, tomamos proyectil jugador y llamamos a Devolviéndolo de nuevo a la piscina. Llamaremos método de golpe personalizado sobre proyectil enemigo, pasándole daño de uno Si las vidas del proyectil enemigo son menos de uno, llamamos a su método de reinicio, devolviéndolo también a la piscina Cada proyectil enemigo en realidad tendrá cinco vidas en mi juego, Son bastante duros cuando los golpean Reducimos las vidas por daños. También vamos a reducir su velocidad a, digamos 60% de la velocidad actual. Tenemos la opción de destruirlos por completo, o simplemente para frenarlos y evitarlos de esa manera. Nuevamente, dándonos más opciones estratégicas, más opciones y más control. jugador puede decidir destruir estos duros proyectiles o simplemente ralentizarlos y dejarlos planos por Estoy jugando el juego y puedo ver que los nuevos proyectiles se están volviendo muy lentos y solo tienen una vida Juego un poco más tratando de controlar los proyectiles con mi ataque básico Sí, se vuelve más claro ahora ya que proyectiles enemigos son un charco de objetos, cuando los regresemos de regreso a la piscina, necesitamos restablecer sus vidas a cinco También puedo aleatorizar su Marco X y Marco Y para darles un fotograma diferente al de la hoja de cálculo, una mutación diferente Ya que estamos modificando su velocidad golpeándolos, también necesitamos restablecer esa velocidad al rango original de valores. Aquí abajo, manejamos la distribución de tipo enemigo. 30% de los enemigos en mi juego serán morfos de águila, 30% serán morfos de rinoceronte, y el último 40% serán morfos y el último 40% serán Estoy rodando al azar dos veces aquí. Eso no va a funcionar. Necesito refactorial el código. Espero que a estas alturas ya estés lo suficientemente cómodo con esta base de código. Puedes modificarlo y ajustar las reglas tú mismo. En mi juego, cada tipo de enemigo morph Águila puede disparar un máximo de cuatro proyectiles Estos proyectiles son duros y tienen cinco vidas, así podemos evitarlos o ralentizarlos Pero sólo con el ataque básico, ignorarán los ataques avanzados de rayo láser. Un morfo de águila con el láser liberará los cuatro proyectiles enemigos El ataque básico liberará un proyectil enemigo por golpe. Es muy divertido para mí diseñar un sorteo y codificar diferentes tipos de enemigos como este. Podría hacer diez episodios más implementando diferentes especies exóticas. Usted es el experto. Ahora te voy a dar otra hoja de cálculo con morph de calamar Ahora si quieres, puedes implementarlo como tu propio enemigo único. ¿Cómo va a funcionar tu quid morph? Házmelo saber en los comentarios. 28. Clase de enemigos de Squidmorph: Ahora que lo tenemos todo, déjame mostrarte lo fácil y rápido que es agregar más tipos de enemigos a nuestra base de código existente. El pliego que te estoy dando es bastante flexible. Puedes darle a este enemigo diferentes propiedades y comportamientos. Te daré una versión enemiga de tamaño regular y también una hoja extendida de ambos tamaños. Implementemos ambos ahora para que nuestro juego sea aún más diverso y divertido de explorar. Puedes descargar todos los activos artísticos en la sección de recursos a continuación. Lo pongo dentro de mi carpeta de proyecto y lo hago referencia aquí dentro de index imagen HTML con una fuente calamar morph Ng e ID serán morph de calamar. Ocultamos esa imagen con CSS porque solo queremos dibujarla con script Java sobre lienzo. La morph del calamar es un insecto espacial suave y flexible. Puede absorber muchos de nuestros proyectiles, pero notamos que cada vez que absorbe uno, se infla un Vamos a alimentarlo con proyectiles hasta que estalle. Construí esta hoja de extensión para crear un tipo enemigo inflable. Si le damos, digamos diez vidas, cada vez que le pegamos, simplemente crece un poco más grande. Pero esta hoja para untar funcionará aunque quieras diseñar tu morfología de calamar de manera diferente. Si le das solo dos vidas cuando lo golpeas con el tercer proyectil El resto de los fotogramas sólo se reproducirán en una secuencia rápida. Esta hoja de cálculo también funcionará para eso, espero que tenga sentido a lo que me refiero Pero déjame mostrarte código también. Básicamente, tenemos un enemigo que puedes modificar como quieras. El pliego que te estoy dando va a funcionar si el enemigo tiene algo 1-10 vidas. Si recuerdas aquí en la línea 161, tenemos nuestra clase enemiga padre que contiene los métodos y propiedades compartidas entre todos los diferentes tipos de enemigos. Entonces estamos extendiendo esa clase para agregar propiedades y métodos comportamientos específicos a tipos de enemigos individuales. Porque queremos que cada tipo de enemigo tenga una imagen diferente, diferente número de vidas, y así sucesivamente. Beetle Morph es nuestro simple enemigo básico. La morph de rinoceronte es una espalda blindada con un exoesqueleto grueso. Eagle Morph sacrificará sus propios segmentos corporales y nos los volverá a escupir Ahora agregaremos Squid morph, un enemigo inflable que se come nuestros proyectiles hasta que nuestros proyectiles En este punto, es muy sencillo agregar más tipos de enemigos porque ya tenemos gran parte de la lógica implementada de las lecciones anteriores. Sólo puedo copiar el bloque de código rinomorfo. Le renombro a Calamar Morph. Y señalamos aquí la hoja de sprites de Calamar morph. Y eso es todo, va a funcionar así. Aquí abajo en la línea 405, tenemos nuestra clase de olas. Crea ondas enemigas y las organiza en una cuadrícula como formación usando este método de creación personalizado que escribimos anteriormente. Estoy llamando al trandom aquí y aquí. Que no es la mejor manera de hacer esto porque quiero que el código sea fácil de leer. Quiero que sea muy fácil de leer. Qué porcentaje de la ola se compone de qué tipo de enemigo. Voy a rodar maotrandom solo una vez por enemigo en la parrilla. A partir de ese valor aleatorio, decidiremos qué tipo de enemigo se agregará. Voy a llamar a esa variable, por ejemplo, número aleatorio y será valor aleatorio 0-1 Así si el número aleatorio es menor 0.3 enemigo será Eagle Morph Alrededor del 30% de los casos, el 30% de cada onda estará hecha de Eagle morphs L. Si el mismo número aleatorio es menor a 0.6 cuando ese número aleatorio esté entre 0.3 y 0.6 otro 30% de la onda será rinoceronte morfos L, lo que significa que cuando el número aleatorio esté entre 0.6 y uno, el último 40% de cada onda estará hecho de morfos de escarabajos Espero que tenga sentido. En realidad tenemos cuatro tipos de enemigos. Voy a añadir una más. Si bloque aquí, tengo que tener mucho cuidado con los corchetes. Cuando hago esto, es fácil romper mi código con un solo soporte fuera de lugar Estamos probando morfos de calamar ahora. Los voy a añadir aquí. Puedo poner a águila morph aquí, por ejemplo. Aún no importa, porque quiero que la mayor parte de la ola, en realidad toda la ola esté hecha de morfos de calamar ahora, porque necesitamos probarlos Como puedes ver, ya funcionan. Y todo lo que hicimos hasta ahora es reutilizar el bloque de código para los morfos de rinoceronte Cuando juego el juego, puedo ver que necesitamos ajustar algunas cosas La lógica que tenemos aquí es que cuando golpeamos a un enemigo, cambiamos su hoja de cálculo por un fotograma horizontalmente Cuando las vidas del enemigo son menos de una. Como se define aquí en la línea 186, seguimos aumentando el fotograma X para reproducir la animación de explosión. Una vez que el fotograma X es más que el cuadro máximo, sabemos que podemos eliminar al enemigo porque toda su animación de explosión terminó de reproducirse. Para ver realmente esa animación de explosión, reviso mi hoja de cálculo de morph de calamar Y veo que necesito aumentar max frame a, digamos 16. Para asegurarse de que todos estos marcos se muestren antes de que el enemigo sea eliminado del juego. Ahora le disparo. Yo agoto las cuatro vidas. Se reproduce el resto de la hoja de propagación que muestra al enemigo inflar y estallar, diseñé la hoja de propagación para que funcione cuando el enemigo tenga una vida o nueve vidas Por ejemplo, simplemente saltamos cuadro por cuadro a medida que lo golpeamos. Siempre que agotemos las vidas, solo jugará los fotogramas que queden Si hago esto, noto que tenemos un error. Algo está empujando el marco Gs hacia atrás cuando ya estaba mostrando el marco de explosión. Es porque estamos moviendo huevos de marco aquí y también aquí. Asegurémonos de que estas dos líneas de código sean mutuamente excluyentes. Y no se ejecutan al mismo tiempo en el mismo bucle de animación. El bloque de código sólo se ejecuta cuando el enemigo tiene menos de una vida. Aquí me aseguro que esta línea sólo corre cuando esta vida es más o igual a una. Esto debería arreglar el error. Si vas a la línea 243, le das a tu morph de calamar dos o cinco o nueve vidas Puedes probarlo y ver que la hoja de cálculo funcionará en todos estos casos También preparé una hoja de cálculo Bo con calamar. Moorphi los agregó al sprite Bo existente. Puedes descargar esta nueva versión en la sección de recursos a continuación, llamó jefe ocho porque habrá ocho criaturas jefes diferentes. Los traigo al proyecto. Aquí jefe ocho, perro, e ID es jefe ocho. Tenemos ocho imágenes de jefe diferentes en esta hoja de cálculo. Ocho filas diferentes. Lo escondo con CSS. Voy a mi clase de jefe y hago referencia a esa imagen aquí en la línea 347. Ahora puedo establecer frame y para que sea un valor aleatorio, 0-7 Así, comento solo para probar la hoja de cálculo, estableceré frame Y Aquí abajo, quiero que las olas comiencen con un jefe para que sea más fácil probar. I Sat Frame X20, Frame Y zero es esta versión de la mantis La fila uno es este marco Y dos nos dará esta fila. Marco Y tres es este marco Y cuatro nos dará el nuevo morph de calamar Se puede ver que reacciona al ser golpeado por una animación en silla de montar de sus dos tentáculos frontales Igual que los otros jefes de antes. Cuando agotamos sus vidas, solo reproduce el resto de la hoja de cálculo en una agradable animación de explosión a baja velocidad de fotogramas El cuadro seis nos dará esta extraña criatura espacial. Observe que todos ellos animan un poco cada vez que los golpeamos con nuestro marco básico de ataque y siete nos darán este calamar espacial Sabemos que todo funciona. Entonces estableceré frame y en un valor aleatorio 0-7 dándonos uno de estos ocho jefes aleatoriamente aquí abajo. Quiero que mi juego comience con una ola enemiga. Quiero que cada ola esté hecha de 25% morfos de calamar, 25% morfos de rinoceronte, Ego morph es en realidad muy Lo hacemos solo el 20% de la ola, y el último 30% estará hecho de beatle morphs, el enemigo simple y básico Si usas los mismos ajustes que utilicé para este juego, intenta jugarlo el mayor tiempo posible y hazme saber tu puntuación en los comandos. Puede llegar a ser bastante difícil y chal***ging a medida que avanza el juego Y tanto la salud como el tamaño de las olas siguen creciendo. Bueno entonces si lograste llegar hasta aquí, este fue un gran proyecto y aprendimos tantos trucos y técnicas diferentes. Si te divertiste codificando conmigo, puedes revisar mis otras clases. Te veré más tarde. 29. Clase de enemigos con langosta: Análisis, confirmado contacto enemigo exitoso. Parece que nuestros láseres están desencadenando una mitosis celular en Al peculiar, ¿qué significa eso? Si les disparamos, se dividen en múltiples criaturas más pequeñas Ah, entonces primero tenemos que dividirlos y después nos dirigimos a los clones. Sí, esa parece ser la estrategia más potente. Dividamos y conquistaremos. Realicé otros proyectos con activos artísticos que son compatibles con este juego. Déjame mostrarte cómo tomar una hoja de sprites de ese juego absoluto y usarla aquí. Es muy sencillo. Puedes descargar la hoja de cálculo en la sección de recursos a continuación. Puse la imagen en mi carpeta de proyectos y la hago referencia aquí. La fuente es la langosta. Moorphpng ID será bogavante morph. Ocultaremos la imagen con CSS aquí ya tenemos toda la lógica en su lugar. Sólo puedo copiar este bloque de código que usamos para Squidmorph. Max frame será de 14 vidas serán ocho, siempre y cuando las vidas sean más de una. Cada vez que golpeemos al eneme, aumentaremos el fotograma en la hoja de cálculo en uno, progresando lentamente Este eneme se divide en múltiples clones más pequeños cuando lo golpeamos Ahora solo tenemos que decidir qué porción del total de enemigos se hará de este nuevo tipo de enemigos. Yo creo nuevo L si bloque. De nuevo, tengo mucho cuidado con los corchetes porque un solo soporte incorrecto puede romper mi código. Puedes elegir tus propios valores. Aquí tenemos cinco tipos diferentes de enemigos. Creo que es apropiado hacer que cada enemigo escriba aproximadamente el 20% de la ola que estamos tratando con números aleatorios. Algunas olas tendrán más morfos de langosta que otras. Lo pruebo, muta. Cada vez que lo golpeo, se divide en clones que funcionan bien Realicé otro juego que se centra más en este error espacial en particular. Si te interesa, te veo en la otra clase donde construimos un juego de defensa planetaria. Tener múltiples tipos de enemigos como este hace que el juego sea más interesante de explorar. Y agregamos aquí a los enemigos para que el jugador piense y haga estrategias un poco A medida que el juego se vuelve más difícil, tenemos que priorizar objetivos, usar diferentes tipos de armas y posicionarnos bien Aquí estamos, logrando todo eso creando una variedad de tipos de enemigos con diferentes propiedades. Esto fue divertido. 30. Proyecto 2: defensa de planetas con JavaScript: Cada juego está hecho de unos pocos bloques básicos de construcción. Una vez que entiendas estas técnicas, podrás ser creativo, combinar estos elementos de diferentes maneras y crear muchos juegos únicos fácilmente. En esta clase, cubriremos muchas de ellas. Desde controles de teclado y mouse hasta consejos y trucos de animación de sprites amigables con el rendimiento Y el enfoque principal esta vez es el patrón de diseño del grupo de objetos. Lo usaremos para enemigos y para proyectiles de jugadores. Porque volver a usar nuestros objetos en lugar de crearlos y descartarlos una y otra vez puede ser un beneficio de rendimiento masivo Hoy estamos construyendo un juego animado de dos D completamente desde cero sin frameworks y sin bibliotecas usando simplemente van script Java y análisis HTML five canvas. Confirmado contacto enemigo exitoso. Parece que nuestros láseres están desencadenando una mitosis celular en esta especie ¿Qué significa eso? Si les disparamos, se dividen en múltiples bichos más pequeños Ah, entonces primero tenemos que dividirlos y después nos dirigimos a los clones. Sí, esa parece ser la estrategia más potente. Dividamos y conquistaremos el interrogam gira alrededor del planeta Literalmente, controlamos una nave espacial y necesitamos defender al planeta asteroides y de todo tipo de extraterrestres y bichos espaciales Este proyecto es completamente independiente e independiente, pero diseñé los activos artísticos para que sean compatibles con el juego Space Invaders que construimos antes Y al final, te mostraré cómo tomar hojas de sprites de ese juego e implementarlas en este proyecto con solo unas pocas líneas de código extra Esta clase es para programadores creativos que quieran aprender sobre los fundamentos del desarrollo de dos juegos D, así como para principiantes de Javascript que quieran practicar sus habilidades de programación orientada a objetos en un divertido proyecto visual e interactivo Vamos como de costumbre, comenzamos con una página web genérica normal en blanco donde vinculamos la hoja de estilo CSS, creamos un elemento HML five canvas con un ID de canvas one Y vinculamos script archivo JS en estilo CSS. Le doy a lienzo un borde, pongo imagen PNG de fondo en mi carpeta de proyectos y la uso como fondo para mi lienzo. Puedes descargar todos los activos de arte del proyecto en la sección de recursos a continuación. Quiero el lienzo en medio de la página tanto vertical como horizontalmente. Entonces utilizo la técnica de posicionamiento absoluto. Por ejemplo, todo lo demás se hará con Javascript. En el archivo JS script, utilizaremos imágenes y otros recursos en este proyecto. Tenemos que esperar a que todos los activos estén completamente cargados y disponibles antes de ejecutar cualquier código Javascript que dependa de ellos. Por esa razón, pondré mi código dentro una función de devolución de llamada en un listener de eventos de carga Cuando se active el evento de carga y todos los recursos estén listos, ejecutaremos un código Javascript. Primero, señalo Javascript hacia mi elemento canvas usando su ID. Me senté contexto a dos D. Como de costumbre, siempre tenemos que hacer esta línea. Cuando estamos montando lona, estamos haciendo un juego de dos D. Hoy puse lienzo a 800 veces 800 píxeles aquí, lo que revelará la obra de arte de fondo completa. 31. L2 Planet y clase de juegos: Puedo definir mi clase planeta aquí arriba, fuera del oyente de eventos Siempre y cuando me asegure de que solo instancie esa clase desde dentro del listener de eventos load Posteriormente, déjame mostrarte todos los objetos de este proyecto necesitarán coordenadas XY y radio porque todo será un círculo y estaremos detectando colisiones entre ellos. El método Draw tomará el contexto como argumento para especificar en qué lienzo queremos dibujar y también para mantener nuestras clases lo más independientes posible. En su interior dibujaremos un círculo que representa al planeta. Pasamos las coordenadas XY del método de arco del radio del punto central del círculo Ángulo de inicio de la trayectoria de forma circular y ángulo que será método pitones a un círculo completo En radianes, llamamos método de relleno para rellenar la ruta con el color negro predeterminado Dentro del listener de eventos de carga. Instancio la clase usando la nueva palabra clave. Ahora puedo usar esta variable planeta que tiene una instancia de clase planeta. Puedo llamar al método de dibujo que definimos en la línea siete. Se espera contexto. Lo paso CTX de la línea 15. Tenemos nuestro planeta aquí dibujado en las coordenadas 200, 200. Puedo cambiar su radio, puedo acariciarlo en su lugar. Vamos a establecer el estilo de trazo blanco y tal vez el ancho de línea a dos píxeles. Podemos moverlo y redimensionarlo ajustando estos valores Esta es nuestra clase planeta. De hecho quiero tener una clase de juego principal, el cerebro de la base de código. Dibujaremos, actualizaremos y controlaremos todo desde aquí. Constructor esperará lienzo como argumento. En su interior lo convertimos en una propiedad de clase. De esa propiedad, extraeremos ancho y alto. Porque quiero que las propiedades de ancho y alto de nuestro objeto principal del juego sean las mismas la anchura y la altura del elemento de lienzo real que dibujaremos y animaremos el juego En lugar de crear una variable llamada planeta e instanciar clase planeta, esa manera, voy a crear una propiedad aquí llamada esto La clase Planet Game también tendrá un método de render. Este método se llamará una y otra vez para cada fotograma de animación. Actualizando y redibujando nuestras formas y objetos, necesitaremos contexto como argumento interior Tomaré esta propiedad planetaria de la línea 19, que ahora tiene una instancia de clase planeta. Y llamo a su método de dibujar asociado desde la línea siete, pasando así el argumento de contexto. Ahora estamos tomando esta clase planeta y la estamos instanciando dentro del constructor de clase de juego Puedo borrar estas líneas y en su lugar voy a crear una instancia de clase de juego. Se espera lienzo como argumento. Lo paso lienzo de la línea 27 a través de eso. El juego ahora tendrá acceso a las propiedades de ancho y alto del lienzo y la anchura y altura del juego se establece automáticamente en 800 veces 800 píxeles. Puedo tomar esa variable de juego de la línea 34 y puedo llamar al método render desde ella. Lo definimos en la línea 21 y podemos ver que espera contexto como argumento. Lo paso CTX de la línea 28. Lo importante a entender aquí es que el código dentro de un constructor de clase se ejecuta una vez en el punto en que creamos una instancia de esa clase usando la nueva palabra clave. Cuando creo una instancia de clase de juego aquí en la línea 34, también ejecuta automáticamente todo el código línea por línea y se crea una instancia de clase planeta y se guarda como esta propiedad planeta aquí en la línea 19. A mí me gusta hacerlo de esta manera. Creo que es más limpio y más fácil navegar En la estructuración de nuestro código así en clases y objetos se llama programación orientada a objetos. Todo este proyecto será una sencilla base de código orientada a objetos. Ahora la clase planeta está conectada a la clase de juego. Y aún puedo cambiar propiedades del planeta ajustando cualquiera de estos valores. estas mismas propiedades ahora también se puede acceder desde el interior del objeto de juego a través de esta propiedad del planeta, que se volverá importante más adelante. ¿Y si quiero que esta conexión sea de dos vías? También quiero que las propiedades que se sientan dentro la clase de juego sean accesibles desde dentro de la clase planeta. Hazlo pasando una referencia que apunte hacia el objeto principal del juego al constructor de clase planeta así. Tenga en cuenta que los objetos en el script Java son los llamados tipos de datos de referencia. Al hacer esto no estoy creando una copia de la clase de juego aquí. Solo estoy creando una referencia que apunte a un espacio en la memoria donde se asienta el objeto principal del juego. Cuando alguna propiedad que se asiente en el objeto principal del juego actualice sus valores, esos cambios serán inmediatamente visibles desde dentro de la clase planet let class constructor espera una referencia al juego como argumento aquí donde llamo al constructor planeta, necesito pasarle esa referencia de juego. Ya que estamos dentro de esa clase de juego, le paso esta palabra clave que cuando se usa aquí, representa todo el objeto del juego. Ahora tenemos una conexión bidireccional agradable y sencilla entre el planeta y las clases de juego. Podemos probarlo accediendo a la propiedad width desde la línea 18 desde el interior del planeta. Porque quiero que el planeta esté exactamente en medio del juego de área de juego con Tams 0.5 Si lo que estamos cubriendo aquí es demasiado rápido o demasiado complicado para ti, echa un vistazo a la clase anterior donde voy mucho más lento en un ritmo amigable para principiantes Para el resto de este proyecto, supongo que ya conoces los conceptos básicos de Javascript y la programación orientada a objetos. Puedes descargar la imagen del planeta en la sección de recursos a continuación. Lo traigo al proyecto como elemento IMG. Aquí en índice HTML, le doy una identificación de planeta. Podemos ver la imagen aquí. Quiero ocultarlo. dibujaremos, pero con Javascript en Canavas no así Habrá más imágenes en el proyecto. Voy a ponerlos todos dentro de un diff con una clase de hidden en CSS. Establecí este diff oculto para que no muestre ninguno. Genial, lo llevaremos al proyecto aquí como esta imagen propiedad en clase planeta. Ahora puedo llamar construido en dibujar método de imagen. Y le paso la imagen que queremos dibujar y las coordenadas X e Y, donde dibujarlo sobre lienzo x e y de un círculo representa el punto central alrededor del cual se dibuja el círculo. Pero con rectángulos e imágenes sobre lienzo las coordenadas X e Y definen su esquina superior izquierda Si quiero alinear la imagen rectangular sobre la caja circular It, normalmente la desplazaría por radio, que para todos los demás objetos de este juego, será exactamente la mitad de su ancho. Pero para la imagen del planeta, hice un poco más grande porque tenemos estas nubes a su alrededor. Y realmente no quiero que esas nubes estén activas en colisiones. Los enemigos y asteroides solo colisionarán con el planeta cuando golpeen el cuerpo real del planeta, no las nubes Por esa razón, tengo que compensarlo un poco más. Digamos por 100 pixeles. Ahora alineamos el planeta circular en caja sobre la imagen rectangular. Por ahora mantendremos el círculo blanco. Representará la zona de colisión del planeta. 32. Posición del ratón L3: Como dijimos antes, cualquier código dentro de un constructor de clase se ejecuta automáticamente en el punto en que creamos una instancia de esa clase usando la palabra clave new. Ya estoy aprovechando eso y estoy creando automáticamente una instancia de clase planeta en el momento en que creo una instancia de clase de juego. En ese mismo punto cuando creamos juego usando la nueva palabra clave, también quiero que se apliquen algunos oyentes de eventos Escucharemos el evento mousemove normalmente con los oyentes de eventos, crearíamos una función de devolución de llamada estándar y algo de código para ejecutarse dentro cada vez que se dispara ejecutarse dentro cada vez El reto aquí es que estoy poniendo el oyente de eventos dentro de un constructor en un objeto Estamos dentro de la clase de juego y estamos llamando al oyente de eventos desde el objeto de ventana con una función de devolución de llamada regular Perderíamos el acceso a estas propiedades de punto que se sientan en la clase de juego para mantener ese acceso, para asegurarnos de que este oyente de eventos recuerde que fue definido léxicamente dentro la clase de juego y para permitirnos manipular estas propiedades en la clase de juego desde una función de devolución de llamada en este oyente de eventos mousemove, tenemos que más simple podemos usar funciones ES seis flechas en impuestos aquí. Debido a que una de las características especiales de las funciones de flecha en el script Java es que heredan automáticamente este curado del ámbito padre, tendrá como argumento el objeto de evento autogenerado como argumento el objeto de evento Le doy un nombre de variable. Por ejemplo, dado que esta función sólo tiene un argumento, los corchetes no son obligatorios. Puedo borrarlos así. Ahora, cada vez que se activa el evento mousemove, objeto de evento autogenerado Vamos a consolog ese objeto para que podamos inspeccionarlo porque ponemos el oyente de eventos dentro del constructor de clase de juego El oyente de eventos se aplicará automáticamente cuando se ejecute esta línea de código, cuando se cree una instancia de clase de juego usando la nueva palabra clave Ahora muevo el mouse sobre lienzo y veo esos objetos cada vez que toma un evento de movimiento del mouse. Si abro el objeto de evento mouse, veo que contiene todo tipo de información sobre el evento mouse que acaba de suceder. Lo que necesito son las coordenadas X e Y del ratón. Podemos ver esas propiedades aquí. Quiero que esas coordenadas del mouse estén disponibles en toda mi base de código, no solo desde dentro del oyente de eventos mousemove Voy a crear este objeto de ratón de punto en la clase principal del juego, y le doy propiedades x e y. Usamos la función ES six arrow aquí, así que podemos usar este punto desde dentro de la función callback De hecho puedo tomar este punto, mouse dot x de la línea 25. Y puedo ponerla igual a x a la exposición horizontal actual del ratón También hago lo mismo para la coordenada Y vertical. Bien, a medida que movemos el mouse sobre lienzo, estamos tomando las coordenadas x e Y del evento y asignándolas a nuestra propiedad de mouse de punto personalizada para que estén disponibles en todo el proyecto Podemos probar qué tan precisas son estas coordenadas dibujando una línea desde la mitad del planeta hasta el ratón. Queremos trazar una línea simple. Entonces empezamos por mendigar camino. Mover dos método definirá las coordenadas x e y de inicio de la línea. Será la mitad del planeta. El método de la línea dos definirá las coordenadas finales. Será la posición actual del ratón. Ahora acariciamos esa línea para dibujarla realmente sobre lienzo. La línea no reacciona porque estamos llamando al método render solo una vez en la primera carga de página. Queremos llamarlo una y otra vez para cada fotograma de animación para ver algún movimiento. Vamos a crear una función personalizada de bucle de animación simple. Yo llamo Animate. Ponemos dentro esta llamada de render. Luego llamamos construido en el método de marco de animación de solicitud que se asienta en el objeto de ventana. Pero esa parte es opcional. Puedo borrarlo. Javascript sabe dónde encontrar ese método sin que tengamos decirle que está en el objeto window. Lo paso animar para crear el bucle. Yo lo llamo una vez en la primera página cargar así para iniciar realmente el primer bucle. Ahora mientras muevo el ratón, método render corre y dibuja la línea desde el planeta hasta el ratón como definimos en las líneas 38.39 Se ve así porque vemos pintura vieja Vemos todos los fotogramas de animación anteriores. En realidad solo queremos ver el fotograma de animación actual. Elimino todo el contenido del lienzo entre cada fotograma usando el método de rectángulo claro incorporado. Despejamos todo el lienzo desde las coordenadas 00 hasta el ancho y alto del área del lienzo. Ahora estamos animando la línea y podemos verificar si las coordenadas que estamos capturando del evento del mouse se alinean con nuestros objetos de juego Todo está bien ahora, pero mira lo que pasa cuando hago la ventana más grande porque el lienzo no es pantalla completa. Esas coordenadas x e y no dan cuenta del espacio en blanco entre la página web y el elemento canvas. De pronto las coordenadas están completamente equivocadas. La línea ya no va del planeta al ratón. Podemos compensar esas coordenadas del ratón contabilizando el espacio en blanco alrededor del lienzo de forma manual o incluso más fácil. El objeto de evento Mouse ya hace eso por nosotros automáticamente. Nos da esas coordenadas ajustadas. En lugar de usar estas propiedades X e Y, usaremos offset X y offset Y a partir de aquí. Reemplázalos aquí. Ahora la línea irá exactamente del ratón a la mitad del planeta. Sabemos que estamos capturando las coordenadas correctas del ratón. Perfecto. Las propiedades Desplazamiento X y Desplazamiento Y son útiles para proyectos en los su lienzo no es Pantalla completa. Borro el consolog. 33. Nave espacial para jugadores de L4: Tiempo para crear la clase de jugador. Su trabajo será administrar las imágenes, comportamiento y los controles del jugador No estoy seguro si lo dejé claro, pero ya tenemos una base de código completamente animada. No estoy animando y moviendo el planeta alrededor, pero ya puedo hacerlo Si quiero ver esto, sabemos que el método de render se ejecuta y otra vez para cada fotograma de animación. Puedo aumentar la posición horizontal del planeta un píxel por fotograma y obtenemos movimiento. Esta base de código ya está completamente lista para todo tipo de animaciones. Simplemente no estoy moviendo las cosas todavía. Puedo mover el planeta horizontalmente. Puedo moverlo verticalmente hacia abajo y hacia arriba. Ahora solo se ve estático, porque aún no estoy moviendo las cosas. Llegaremos pronto. que hicimos con la clase planeta. Necesito acceso a propiedades y métodos que se sientan en la clase de juego. Desde dentro de la clase de jugador, crearé una propiedad, una referencia apuntando hacia el objeto principal del juego. Como dijimos antes, todos los objetos activos en nuestro juego necesitan tener coordenadas x e y y radio. Para que podamos usar estos valores para detección de colisiones entre círculos más adelante. Además, todos los objetos en esta base de código a partir de ahora tendrán un radio de 40 píxeles. El radio es un semicírculo. Los fotogramas de sprites rectangulares serán 80 veces 80 píxeles. activos artísticos de este proyecto son totalmente compatibles con el juego principal Space Invaders que construimos antes Siéntete libre de tomarlos y usarlos en ese proyecto también. Si quieres, te mostraré una manera divertida, cómo hacerlo en una lección extra sobre ese proyecto. Estoy seguro de que algunos de ustedes pueden averiguarlo. Incluso sin mi ayuda, podría ser divertido agregar el tipo de enemigo único que estaremos implementando en este proyecto a ese proyecto anterior también. Solo como ejercicio, me aseguré de que todo sea compatible. Como es habitual, el jugador tendrá un método de empate y nosotros dibujaremos un círculo que represente al jugador. Siempre que dibujamos un círculo, tenemos que comenzar con el camino de inicio. Utilizamos método para mapear la ruta. De hecho, lo dibujamos en lienzo usando el método de trazo o película incorporado. Esta es la clase de jugador. Simplemente vamos a repetir el patrón que hicimos con el planeta. Lo instancio como esta propiedad de jugador. Aquí llamaremos al método de sorteo del jugador. Definimos en la línea 24 dentro del render así, jugador se moverá. Voy a necesitar un método de actualización si quiero que el planeta se mueva de alguna manera, por ejemplo, rotar, tendría que darle un método de actualización así también. Por ahora, vamos a aumentar la exposición horizontal del jugador por un cuadro de animación Siler pick Sólo una prueba, yo llamo al método de actualización que acabamos de definir. A partir de aquí el jugador se mueve porque render se llama una y otra vez desde el interior del bucle de animación en la línea 75. También podríamos adjuntar jugadores posición horizontal al ratón. Así, quiero que el jugador gire alrededor del planeta y siempre mire la dirección hacia el ratón. Esta parte será un poco más desafiante de implementar para principiantes, pero una vez que logremos que funcione, el resto del juego será fácil. Primero, traeré la imagen del jugador al proyecto. Puedes descargarla en la sección de recursos. Abajo, le doy a clase de jugador esta propiedad de imagen y señalé hacia esa nueva imagen que acabamos incluir llamo método de imagen construida en dibujar. Y pasé la imagen y las coordenadas X e Y. Ahora si movemos al jugador tanto círculo como imagen se mueven juntos así. Quiero desviar la imagen rectangular para que se asiente directamente encima del hit box circular. Yo desfase X e Y por la mitad del ancho del jugador y la altura por el valor del radio. Preparé todos los activos artísticos para este proyecto en tamaños específicos que necesitaremos en el juego. Hacer esto me ahorra mucho trabajo ahora mientras estoy codificando, porque las imágenes están listas para funcionar, y no necesito escalar ni cambiar el tamaño de nada con código, lo que de todos modos sería un desperdicio en términos de rendimiento 34. Matemáticas L5: Quiero mover al jugador por el planeta dependiendo dónde se encuentre actualmente el ratón en relación con el planeta. Escribiremos toda esa lógica en un método reutilizable. Yo llamo, por ejemplo, calcular Lo estoy haciendo de esta manera. Podremos reutilizar este método posteriormente para hacer que los enemigos se enfrenten hacia el planeta a medida que nos atacan. El método tomará el objeto A y el objeto B como argumentos y calculará una dirección vectorial entre estos dos objetos. Digamos que tenemos dos objetos que tienen la posición x e y. Por ejemplo, el punto central del planeta y el cursor del ratón. Estos dos objetos se sientan en algún lugar de la zona de juego. Queremos calcular la distancia entre estos dos objetos en el eje x horizontal. Primero, será la posición horizontal del objeto A menos la posición horizontal del objeto B. Hacemos lo mismo para el eje vertical. Ahora tenemos dos lados de un triángulo rectángulo imaginario. Y queremos calcular la distancia entre estos dos puntos, en este caso entre planeta y ratón, por ejemplo. Estamos calculando una distancia entre dos puntos en un espacio de dos D. Necesitamos calcular un hipotenus de este triángulo rectángulo imaginario Ángulo recto estaría aquí. Esta es la hipotensión del triángulo rectángulo. El lado más largo opuesto al ángulo recto. Este es un caso de uso perfecto para fórmula del teorema de Pitágoras que se vería así Pero como estamos usando Javascript, es aún más simple porque tenemos disponible un método hipotenso incorporado Sólo tenemos que pasarlo de este lado y este lado, y nos va a dar la longitud del hipoteno, que en este caso es la distancia real entre el objeto A y el objeto B. Pasamos aquí como argumentos Ahora solo necesito dos números muy pequeños que apunten en cierta dirección, vertical y horizontalmente. Y la combinación de estos dos valores nos dará un vector de dirección entre el objeto A y el objeto B. Lo usaremos para hacer que la nave espacial jugador apunte lejos del punto central del planeta hacia el cursor del ratón, voy a reducir mis vectores a un rango de valores entre menos uno y más uno comprobando la relación entre x e hipotenus Distancia, esa será la dirección horizontal porque la distancia la hipotensión siempre es mayor que x. porque el ratón puede estar a la izquierda o a la derecha del planeta El valor resultante aquí será cualquier cosa entre menos uno y más uno, pero nunca puede ser más de uno porque el Dx siempre es menor que distancia menor que hipotenus. Ten en cuenta que el ratón puede estar a la derecha del planeta, pero también a la izquierda así por ejemplo. Es por eso que el valor de la relación resultante también puede ser negativo. En fin, ahora tenemos todo lo que necesitamos. Sabemos en qué dirección necesitamos empujar un objeto si queremos empujarlo hacia otro objeto o alejarlo de otro objeto, mx y mis valores nos están diciendo que empuje horizontal y vertical haré este método calc Am devuelva una matriz de valores porque necesitaremos múltiples valores De aquí para posicionar el jugador hit box y luego para rotar correctamente la imagen del jugador, tenemos que recordar en qué orden estamos devolviendo estos valores. Sabemos que cuando llamamos a Calc Am, devolverá una matriz donde el elemento con un índice de cero es la dirección horizontal entre el objeto A y el objeto B. Valor con un índice de uno es la dirección vertical El índice vectorial vertical dos es la distancia entre a y B horizontalmente. El índice tres es un D Y la distancia entre el objeto A y el objeto B verticalmente. Sé que esto fue un montón de matemáticas. Lo siento, ya casi terminamos. Ahora bien, si es la primera vez que usas esta técnica, no te preocupes demasiado por ello. Sólo tienes que seguir lo que estoy haciendo. Necesitas usarlo con más frecuencia Para otros proyectos, eventualmente tendrá más sentido para ti. La repetición realmente ayuda con esto. Por ahora, solo entiende que escribimos un método que nos da una distancia entre dos puntos en un espacio de dos D. Y ahora usaremos esos valores para apuntar y rotar objetos acercándose o alejándose unos de otros. Quiero mover al jugador por el planeta, siempre de cara al ratón. Esto lo puedo implementar de muchas maneras. Por ejemplo, puedo darle al jugador esta propiedad dot am para cada fotograma de animación. Voy a establecer este punto una propiedad a ese método personalizado calc Am que acabamos de escribir Haremos que calcule y produzca esos cuatro valores y los devolveremos como una matriz. Ese método nos da la distancia y dirección entre dos puntos. Entonces quiero verificar esos valores entre mouse y el planeta. La posición relativa entre el ratón y el planeta determinará hacia dónde se enfrenta el jugador. Voy a consular este punto am y obtendremos esa matriz con cuatro valores que devuelve el método Lkm por razones de rendimiento De hecho, tendrá sentido actualizar solo este punto. Apunte solo cuando se activa el evento de movimiento del mouse. Porque si el ratón no se mueve, estamos recalculando el mismo valor y otra vez para cada fotograma de animación, lo cual no es necesario Como dijimos antes, el método calc devuelve la dirección horizontal, la dirección vertical, la distancia horizontal y la distancia vertical, en este caso entre el ratón y el planeta Porque esos son los dos valores que le pasamos como objeto A y objeto B. Llegaré hasta aquí, así borro el consolo Ahora puedo usar estos valores para rotar el reproductor hacia el ratón. posición horizontal del jugador siempre estará comenzando desde la mitad del área de juego, que en este caso también es la posición x horizontal del planeta, el punto central del círculo del planeta. Solo queremos agregarle ese pequeño valor vectorial direccional. Pero debido a que ese valor es muy pequeño entre menos uno y más uno, apenas veríamos el movimiento. Yo lo multiplico por veces, digamos 50. Perfecto. Eso ya funciona horizontalmente. Puedo hacer lo mismo para la posición y vertical. Vamos desde el punto central del planeta hacia el ratón. Y potenciamos de nuevo ese vector multiplicándolo por Sí, eso funciona. El número que usamos aquí para multiplicar el vector por, ahora influirá en la distancia entre el planeta y la nave espacial jugador Digamos que quiero que la distancia sea igual al radio del planeta. Obtenemos algo de movimiento interesante. Si los valores vertical y horizontal no coinciden así, sería útil que el planeta fuera elíptico, por ejemplo Ahora el punto central del círculo de jugadores se mueve a lo largo del radio del círculo del planeta. Si quiero sacarlo del planeta por completo, tenemos que empujarlo por la mitad del círculo de jugadores más, más el radio del jugador. Así, necesitamos usar corchetes para asegurarnos de que la suma ocurra primero, y luego el valor resultante se multiplica por el vector. Puedes jugar con los valores si estás codificando a lo largo, y esto dejará claro lo que está sucediendo. 35. Rotación de lienzo L6: Ya no necesitamos esta línea. También queremos rotar la imagen del jugador para que siempre mire hacia el ratón, lejos de la mitad del planeta. Voy a darle a jugador una propiedad a la que llamo, por ejemplo este ángulo. Cuando llamas a canvas rotate, transformará el estado del lienzo. Entonces todo lo dibujado después de esa rotación sería girado. Solo queremos que la imagen de la nave espacial rote. Restringir cualquier cambio al estado del lienzo envolviéndolo entre los métodos integrados de seguridad y restauración. Guardamos el estado del lienzo, hacemos cualquier cambio aquí, luego llamamos a restaurar y restablecer todo lo demás formas dibujadas en lienzo después de que no se vean afectadas La rotación del lienzo es un poco poco intuitiva si nunca la habías visto antes Básicamente recuerda esto. Para rotar algo sobre lienzo, primero hay que traducir el punto central de rotación, que es lo mismo que las coordenadas 00 en lienzo Tienes que traducir eso sobre las coordenadas de la mitad del objeto que deseas rotar. Entonces llamas rotar, lo pasas ángulo, quieres rotar en radianes Después dibujas ese objeto que quieres que se rote. Pero dibujamos ese objeto no en sus coordenadas x e y, sino en las coordenadas 00. Porque la posición real del objeto ahora se expresa en los valores que pasamos a traducir. Creo que es seguro decir que la rotación del lienzo es mucho más complicada que la rotación CSS. Pero en realidad es fácil si lo haces algunas veces. De hecho, hagámoslo en código. Quiero rotar la nave espacial jugador. Traduzco el punto central de rotación sobre este punto x y z punto y coordenadas del jugador El punto central de rotación también es coordenadas 00 en lienzo. Ahora estoy empujando demasiado lejos al jugador porque estoy expresando su posición aquí por translate y lo estoy empujando aún más de ahí por las coordenadas dx punto Y. Este podría no ser el mejor ejemplo para enseñar a alguien la rotación porque aquí tenemos la imagen de la nave espacial del jugador y también el círculo de hit box Así que aquí están pasando muchas cosas. exposición del círculo es cero y la exposición de la imagen es menos radio Debido a la diferencia entre cómo se dibujan rectángulos y círculos en lienzo, x e y es el punto central de un círculo, pero es una esquina superior izquierda de un rectángulo o imagen cuando estamos dibujando formas en En fin, ahora lo arreglamos y estamos traduciendo con éxito el punto central de rotación sobre la nave espacial jugador Ahora cuando gire, rotará desde la mitad de la imagen del jugador, que es exactamente lo que quiero construido en Canvas. El método Rotate toma el valor del ángulo de rotación en radianes. Le paso este ángulo de punto desde la línea 25. Esto es rotación por 0 Radianes método pi girará la nave espacial así por 3.14 radianes, que es El método de medio círculo pi por dos es 6.28 radianes, 360 grados Giramos la nave espacial por todas partes. La mitad pi es de 1.57 radianes, o aproximadamente 90 grados, estamos mirando hacia arriba Ahora espero que tenga sentido. Necesitamos que el ángulo sea de 0-6 0.28 radianes, 0-2 pi. Necesitamos ese valor de ángulo para que coincida con la dirección entre el cursor del ratón y el punto central del planeta, para que el jugador esté mirando hacia el ratón. Para ello, tenemos otro útil método incorporado llamado método A 102, que nos da un ángulo en sentido antihorario en radianes entre el eje x positivo y una línea proyectada desde la coordenada 00 hacia un punto específico, hacia coordenadas x, y específicas El resultado en valor es en realidad un valor entre menos pi y más pi. Lo importante a tener en cuenta aquí es que lo pasamos DY primero y x segundo. Pasamos estos argumentos al revés. Así es como funciona el método incorporado. Ya estamos calculando estos valores en el método calc. Se están devolviendo como valores con índice 2.3 en esta matriz. Porque contamos índices en array desde 00123. No tengo que volver a correr el método de calma. Ya lo estamos ejecutando en la línea 38. Simplemente voy a tomar este objetivo que sostiene esa matriz devuelta. Y tomo elemento con un índice de dos, elemento con un índice de tres valores x y D Y. Bien, para que podamos ver que puede funcionar en, esto es útil para tantas cosas en codificación creativa y desarrollo de juegos. Por cierto, necesito arreglar esto solo me enterró un segundo hasta que me dé cuenta de esto. ¿Qué tal si cambio estos dos valores? La trigonometría puede ser complicada, ¿de acuerdo? Eso si multiplico estos valores por menos uno, ¿verdad? Dame un segundo más, ¿de acuerdo? Cometí un error aquí. Dije que las matemáticas 8102 necesitan DY primero y dx segundo. Pero si, lo estoy pasando x y DY, necesito cambiarlos por ahí. Sí, esto es exactamente lo que necesitamos. Perfecto. Esto fue un montón de matemáticas. Si llegaste hasta aquí, el resto del proyecto será fácil y divertido. Ya hicimos la parte más desafiante. Ahora tenemos nuestro método reutilizable calc M. Y podemos volver a usarlo siempre que necesitemos conseguir una distancia entre dos objetos y apuntarlos uno hacia el otro o hacer que se alejen el uno del otro. Te voy a mostrar cómo hacer eso cuando implementemos enemigos un poco más tarde. 36. Modo de depuración de L7: Aquí definimos oyentes de eventos. Implementemos otro oyente de eventos. Esta vez para un evento clave arriba. Dentro consologe el objeto de evento generado automáticamente. Ahora cuando hago clic en Canvas y empiezo a escribir en el teclado, vemos la tecla que se presionó. Puedo decir si la tecla que se presionó es una D minúscula, establezca esta propiedad de nuevo en true. Vamos a crear esto, hacer depurar aquí. Cuando presionamos la letra D, en realidad pondremos esta bolsa de punto D su signo de exclamación opuesto, Me gusta Consolok esta bolsa de punto D cuando presiono D en mi teclado. Ahora activo y desactivo el modo de depuración. Si es cierto, la letra D lo establecerá en falso. Si es falso, lo establecerá en true. Este truco también es útil para muchas otras cosas. Es bueno recordar esta técnica dentro del método de sorteo en la clase de jugador, estamos dibujando la imagen del jugador, la nave espacial, y también estamos dibujando un círculo alrededor de ella Este círculo representará el área de colisión. Los jugadores golpearon caja. Es algo útil de ver a veces, pero quiero tener una manera de mostrarlo y esconderlo. Diré que solo dibuja el círculo del área de colisión alrededor del jugador si el error D es cierto. Ahora cuando presiono la letra D una y otra vez, estoy mostrando y ocultando el hit box. Es lo mismo con el planeta. Tenemos la imagen del planeta y amplio círculo de colisión a su alrededor. Sólo vamos a dibujarlo cuando este juego hacer debug es cierto, así. Nuevamente, puedo presionar la letra D en mi teclado repetidamente y mostrará y ocultará estos elementos de colisión. Podemos mostrar y ocultar todo tipo de información útil en nuestro juego. Así, ahora podemos ingresar al modo de depuración en nuestro proyecto, lo que nos ayudará a desarrollar el juego. Al mismo tiempo, podemos ocultar fácilmente estos elementos para ver cómo será el resultado final para el usuario cuando se oculten los hit boxes y tal vez algunos otros elementos de soporte. 37. Grupo de objetos de L8: Esto está empezando a verse bastante bien. Tenemos un planeta, un jugador que gira alrededor de él, e implementamos un modo de depuración. Ahora quiero que el jugador pueda disparar proyectiles láser Tenemos que asegurarnos de que esos proyectiles vuelen en la dirección correcta desde la punta de la nave espacial del jugador hacia el cursor del ratón Además de eso, optimizaremos el rendimiento de este proyecto implementando proyectiles como un pool de objetos reutilizables Significa que en lugar de crear y desechar constantemente estos objetos proyectiles medida que el jugador dispara, crearemos un charco de Y sacaremos de la piscina cuando los necesitemos y los devolveremos nuevo al pool de objetos en lugar de destruirlos y eliminarlos. El patrón de diseño de pool de objetos nos ayuda a administrar mejor los automáticos de asignación de memoria procesos automáticos de asignación de memoria y recolección de basura. Asignamos la memoria solo una vez cuando creamos el pool de objetos inicial, que es más eficiente en el rendimiento. La recolección de basura es un proceso automatizado donde Java Script recupera memoria eliminando objetos a los que ya no se hace referencia en ningún lugar del La recolección de basura ocurre automáticamente, y a veces puede ocurrir en momentos incómodos Por ejemplo, en un momento en el que necesitamos animar una gran ola enemiga Y como resultado, puede causar menor velocidad de fotogramas y tartamudeo de animación Queremos que nuestro juego funcione sin problemas y rápido y que siempre responda rápidamente a las entradas de los jugadores. Entonces eliminaremos por completo el proceso de recolección de basura reutilizando el mismo, digamos 20 objetos proyectiles una y otra vez, en lugar de desechar los viejos y crear Esta sencilla técnica de mejora del rendimiento puede hacer mucho por tus proyectos. Aprendamos a implementarlo en Proyectiles, y cuando lo entendamos, también lo usaremos para enemigos proyectil de clase se utilizará como plano para crear ese charco inicial de digamos 20 objetos de proyectil reutilizables Como es habitual, necesitamos una referencia a la clase de juego principal para obtener acceso a propiedades y métodos que se sientan en el objeto principal del juego. Los proyectiles estarán involucrados en la detección de colisiones, por lo que debemos asegurarnos de que tengan coordenadas x e y y propiedad de radio Los haré grandes al principio para que los podamos ver claramente. Los objetos creados por esta clase de proyectiles serán los llamados miembros del grupo de objetos Necesitamos darles una propiedad que indique si el objeto está disponible en la piscina o si se está utilizando actualmente. Inicialmente, se fijará la propiedad libre. El verdadero objeto proyectil está sentado en la piscina inactivo Está a la espera de ser utilizada. Está listo y disponible, es gratis. Cada miembro del grupo de objetos necesita dos métodos, método start que se ejecutará cuando extraemos el objeto del grupo y lo hagamos activo en el juego. En ese punto, fijamos propiedad libre para enfrentar. Este objeto ya no es gratuito y está disponible. Está siendo utilizado, dibujado y animado en el juego. También necesitamos un método de reinicio en lugar de eliminar el objeto. Cuando hayamos terminado con él, llamaremos a su método de reinicio que volverá a establecer su propiedad libre en true. Significa que el objeto se vuelve inactivo, listo para ser usado nuevamente cuando lo necesitemos. Cuando disparemos un proyectil, llamaremos a su método de inicio para activarlo. Cuando el proyectil choca con un enemigo o cuando vuela fuera de la pantalla, llamaremos a su método de reinicio para desactivarlo Le daremos un método de sorteo. Solo dibujaremos el objeto cuando su propiedad libre sea falsa, cuando esté activa en el juego. No queremos dibujarlo cuando solo está sentado en el objeto esperando ser utilizado. El método de actualización moverá el proyectil alrededor. Nuevamente, solo queremos ejecutar el código de actualización para proyectiles activos si este libre es falso dentro del método draw, dibujamos un círculo que representa el método de proyectil start path R. No te confundas aquí por los saltos de línea, está todo en una línea, solo me rompe a otra línea porque mi ventana del editor de código es demasiado estrecha. Porque quiero mostrarte el código y el proyecto lado a lado dentro de actualización. Si el proyectil está activo, actualizaremos su posición X e Y por los valores de velocidad X y velocidad Y crearon la propiedad de velocidad horizontal y la configuré en un píxel Un píxel por fotograma significa que se moverá de izquierda a derecha. La velocidad Y también será de un píxel, lo que significa movimiento de arriba a abajo. Estas dos velocidades se combinarán para determinar la dirección final. La combinación de estos dos valores particulares de velocidad, 11, dará como resultado un movimiento en la dirección inferior derecha. La clase Proyectil está lista. Es un miembro del grupo de objetos. Cuenta con una propiedad gratuita, métodos de inicio y reinicio. Y el código dentro del sorteo y actualización solo se ejecuta cuando el objeto está activo, cuando su propiedad libre está establecida para caer. Gestionaremos el pool de objetos desde el interior de la clase de juego desde el cerebro principal de esta base de código. charco de proyectiles será una matriz que contiene todos los objetos proyectiles activos y también los inactivos El número de proyectiles determinará cuántos objetos queremos crear y tener disponibles en la piscina Si tenemos muy pocos de ellos, podemos acabarnos y no tener ninguno disponible cuando los necesitamos. Si creamos demasiados, podríamos estar desperdiciando memoria si nunca estamos usando una gran parte del pool que creamos. Llegaremos al número correcto más adelante cuando el juego sea jugable y podremos probar qué tan rápido estamos usando y volver a usarlos. A medida que se crea el juego, ejecutaremos automáticamente un método personalizado. Yo llamo crear alberca de proyectiles, que llenará la matriz de piscinas de proyectiles con cinco objetos de proyectiles inactivos Voy a escribir ese método aquí abajo. En su interior creo un bucle de cuatro que se ejecutará cinco veces. Cada vez que corre, tomamos alberca de proyectiles y empujamos nuevo proyectil dentro Puedo ver que necesita juego como argumento. Le paso esta palabra clave. La clase de juego principal manejará nuestra piscina de proyectiles. Almacenamos cinco miembros del pool de objetos en matriz de pool de proyectiles y los creamos en la primera página, cargamos tan pronto como creamos el objeto del juego Porque como dijimos antes, todo el código dentro del constructor de clase se ejecuta automáticamente. Cuando creamos una instancia de una clase usando la nueva palabra clave, necesitamos un método para crear ese charco de proyectiles Y también necesitamos un método más. Voy a llamar, por ejemplo, obtener proyectil. Cuando llamamos a este método, circulará sobre el conjunto de charcos de proyectiles que creamos A medida que recorrimos los índices en la matriz que creamos anteriormente. Tan pronto como encontramos el primer objeto proyectil con propiedad libre establecida en true, sabemos que este objeto está actualmente inactivo Es gratis y disponible. Lo cogimos y lo devolvemos. Obtener método de proyectil, encuentra el primer objeto libre en la piscina y lo devuelve de nuevo. Por favor, no te confundas por el rompimiento de línea aquí, todo está en una línea. Es solo que la ventana de mi navegador es demasiado estrecha. Si free es true, get projectil method devuelve el objeto y deja de buscar inicialmente Todos los proyectiles son libres, están inactivos y libres para ser utilizados Si ejecutamos método start, los activamos y ya no son libres. Agrego un oyente de eventos más, esta vez para un evento de mouse down. Cuando hacemos clic en el botón izquierdo del ratón sobre el ratón abajo establecer el ratón X e Y a X e Y del evento y llamar al método de disparo en la clase del jugador. A este jugador se accede a través esta referencia aquí es esa clase. Vamos a darle un método de disparo personalizado. Cuando deberíamos crear una variable auxiliar temporal, configuramos a ese método personalizado get proyectil que acabamos de escribir Accedemos a él a través de esta propiedad de juego aquí y se sienta en el objeto de juego aquí abajo. Este método recorre la piscina y devuelve el primer objeto proyectil libre Digamos que actualmente estamos dibujando todos los proyectiles. Actualmente todos están activos y nos quedamos los disponibles gratuitos que podrían romper nuestro código. Para arreglar eso digo si encontramos un proyectil libre, si el método get proyectil en realidad fue capaz de encontrar uno que esté libre y disponible sólo entonces tome ese proyectil libre y llame a su método de inicio para Aquí algunas propiedades del objeto proyectil cambian los valores medida que lo animamos y actualizamos Tenemos que restablecerlos a los valores predeterminados en algún momento. Podemos optar por hacerlo cuando lo activamos, pero también cuando desactivamos el objeto Haré todo dentro del método de inicio. Cuando activemos el objeto, será más sencillo de implementar en combinación con otras elecciones que ya hicimos con esta base de código en particular. El método Start esperará las coordenadas x e y como argumento, y establecemos esos valores como tx y ty. Al principio, comencemos todos los proyectiles a partir de coordenadas. Digamos 100, 100. También debería haber consolado la piscina solo para verificar si después de que se ejecuta este método, en realidad estaba lleno de cinco objetos. Todo bien. Inspecciono uno para asegurarme de que no tenemos ningún error aquí. Si ves algún no valor, no un número, podría significar que tenemos un problema en alguna parte a medida que calculamos estos valores. Todo está bien aquí. Borro el consolog dentro del método render Elimino este camino de inicio. Tomo todo el conjunto de charcos de proyectiles, y por cada objeto proyectil dentro de la matriz, llamo a su método de dibujo, pasándolo contexto y también llamo actualización Si hago clic, no pasa nada. Sigo el código para encontrar mi error. Primero verifico si este evento mousedown realmente se está ejecutando. Rápido. Consolog me va a ayudar aquí. Sí, sé que el problema no está aquí. El siguiente paso sería ver si el método de disparo en realidad está ejecutando proyectil icsolog desde Cuando hago clic, puedo ver que el método Obtener proyectil es devolver correctamente los objetos proyectiles de la piscina para mí, genial, eso significa que el problema debe estar en algún lugar de la clase real de proyectiles Vengo aquí y veo que olvidé una línea. El método Arc en realidad no renderiza formas en lienzo. Para realmente dibujarlo aquí, necesito llamar ya sea trazo o fallar. Ahora cuando hago clic en lienzo, estamos dibujando proyectiles Quiero darles un color diferente, pero quiero que ese color se restrinja solo al código de dibujo del proyectil y no afecte a ninguna otra forma que dibujemos en lienzo Posteriormente envuelvo este código entre los métodos seguros y restauradores al estilo de relleno para que los proyectiles dorados se muevan hacia abajo Debido a la combinación de estas dos velocidades, un píxel a la derecha y un píxel hacia abajo por fotograma de animación resultarán en esta dirección de movimiento. El método Start espera las coordenadas X e Y como argumentos. En realidad quiero que el proyectil empiece por el jugador que les dispara. Se lo paso a esta doxy porque aquí estamos dentro de esa clase de jugador que funcionó 38. Proyectiles para jugadores de L9: Si sigo dando clic con el ratón, disparamos cinco proyectiles Y después de eso, en lugar de devolver un proyectil, obtener método proyectil comienza a regresar indefinido Es porque nos quedamos sin objetos en la piscina y ya no puede encontrar ningún objeto disponible gratis. no hay objeto proyectil con Ya no hay objeto proyectil con tres propiedades establecidas como verdaderas. Debido a que activamos los cinco, me aseguraré de que los proyectiles reinicien automáticamente cuando se muevan fuera La exposición horizontal del proyectil es menor que cero, si es mayor que el ancho de juego, o si la posición vertical es menor que cero, o si es mayor que la altura del juego Si alguna de estas cuatro condiciones es cierta, sabemos que el proyectil se movió hasta el borde de la pantalla Entonces podemos llamar a su método reset, que establecerá su propiedad libre en true. Se desactivará. También significa que debido a estas comprobaciones, ese proyectil ya no será dibujado y actualizado Ahora puedo disparar si los cinco proyectiles están activos, todavía nos estamos poniendo Hacemos click más, pero en cuanto uno de ellos se mueve fuera de pantalla, se restablece y podemos reutilizarlo y lo volvemos a disparar. Gran trabajo. Tenemos un pool de objetos reutilizables en funcionamiento. Ahora quiero que los proyectiles vuelen en la dirección correcta Ya tenemos un método personalizado de Calc Am que toma dos objetos circulares Nos da dirección horizontal y vertical. Podemos usarlo para hacer que se muevan acercándose o alejándose el uno del otro. Ya estamos calculando ese valor desde antes y lo estamos configurando a esta propiedad Dom en la clase de jugador. Podemos usar el mismo valor porque todavía queremos que la dirección del movimiento del proyectil esté determinada por la posición relativa entre el planeta y el ratón Queremos que el proyectil vuele desde la mitad del planeta hacia el ratón Primero paso el método de inicio velocidad x de menos uno y velocidad y de uno. Nos aseguramos de que el método start espera estos valores y establece sus propiedades speed x y speed y a esos valores. Se están agregando a sus coordenadas de posición aquí menos un píxel por fotograma horizontalmente y más un píxel por fotograma verticalmente resultarán en esta dirección de movimiento. Menos diez y más uno nos darán esto. Vamos a usar los valores que obtenemos del método calc. Sabemos que el vector vertical es el índice cero en la matriz, el vector horizontal es el índice uno. Estamos utilizando estos valores, que son la relación entre la distancia horizontal y la distancia hipotenosa. Ahora podemos disparar proyectiles en cualquier dirección. Se mueven bastante despacio. Puedo crear una propiedad de modificador de velocidad. Siempre y cuando multiplique ambos por el mismo valor modificador, solo escalará el vector manteniendo esa dirección correcta. Nos estamos quedando sin proyectiles. Vamos a crear algún búfer. A lo mejor quiero crear 30 en la piscina para asegurarme de que no se nos acabe. Depende de qué tan rápido se muevan fuera de la pantalla y qué tan rápido se restablecen. Podemos probarlo y ajustar el valor posteriormente. Los proyectiles provienen de la mitad del círculo de jugadores, lo que no es lo ideal Quiero que vengan de la punta de la nave espacial. Aquí en la clase del jugador dentro del método de disparo, extendemos las coordenadas x iniciales de cada proyectil por ese valor vectorial horizontal escalado al radio del jugador que debería ordenarse También uso el vector vertical aquí, está todo en una línea. Espero que puedas leer mi código. Estamos pasando muchas cosas al método de inicio. Ahora estoy usando radio aquí, porque quiero que los proyectiles vengan directamente de la punta de la nave espacial de los jugadores Si usa diferentes valores aquí como este, debería tener más sentido lo que exactamente está sucediendo aquí. Si juegas con estos valores por un tiempo, si uso este radio de punto aquí aquí, el proyectil saldrá exactamente del punto que yo quiero Disparamos cuando hacemos clic alternativamente. También puedo decir si la tecla que se soltó es número uno en el teclado o cualquier tecla que elijas. También llamar jugador tiro. Ahora puedo disparar haciendo clic el ratón o pulsando una tecla en mi teclado. Cada vez que disparamos, obtenemos el método de proyectil. Llegaremos a una matriz de proyectiles desde la línea ciento siete Nos dará un objeto proyectil gratuito. El método de inicio lo colocará en la punta de la nave espacial del jugador Y el método de calma le dará velocidad horizontal y vertical que coincida con la distancia relativa en el punto en el tiempo entre planeta y el ratón para enviar el proyectil hacia fuera en la dirección correcta Tan pronto como los proyectiles se mueven fuera del área de lona, los reiniciamos y están listos para ser reutilizados siempre que los necesitemos Implementamos un pool de objetos en nuestra base de código. Hago que se muevan más rápido y los hago más pequeños. Sí, esto está más cerca de lo que realmente quiero que se vean en el juego final. Ya puedo eliminar este consolog. 39. Grupo de enemigos de L10: Ahora que vimos cómo implementar un grupo de objetos en nuestra base de código, solidificemos ese conocimiento repitiendo la misma lógica para crear un grupo reutilizable de objetos enemigos Recuerde que cada miembro del grupo de objetos necesita una propiedad que indique si está activa o inactiva. Solo dibujamos y actualizamos si está activo. El método Start activará el objeto. Establecerá propiedades a algunos valores que el objeto necesita al inicio de su ciclo de vida. En este caso, posicionamos el proyectil dependiendo de la posición del jugador, y le damos dirección de movimiento dependiendo de la posición del ratón El método de reinicio está aquí. Para desactivar el objeto, tenemos una matriz que contiene los objetos. Establecemos el número. Y necesitamos dos métodos de ayuda, uno para crear la piscina y otro para obtener un artículo gratis de la piscina. Vamos a implementar algo muy similar para que los enemigos solidifiquen este conocimiento y aclare cualquier cosa en caso de que todavía no estés del todo seguro de cómo funciona algo de esto La repetición siempre me ayuda a crear una clase enemiga. Se necesita una referencia al objeto principal del juego. Necesita X e Y, y radio. Por ejemplo, fijemos las coordenadas X e Y iniciales en 100, 100. Apenas por ahora, estaremos recortando marcos rectangulares de la hoja de sprites Al saltar por el ancho y alto del marco alrededor de la hoja de sprites Creé los activos de arte para que coincidan con lo que necesitamos en la colisión de juegos. Los círculos tienen un radio de 40 píxeles. Los fotogramas de sprites individuales serán 80 veces 80 píxeles. Este tamaño de cuadro también coincide con el juego Space Invaders que hicimos antes Las hojas de spride enemigo son totalmente compatibles y también se pueden usar fácilmente en ese juego usar fácilmente en ese juego si quieres expandirlo La velocidad x y la velocidad Y determinarán en qué dirección se moverá el enemigo. Dado que se trata de un miembro del grupo de objetos, necesita propiedad gratuita. Al principio lo dijimos, el verdadero método de inicio se pondrá libre a cara para activarlo. El método Reset pondrá libre a true para desactivarlo. El método de dibujar esperará contexto como de costumbre. Y el código dentro solo correrá para enemigos activos. El método Update también ejecutará su código solo para los activos. Al principio, dibujemos a los enemigos como simples círculos. El combo habitual del método de arco de ruta de mendicidad , los trazo. El método de actualización moverá sus coordenadas x e y dependiendo de los valores de velocidad x y velocidad y. Lo mismo que hicimos para los proyectiles. Necesitamos una matriz que retenga todos los objetos enemigos activos e inactivos. El número de enemigos de la piscina enemiga será de 20. Y automáticamente crearemos y llenaremos ese charco con 20 objetos enemigos. En el punto en que creamos una instancia de clase de juego, igual que hicimos para los proyectiles Crear grupo enemigo creará y empujará 20 objetos enemigos a la matriz de piscinas enemigas. El constructor de clase enemiga espera el juego como argumento. Le paso esta palabra clave, Get. Enemigo recorrerá el conjunto de charcos enemigos que acabamos de llenar con 20 objetos inactivos. Revisaré propiedad gratuita para cada uno. Y en cuanto encuentra el primer objeto enemigo con tres propiedades establecidas en true, devuelve ese objeto y deja buscar en la piscina enemiga del consulado. En este punto, aquí debería llenarse de 20 objetos enemigos. Después llamamos al método create, sí los veo. Abro uno solo para comprobarlo. Todo parece bueno aquí. Borro el consolo Igual que dibujamos y actualizamos proyectiles para cada fotograma de animación, tomamos piscina enemiga, llamamos a cada uno en él Por cada enemigo llamamos a su sorteo y actualización. Recordamos que estos métodos sólo se ejecutarán si el objeto está activo, si es free property se establece en false. Inicialmente, todos los objetos enemigos tienen una propiedad libre establecida en true. Al llamar a start se activará el objeto. Quiero que el primer objeto de la matriz de piscinas enemigas se active automáticamente. Inmediatamente cuando comienza el juego, tomo un objeto con un índice de cero dentro del conjunto de pool enemigo y lo llamo método start. Lo puedo ver aquí. Activemos los primeros cinco objetos. Para ello, hay que tener cuidado. Si no tenemos suficientes objetos en la piscina, esto rompería el código porque no estoy haciendo una declaración if. Verifique primero, asegúrese de que el número aquí sea lo suficientemente alto. Estoy dibujando a los cinco enemigos en las mismas coordenadas. Si los muevo, todos se mueven, podría establecer las coordenadas x e y iniciales en 00. Cuando se ejecuta el método start, pongamos X a un valor aleatorio entre cero y ancho de juego, posición vertical y a un valor aleatorio entre cero y altura de juego. Ahora cuando actualizo la página, los cinco enemigos están dispersos aleatoriamente por el área de juego visible. Como siempre, estamos agregando valores de velocidad x y velocidad y a sus coordenadas x e Y para cada fotograma de animación para darles dirección y velocidad de movimiento. La combinación de estos valores determinará su vector de movimiento. Yo establecí velocidades 200. Estableceremos su velocidad cuando se ejecute el método de inicio. Quiero que el enemigo se mueva directamente hacia la mitad del planeta. Nos están atacando. Necesitamos un vector entre esa posición de partida enemiga aleatoria y el planeta. Ya hicimos algo parecido. Creo una variable auxiliar temporal llamada am. Ejecutará nuestro método utilitario, pero esta vez queremos vectores direccionales entre el enemigo. Esta palabra clave, estamos dentro de esa clase enemiga como objeto B. pasamos por el planeta. Si recuerdas, devuelve una matriz de cuatro valores. El primer elemento, el elemento con índice de cero es la dirección horizontal. Elemento con un índice de uno es la dirección vertical. No funciona y está bien. Es porque necesitamos calcular la puntería después de posicionar a los enemigos. La Posición del Enemigo es por supuesto, muy importante en este cálculo para obtener la dirección correcta entre el enemigo y el planeta. Primero establecí mi posición, luego llamamos calma. Ahora los enemigos se mueven hacia el planeta. Perfecto. 40. Detección de colisiones L11: Quiero que los enemigos pasen por el planeta así. Quiero que choquen con él. Vamos a crear un método de colisión de cheques reutilizable. Se esperará objetar a y objetar argumentos BS. En este proyecto todos los objetos tendrán casillas circulares para marcar colisión entre dos círculos, círculo A y círculo B, tenemos que verificar la distancia entre el punto central del círculo A y el punto central del círculo B en el eje horizontal. Eso nos da este sitio. Entonces hacemos lo mismo en el eje y vertical para conseguir este lado. Ahora para obtener la distancia real entre estos dos puntos en el espacio, necesitamos la longitud del hipotenus, que podemos obtener por fórmula del que podemos obtener por teorema de ptagoras o construido en Tenemos una distancia entre dos puntos, entre el punto central del objeto circular A y el objeto circular B. Calculamos la suma de sus dos radios Si la distancia entre los puntos centrales es mayor que la suma de radios, sabemos que los círculos no chocan Es lo mismo, el círculo toca. Si la distancia es menor que la suma de radios, sabemos que los círculos chocan Simplemente devolveré directamente la expresión así, haciendo que este método sea fácil de usar. Lo llamamos, le pasamos objeto A y objeto B. Y este método devolverá directamente true o false dependiendo si estos dos objetos chocan o no A ver si funciona subiendo a la clase enemiga. Y dentro del método de actualización, comprobamos colisión entre enemigo y planeta. Cuando ocurra una colisión, reiniciaremos el enemigo que lo desactivará y lo devolverá al grupo de objetos disponibles Copio este bloque de código. Nosotros hacemos lo mismo por el jugador. Si el enemigo choca con la nave espacial jugador, reiniciamos el A ver. Sí, eso funciona. 41. Eventos periódicos L12: Cuando reiniciamos a los enemigos, ellos se han ido. Pero ahora necesito más enemigos para seguir viniendo. este momento solo activamos a los cinco primeros enemigos de la piscina. Aquí quiero activar nuevo enemigo periódicamente. Digamos que cada método de fotograma de animación de solicitud de 1 segundo genera automáticamente una marca Y pasa ese valor de marca como argumento para animar método Imagina que lo está pasando aquí así. A pesar de que sucede de manera invisible detrás de escena, el valor de marca de tiempo es simplemente el número de milisegundos que pasaron desde que se llamó al primer fotograma de animación de solicitud en Todo lo que necesitamos hacer para obtener acceso a ese valor de marca de tiempo es darle un nombre de variable Aquí, el script Java sabrá automáticamente que queríamos la marca de tiempo También necesitamos realizar un seguimiento de la marca de tiempo del bucle de animación anterior Lo llamaré la última vez que lo puse afuera animate aquí. Inicialmente lo puse a cero. Entonces usamos estos valores para calcular el tiempo delta. tiempo delta es el número de milisegundos que tarda nuestra computadora en servir un fotograma de animación Es la diferencia entre la marca de tiempo de este bucle de animación y la marca de tiempo del bucle de animación anterior. Si tienes una computadora rápida, tu computadora puede servir marcos más rápido. Su tiempo delta será menor porque esa computadora rápida necesita menos milisegundos para generar cada solicitud de fotogramas de animación El método de fotograma de animación también normaliza automáticamente la velocidad de animación Si puede hacerlo, ajustará velocidad de animación a la frecuencia de actualización de la pantalla porque no tiene sentido calcular y dibujar fotogramas de animación que tu pantalla no es capaz de mostrar. Una vez que calculamos el tiempo delta, establecemos la última hora en la marca de tiempo actual. Para que pueda ser utilizado como la antigua marca de tiempo del bucle de animación anterior. En el siguiente bucle de animación, la mayoría de nosotros tenemos pantallas que sirven 60 fotogramas por segundo. 1,000 milisegundos divididos por 60 es alrededor de 16.6 mi tiempo delta es 16.6 mi animación se ejecuta a 60 fotogramas Si tienes una pantalla de alta frecuencia de actualización o si tienes una computadora débil muy vieja, tu tiempo delta podría ser diferente al mío. Usar el tiempo delta como este nos aseguraremos de tener en cuenta eso, contamos el tiempo real, milisegundos reales Esto nos permite asegurarnos de que el tiempo en nuestro juego sea el mismo. En máquinas rápidas y lentas, borro el consolog Paso tiempo delta para renderizar método. Me aseguraré de que el método render espera ese valor como argumento, y yo consolog delta time a partir de aquí Sí, sigue funcionando. Siempre asegúrate de eliminar tus consologs. Dejar un consolo como este puede ralentizar tu aplicación. Estas variables se relacionan con los enemigos. Solo agregaré un enemigo al inicio del juego. Voy a necesitar dos variables de ayuda, temporizador enemigo e intervalo enemigo. El temporizador enemigo contará el tiempo delta desde cero hasta 1,000 Luego agregamos nuevo enemigo y volvemos a contar. Queremos activar periódicamente un objeto enemigo del grupo de objetos cada 1 segundo, cada 1,000 milisegundos. Si el temporizador enemigo es menor que el intervalo enemigo, seguimos agregando valor de tiempo delta al temporizador para cada fotograma de animación, y otra vez hasta que obtengamos más de 1,000 Y luego esta declaración L se ejecutará cuando temporizador acumuló suficiente tiempo delta. Reiniciamos el temporizador de nuevo a cero para que pueda contar de nuevo y activamos un nuevo enemigo help er, variable temporal llamada enemigo será igual a nuestro método personalizado get enemies. Si conseguir enemigo fue capaz de devolver un objeto enemigo libre y disponible de la piscina, llamamos a su método de inicio para activarlo. Bonito, Estamos sacando a un nuevo enemigo del charco de objetos cada 1 segundo. Realmente no quiero que los enemigos aparezcan en algún lugar en medio de la lona al azar. Probemos algo. Si llamo al método aleatorio por sí solo así, devolverá un valor 0-1 si ese valor es menor a 0.5 Aproximadamente en 50% de los casos dijimos posición horizontal del enemigo entre cero y ancho de juego, y la posición vertical será cero Enemigo aparecerá en algún lugar de esta zona. En el otro 50% de los casos establecemos x a cero. Y será un valor aleatorio entre cero y altura de juego. La posición resultante será al azar, en algún lugar de esta zona. Bien, ahora podemos hacerlo por los otros dos lados también. ¿Cómo hacer eso? Por ejemplo, podemos usar un llamado operador ternario El único operador Javascript con tres operandos. Lo usamos como una simple declaración FL de una línea . Va así. Una condición para evaluar si es cierto signo de interrogación, hacer esto ls colon, hacer eso. Si método aleatorio es menor a 0.5, la posición vertical de este enemigo será cero L En otro 50% de los casos punto y será punto de altura. Si se ejecuta este bloque de código, vendrán enemigos de aquí o de aquí. Sí, hacemos lo mismo en este bloque de código. Si el método aleatorio es menor a 0.5 este punto x es cero, x es el ancho del juego, mientras que al mismo tiempo, posición vertical es aleatoria entre cero y la altura del juego. Si se ejecuta este bloque de código, vendrán enemigos de aquí o de aquí. Ahora tenemos enemigos que vienen al azar de las cuatro direcciones. Cuando chocan con el jugador o con el planeta, se restablecen y desaparecen También quiero poder disparar a los enemigos. Quiero verificar colisión entre enemigo y proyectiles. Tomo toda la matriz de extracción de proyectiles para cada objeto de proyectil Compruebo primero si el proyectil está activo, si su propiedad libre está establecida en false Ten en cuenta que este bloque de código también solo corre para enemigos activos. Solo queremos verificar colisión entre enemigo activo y proyectil activo Si el proyectil está activo, también verificamos la colisión entre este enemigo y el proyectil actual sobre el que estamos circulando. Si el proyectil y el enemigo chocan, reiniciamos el proyectil a desactivado y lo devolvemos a la piscina de objetos Cuando lo pruebo, puedo ver que los proyectiles se destruyen cuando golpean a Genial. También restableceré enemigo cuando ocurra la colisión. Bien, ahora podemos disparar a los enemigos. Tenemos un bucle de juego completo e implementamos proyectiles y enemigos como pool de objetos, lo que implicó algo de lógica extra Pero merece la pena por razones de rendimiento. El código sigue siendo limpio y fácil de leer. Creo que no me gusta cómo los enemigos simplemente aparecen. Quiero que eso suceda fuera de la pantalla, y el enemigo simplemente flota a vista sin problemas y sin problemas Empujaré a las posiciones iniciales enemigas fuera de la pantalla. Doy cuenta del radio, aquí, aquí, aquí y aquí. Ahora arrancan de pantalla y flotan a la vista mucho mejor. Puedo hacer que los enemigos coman más a menudo. ¿Qué tal un nuevo enemigo cada medio segundo? 42. L13 Asteroide enemigo: El planeta está realmente bajo ataque. Ahora los círculos blancos nos están atacando. Usemos algunos gráficos. Primero, hacemos un simple tipo enemigo, un asteroide. Podemos descargar la hoja de cálculo del enemigo en la sección de recursos a continuación, Dale una idea de asteroide Esta es la clase enemiga padre. Contiene todos los métodos y propiedades compartidas entre todos los tipos de enemigos. Voy a crear una clase infantil llamada clase asteroide. Asteroide extiende eneme. Ahora enemigo es una clase padre, también llamada superclase asteroide es una clase infantil también llamada clase de asteroides de subclase Constructor esperará el juego como argumento aquí. Primero, quiero ejecutar todo el código dentro del constructor. Dentro de la clase enemiga padre dentro de su superclase. Quiero ejecutar todo el código aquí. Paso la referencia del juego a lo largo. Sabemos que se espera ahí en línea en el 98. clase infantil de asteroides contendrá propiedades y métodos que son específicos solo para este tipo de enemigo El asteroide tendrá esta propiedad de imagen de punto que apuntará a esa hoja de propagación de asteroides que importamos hace un momento Las propiedades compartidas están en la clase padre. Las propiedades y métodos específicos están en la clase hijo. Porque estamos usando la palabra clave de extensión aquí. Javascript configura automáticamente una cadena prototipo detrás de escena cuando llamamos al método de dibujar y actualizar en clase de asteroides y Javascript no puede encontrarlos aquí, viajará automáticamente a la clase enemiga padre Intentará encontrar esos métodos ahí ya que todos los tipos de enemigos tendrán imágenes. Puedo subir aquí y puedo llamar método build in draw image. Le paso la imagen que quiero dibujar y las coordenadas x e y. Dónde dibujarlo, obtenemos un error porque estamos instanciando la clase enemiga padre y el script Java no puede ver esta propiedad de imagen en Tengo que crear una instancia de clase infantil de asteroides en su lugar Ahora estamos dibujando toda la hoja de cálculo. Reduciré el número máximo de enemigos a dos para que sea menos ocupado y más fácil para nosotros ver lo que está pasando. Podemos pasar el método de dibujo de imagen, opcional ancho y alto así. Ahora toda la imagen será estirada o apretada para llenar esa área predefinida. En este caso, estoy apretando toda la hoja extendida del asteroide en un área de un marco También podemos pasar, dibujar imagen nueve argumentos para obtener el máximo control sobre la imagen que queremos dibujar. Le pasamos la imagen que queremos dibujar fuente x, fuente Y, ancho de origen y altura de origen del área que queremos recortar de la imagen de origen. Y lo pasamos destino X, destino Y, Destino con y altura de destino para definir dónde en el lienzo de destino queremos colocar ese trozo de imagen recortado en dos Primero, recorte este marco desde la coordenada 002 con y la altura de un fotograma. Niza otra vez, como sabemos, los círculos y rectángulos se dibujan de manera diferente en lienzo Para alinear la imagen rectangular del asteroide sobre su caja de cabeza circular, necesito desviar el argumento de destino x de posición horizontal por el radio Así, también desfase la coordenada Y vertical por el radio. Al igual que este planeta está siendo atacado por asteroides. Ahora aumento el tamaño del objeto enemigo, tire hacia atrás a 20. Quiero un nuevo asteroide enemigo cada, digamos 1,700 Yo sólo quiero dibujar los círculos de colisión sobre los enemigos. Si el modo de depuración está activo, quiero poder mostrarlos y ocultarlos presionando D en el teclado, totalizando el modo de depuración activado y desactivado. 43. Animación de sprites en L14: Podemos navegar por la hoja de cálculo y decidir qué marco estamos recortando actualmente ajustando el valor que pasamos para dibujar el método de imagen Aquí el argumento de la fuente x puede ser así. Cero veces ancho de un solo cuadro nos dará este uno veces ancho, obtenemos este marco. Cinco veces el ancho es este marco. La forma en que estructuro esta lámina Spry. navegación horizontal alrededor de la hoja Spry reproducirá fotogramas de animación de explosión Fuente y argumento irá arriba y abajo alrededor de la hoja de cálculo. Cero veces altura es este marco, Esto es una vez altura, dos veces altura, tres veces altura. Fuente y argumento pasado para dibujar el método de imagen determinará cuál de estos cuatro asteroides disponibles estamos dibujando Pondré este multiplicador en una variable. Yo llamo frame Y. Puedo ponerlo aquí, pero tal vez cada tipo de enemigo tendrá diferente hoja de cálculo con diferente número de filas de animación Puse el marco Y aquí en la subclase de asteroides. Quiero aleatorizarlo. cada objeto asteroide se le asignará aleatoriamente una de las cuatro variaciones del asteroide que creé para ti Necesito envolverlo en piso de método para redondearlo a enteros porque no hay fila 1.5 Por ejemplo, fotograma Y será aleatoriamente cero o uno, o dos, o tres ya que los enemigos son un grupo de objetos Si juego así, constantemente solo estoy usando los primeros uno o dos objetos una y otra vez, devolviéndolos a la piscina, llevándolos de nuevo, por eso las imágenes no cambian. Puedo, por ejemplo, aleatorizar la fila cada vez que se activa el objeto enemigo aquí, dentro del método de inicio Por ahora esto está bien, aunque puede darnos problemas si otros tipos de enemigos no tienen roles de sprite de fuerza También puedo hacer que los enemigos sean más desafiantes dándoles múltiples vidas. Le doy al objeto enemigo una costumbre en el método. Este método tomará como argumento el valor del daño. Cuando se ejecuta el método, reducimos vidas de enemigo por daño. Aquí comprobamos colisión entre enemigo y proyectiles. En lugar de reiniciar al enemigo de inmediato, llamaré a ese método de golpe y le paso uno como daño. Hacer esto significa que cuando golpeamos a un enemigo con un proyectil, reducimos sus vidas uno si las vidas del enemigo son menos de una Quiero seguir aumentando el fotograma X para reproducir la animación de explosión, necesitamos usar ese valor de fotograma X aquí. A medida que aumenta desde 01234, estamos recortando diferentes marcos de sprites horizontales y estamos reproduciendo Podría poner el fotograma X en la clase padre, pero mantengámoslo aquí en la clase hijo junto al fotograma Y. Ahora cuando golpee al asteroide cinco veces y agote sus vidas, el resto de la hoja extendida jugará los fotogramas de explosión Ahora necesitamos restablecer también ese asteroide cuando se reproduzcan todos sus fotogramas de animación horizontal Le daré una propiedad de marco máximo y la estableceré en siete. Sabemos que una vez que se mostraron los siete fotogramas, estamos seguros de restablecerlo y devolverlo a la piscina. Si el fotograma X es mayor que el cuadro máximo, llamamos reset ya que los enemigos son miembros del grupo de objetos, también necesitamos restablecer estos valores. A medida que reutilizemos estos objetos, le daré propiedad max lives y recordará cuál era la cantidad original de vidas de ese enemigo antes de que empezáramos a dañarlo. Ahora cuando disparo a los asteroides, no regresan porque su marco X está atascado a un valor alto y siguen reiniciándose Cuando iniciamos un asteroide, tenemos que asegurarnos de que su Marco X esté ajustado de nuevo a cero Sus vidas necesitan retroceder al máximo de vidas. Bonito, ahora todo funciona. Cuando el modo de depuración está activo, quiero mostrar el número de vidas como un gran número dibujado sobre cada asteroide Lo haremos aquí dentro este bloque de código condicional que se ejecuta sólo cuando debug está activo. Yo llamo método construido en relleno de texto que toma al menos tres argumentos, el texto a dibujar y las coordenadas x e y punto de anclaje en relación con el que se dibujará el texto. El texto que quiero dibujar es vidas propiedad del enemigo y queremos que ese número viaje a medida que el enemigo se mueve, le doy coordenadas xy del enemigo. Aquí abajo donde configuramos nuestro lienzo, voy a definir la propiedad de fuente de lona. Vamos a establecerlo en 30 píxeles. El estilo de relleno Helvética será de color blanco. Aumento el tamaño de la fuente. Como puede ver cuando colocamos el punto de anclaje del texto exactamente en el medio de nuestro asteroide circular, el texto se dibuja hacia la parte superior derecha A partir de ahí, el punto de anclaje se asienta en el borde inferior izquierdo. Text Align Property define dónde se asienta el texto que dibujamos en el lienzo en relación con su punto de anclaje en el eje x horizontal. Lo configuro para centrar el texto La propiedad Baseline define la relación entre el texto y su punto de anclaje Verticalmente, lo configuro en medio. Ahora el texto está centrado sobre el asteroide. Agradable, cuando destruimos el asteroide, la explosión, los fotogramas de animación juegan demasiado rápido. Estamos utilizando hojas de sprites con número limitado de marcos para ayudarnos con el rendimiento Ahora necesitamos una forma de ralentizar la velocidad a la que las hojas de sprites intercambian fotogramas para que los usuarios puedan apreciar realmente los detalles de las animaciones Usaremos la misma técnica que usamos aquí con las variables de temporizador e intervalo de ayuda. La actualización de Sprite será falsa. Cambiará a verdaderas repisas periódicas para ese fotograma de animación permitir que todas las hojas de sprites nuestro juego salten al siguiente El temporizador de Sprite contará el tiempo delta desde cero hasta el valor del intervalo de sprite una y otra vez Cada vez que alcanza el intervalo activa actualización de sprites y se restablece nuevo a cero para que pueda contar Digamos que quiero activar la actualización de sprites cada 150 milisegundos, vamos a escribir la lógica para actualizar periódicamente los Aquí estamos repitiendo la misma lógica que usamos para activar a los enemigos. Si el temporizador de sprite es menor que el intervalo de sprite, sigue aumentando el temporizador de sprite por tiempo delta, que ya estamos calculando desde antes Es el tiempo en milisegundos. Se necesita nuestra computadora para servir un fotograma de animación más, lo que significa que el temporizador de sprite es más alto que el intervalo Acumuló suficientes milisegundos a través del tiempo delta, podemos restablecer el temporizador de sprite de nuevo a cero para que pueda contar nuevamente y establecer la actualización de sprite Quiero que la actualización de sprites sea cierta solo para ese fotograma de animación Aquí, lo volveré a poner en falso. Ahora bien, este bloque de código significa que actualización de sprites está establecida en true para un fotograma Cada vez que el valor del intervalo de sprite que establecemos, se alcanzan 250 milisegundos. Cuando consolo sprite update, vemos que es falso y periódicamente se establece Genial, cada vez que es cierto, quiero animar las hojas de sprites, quiero saltar a un nuevo marco en la hoja de sprites No olvidemos eliminar el consologe. manejaremos la lógica de animación de sprites Aquí manejaremos la lógica de animación de sprites si la vida del asteroide es menor que Sólo si la actualización de sprites es verdadera, sólo diez. Aumentamos el marco x en uno. Ahora la animación de explosión se reproduce más lentamente. Podemos establecer esa velocidad a cualquier cosa queramos usando la propiedad sprite interval El problema es que ahora cuando la explosión juega más despacio, noto que los proyectiles aún chocan con asteroide que Quiero que los proyectiles hagan caso omiso de las explosiones. Solo verificamos colisión entre enemigo, asteroide y proyectiles si las vidas del enemigo son más o iguales a una Si las vidas son menos de una y se está reproduciendo la animación de explosión, proyectiles volarán a través Sí, esto es mucho mejor. Puedo mostrar y ocultar los recuadros golpeados presionando la letra D, habilitando y desactivando el modo de error D. 44. L15 Enemigo de morfología de langosta: Quiero que los asteroides tengan una sola vida. Será un tipo enemigo simple, básico. Nuestros láseres están calibrados para la defensa de asteroides y podemos destruir fácilmente cada uno con un solo golpe Agreguemos un tipo de enema más desafiante. Puede descargar la Hoja de Spread de Morph de Langosta en la sección de recursos Como de costumbre, la puse en mi carpeta de proyectos y la incluí aquí, dándole una idea de la morph de langosta En este punto, es fácil implementar más tipos de enemigos porque ya tenemos la mayor parte de la lógica implementada. Copio todo este bloque de código para asteroide y le renombro. Señalo esta propiedad de imagen de punto a la hoja de cálculo de morph de langosta Puedo establecer el marco máximo a 14. Sabemos que cuando lleguemos a ese punto, es seguro reiniciar al enemigo. Creé las hojas de cálculo con un plan específico en mente. El asteroide tendrá una vida cuando lo golpeemos. El resto de la hoja de cálculo reproducirán la animación de explosión La morph de langosta tendrá ocho vidas. Cada vez que lo golpeamos, saltamos por un fotograma hasta agotar las ocho vidas Entonces el resto de los fotogramas se reproducirán en una secuencia de animación. Aquí abajo comento clase de asteroides, y en su lugar empujaré a tipo enemigo de transformación de langosta en matriz de piscinas enemigas Bien, tenemos algunos alienígenas entrando. Juego para ver lo que tenemos hasta ahora. Puedo presionar D para deshabilitar el modo de depuración. Puedo ver que cuando los golpeo ocho veces y agoto las vidas, toda la hoja de cálculo Puedo ir aquí dentro del método hit. Y cuando golpeamos al enemigo y disminuimos sus vidas por el valor de daño, puedo establecer el marco X para que sea vidas máximas menos las vidas actuales. Esto ya funcionará. Va a una nueva etapa cada vez que pierde una vida, y luego juega la explosión. Puedo hacerlo de otra manera aquí. Puedo decir si las vidas enemigas son más o iguales a una, cada vez que le pegamos. Aumentar el fotograma X en uno obtenemos el mismo comportamiento. Los enemigos se reinician instantáneamente cuando chocan con el jugador o con el planeta Cuando el enemigo choca con el planeta, al instante establecí sus vidas 20 las cuales jugarán la hoja de cálculo También quiero que los enemigos dejen de moverse en los bordes del planeta. Puse velocidad x20 y velocidad y20. colisión con el planeta ocurre cuando el enemigo golpea el planeta. Ahora se detienen y reproducen su secuencia de animación. Creo que esto se ve más interesante. Reproducen la secuencia cuando el fotograma actual es mayor que el cuadro máximo, se reinician y vuelven al grupo de objetos. Cuando el enemigo choca con el jugador, yo solo pondré vidas a cero Para que sea animado. No voy a detener su movimiento. Puedes tomar tus propias decisiones Aquí, diseña el juego como quieras. Sólo te estoy mostrando cómo funciona la base de código. Tenemos un bicho si tu consulado enemigo. Cada vez que activemos uno, notarás que algunos de ellos están pegados fuera de la pantalla, no viniendo de un lado de la pantalla en absoluto. Porque su posición horizontal es ninguna, no es un número. Es porque dije este juego punto radio aquí, necesito usar el radio del enemigo en su lugar. Probablemente lo notaste antes. En fin, ahora está arreglado. Quiero rotar a los enemigos para asegurarme de que miran hacia el planeta en la dirección de su movimiento. Cada vez que giramos lienzo, se suma y afecta a todo lo dibujado en lienzo. Quiero que cada enemigo sea rotado por su valor de ángulo individual. Voy a restringir cualquier cambio que haga a cada código de dibujo enemigo envolviendo este bloque de código entre los métodos safe y restore. Guardamos estado lienzo, hacemos cualquier cambio aquí queremos que se aplique a estas llamadas de dibujo. Luego reiniciamos de nuevo a lo que era antes para asegurarnos de que las formas dibujadas después de esto no se vean afectadas en el lienzo No puedo simplemente rotar si roto 0.1 radianes. Aquí notarás que todo está un poco apagado para rotar cualquier cosa en navas Primero tenemos que trasladar el punto central de rotación, que es lo mismo que la coordenada 00 en navas sobre el centro del objeto. Queremos rotar. Traduzco a zx punto Y del enemigo que estamos a punto de dibujar Ahora que lo traducimos, hay que tener en cuenta que estamos traduciendo el punto central de rotación así como el punto que se considera la coordenada 00 en Lo estamos traduciendo sobre la posición enemiga. Entonces vamos más lejos de ahí para atraer realmente al enemigo. Efectivamente duplicamos la posición enemiga, empujándola más a la derecha y al fondo de la pantalla. La posición enemiga ahora está definida por el traducir. Entonces tenemos que dibujar realmente todo en la coordenada 00. Si dibujo círculos de colisión enemiga en la coordenada 00, obtenemos esto. También tengo que hacer eso para las coordenadas de texto que muestran vidas enemigas. Para la imagen real que está centrada sobre el círculo. Al compensarlo por radio, hacemos menos radio aquí y aquí Para rotar algo sobre lienzo, traducimos sobre el objeto que queremos rotar. Entonces dibujamos ese objeto en la coordenada 00, porque la posición del objeto ahora se define por translate. Ahora soy libre de rotarlo y funcionará si roto 0.1 radianes, 0.5 radianes En cambio, usaré una propiedad de ángulo distal. Lo defino aquí, 1 radián significa que todos los enemigos se rotan así, 2 radianes Quiero que el ángulo de rotación dependa la posición relativa entre el enemigo y el planeta. Ya escribimos el método ayudante Lkm y lo estamos usando para establecer la velocidad del enemigo El método Lkm nos da vectores direccionales así como DX y DY distancia vertical y horizontal entre los dos objetos que le pasamos como argumentos voy a necesitar Puedo ver que estos son elementos con índices de 2.3 devueltos en esta matriz. Inicialmente, establecí el ángulo 20. Realmente no importa porque cuando se ejecuta el método start, establecemos este ángulo en función la posición relativa entre este enemigo y el planeta. Ya lo hicimos al rotar nave espacial jugador lejos del punto central del planeta Aquí es lo mismo. Utilizamos el método construido en el método 102 que nos dará un ángulo entre el eje x positivo y una línea proyectada hacia un punto específico. Sabemos que espera dy first y dx como segundo argumento. Tengo que pasar estos argumentos en este orden. Los enemigos no están apuntando a donde yo quiero que lo hagan. Pero no importa porque sabemos que el ángulo que estamos obteniendo es relativo a la posición inicial del enemigo y del planeta. El método de inicio se ejecuta solo una vez para configurar al enemigo y puedo ajustar ese valor resultante de muchas maneras. Podría rotar toda la langosta u hoja de cálculo en mi editor gráfico, y luego podría usar eso, pero también podría hacerlo fácilmente con código Puedo ver que simplemente necesito rotar al enemigo 90 grados, un cuarto de círculo, para asegurarme de que se enfrente al planeta. Añado el método pi valor aquí. Si agrego el método pi, lo estoy rotando 3.14 radianes, por 180 grados, medio círculo Yo sólo quiero rotar medio pi, 90 grados, un cuarto de círculo. Incluso si aún no entiendes completamente el método 8102, solo recuerda cómo usarlo. Pásalo los puntos que sean relevantes en la rotación que quieras y mira qué resultado obtienes. Entonces puedes ajustar fácilmente ese ángulo aún más como acabamos de hacer. Podría haber incluso una forma más sencilla de hacer esto en la que no estoy pensando ahora mismo. Sé que algunos de ustedes me darán sus trucos y mejoras en los comentarios, y eso lo agradezco. En fin, los alienígenas enemigos ahora están de cara hacia el planeta. Cada vez que los golpeamos, empujamos la hoja de cálculo al siguiente fotograma cuando se agotan vidas Animación, Jugar animando hojas extendidas así es muy barato En cuanto al rendimiento, tu juego funcionará sin problemas Si estás contento con una animación de sprites de baja velocidad de fotogramas como esta, creo que el juego se ve vivo. Incluso con estas técnicas de animación de sprites amigables con el rendimiento , me gusta cómo reacciona a las acciones de los jugadores Hace que el juego se sienta muy interactivo y receptivo. 45. Texto de juego L16: El método de texto de estado de dibujo mostrará la puntuación, las vidas de los jugadores y los mensajes de juego. Se esperará el contexto como argumento. La fuente será de 30 píxeles de impacto. Por ejemplo, puedes usar fácilmente cualquier fuente personalizada de Google aquí si quieres. Simplemente impórtelos en HTML índice. Dale al lienzo esa propiedad de familia de fuentes en estilo CSS y usa el nombre de esa familia de fuentes aquí. En lugar de impactar funcionará. Yo uso fondo personalizado en otros juegos muchas veces antes de este tiempo vamos a ir con fondo de impacto predeterminado y enfocarnos en otras cosas a las que llamo método de texto de relleno incorporado . El texto será las coordenadas de puntuación serán 2030. Quiero que el texto se dibuje sobre el planeta pero debajo del jugador y los proyectiles y enemigos crearán un bonito efecto de capa cuando el texto esté en medio de los objetos del juego Cuando llamo texto de estado de dibujo y le paso contexto, puedo ver que el texto de partitura está alineado al centro y las vidas enemigas ahora se dibujan en fuente diferente. Esta declaración volvió a cambiar todo el estado de la lona. Lo envuelvo en caja fuerte y restauro para poder dar configuraciones específicas al texto de estado sin afectar a otros textos y formas dibujadas en lienzo. Dije texto alinear a la izquierda. Yo creo esta propiedad de puntuación. Voy a la clase principal enemiga cuando el enemigo terminó de jugar la animación de explosión y se restablece de nuevo al grupo de objetos. Yo aumento la puntuación por el enemigo max vidas, lo que significa fácil asteroide, enemigo dará un punto de puntuación La langosta se transforma, el enemigo dará ocho puntos de puntuación. Tiene ocho vidas aquí, agrego puntaje para dibujar realmente el valor. Cada vez que destruyo a un enemigo y la animación de explosión termina de jugar, me recompensan ocho puntos de puntuación Se puede ver que cada vez que el enemigo explota, obtenemos ocho puntos de anotación Solo quiero otorgar puntos de puntuación al jugador si destruimos al enemigo disparándolo, el enemigo se destruye porque chocó con el jugador o el planeta No queremos dar puntos de puntuación por eso. Voy a dar a cada enemigo una propiedad llamada colisionado. Al principio, lo configuré en falso. Cuando iniciamos al enemigo, siempre tenemos que restablecerlo de nuevo a falso. Si el enemigo choca con el planeta, nos fijamos colisionamos a la verdad Además, si el enemigo choca con el jugador. Si enemigo choca con un proyectil, no hacemos nada, lo que significa que colisionó permanecerá falso Aquí, solo agregamos puntaje si colisionado es falso. Si le disparo al alienígena, obtengo ocho puntos de anotación. Si el enemigo choca con el jugador o el planeta, no obtenemos puntos para ese grado Yo creo una propiedad llamada puntuación ganadora. Inicialmente para los fines de prueba lo puse en diez. Nos encargaremos de ganar o perder condición aquí dentro del método de render. Si la puntuación es mayor o igual a la puntuación ganadora, el juego terminado es cierto. Aquí abajo dentro del método de texto de estado de sorteo, manejaremos game over messages Game over is true. Quiero dos grandes mensajes en medio de la pantalla. Ponemos al texto una línea al centro. Puedo, por ejemplo, crear dos variables Led llamadas mensaje uno y mensaje dos. Se les asignará un texto diferente cuando ganemos y cuando perdamos. Si ganamos, si la puntuación es más que la puntuación ganadora, el mensaje uno dirá que ganas. Mensaje dos, tu puntuación es, Mostramos el valor de la puntuación más un signo de exclamación como este Ahora los vamos a dibujar. El mensaje uno será grande, 100 pixeles. Impact, Nuevamente, puedes usar tu propia fuente personalizada aquí en lugar de impactar. El método Filltext dibujará el mensaje uno en el medio horizontalmente y 200 píxeles desde la parte superior Otro relleno de texto para mensaje dos, nuevamente, en el medio horizontalmente, y digamos 550 píxeles de la parte superior verticalmente. Intentemos ganar el juego. Necesito diez o más puntos de puntuación. Bien, quiero que el segundo mensaje sea más pequeño. ¿Qué tal 50 píxeles impactan de nuevo? Disparo enemigo uno, enemigo dos. Aquí vamos. Sí, eso se ve mejor. Quiero que los enemigos dejen de venir cuando se acabe el juego en una piscina, un nuevo objeto enemigo de la piscina, si gameover es falso 46. L17 Vive un jugador: Ahora bien, ¿cómo podemos perder este juego? Podemos, por ejemplo, darle al jugador un número limitado de vidas. Puse esas vidas aquí en el objeto principal del juego. Yo voy aquí abajo. Oh, esta restauración tiene que estar aquí abajo. De lo contrario, los números que usemos para mostrar vidas enemigas cambiarán. 250 píxeles impactan cuando aparece el mensaje de juego sobre, ahora está arreglado. Quiero dibujar vidas de jugador bajo el marcador. Yo creo un bucle de cuatro. Se ejecutará una vez por cada vida, y dibujará un rectángulo para cada una. El margen izquierdo será de 20 píxeles. posición horizontal determinará el espacio entre rectángulos, por lo que será de 15 píxeles Los espaciamos multiplicando ese espacio en el índice de Tams en la posición vertical de cuatro bucles será tal vez 60 píxeles de El ancho de cada rectángulo debe ser menor que el espacio. En, por ejemplo, diez de altura serán 30 pixeles. Se pueden cambiar estos valores para ver qué hacen. Este es el margen izquierdo, este es el espaciado horizontal. Si el ancho de cada rectángulo individual es el mismo o mayor que el espaciado, se convertirán en una sola barra. Puedes construir barras de carga como esta si quieres. Quiero que estén separados aquí, así que voy por diez, supongo. Si enemigo choca con el planeta, me llevo vidas a este juego y lo reduzco en una Además, cuando ocurre una colisión con la nave espacial jugador, la pruebo. Cuando chocamos, inmediatamente toma las cinco vidas. Porque está tomando una vida por fotograma de animación. Solo queremos que este bloque de código corra por enemigos que tienen vidas más o iguales a uno. Ingresamos a este bloque de código y establecemos vidas en cero, lo que evitará que se ejecute el mismo bloque de código. Nuevamente, esto debería arreglarlo. Yo hago lo mismo con las colisiones de jugadores. Sí, ahora una colisión se lleva solo una vida Perfecto. Aquí dentro del bloque de código de condición ganar y perder digo si la puntuación es más o igual a puntuación ganadora o si la vida es menor que una en ambos casos, set game over a true, lo que impedirá que vengan enemigos. Y mostrará el mensaje grande uno y el mensaje dos. Si ganamos, ponemos mensajes a estos otros, lo que significa que perdimos todas las vidas. Mensaje uno dirá que pierdes. Mensaje dos dirá inténtalo de nuevo. Yo lo pruebo. Disparo a los enemigos, gano. Los enemigos chocan con el planeta. Estamos perdiendo vidas, perdemos todas las vidas y perder mensaje aparece genial. Tenemos un bucle de juego completo. Inicialmente, estableceré D bug en false. Todavía podemos encenderlo y apagarlo presionando la letra D en el teclado. Quiero traer a todos los tipos de enemigos al juego al mismo tiempo. Aquí donde creamos pool enemigo, creo una variable de número aleatorio. Lo configuré en valor aleatorio 0-1 si el número aleatorio es superior a 0.25, en 25% de los casos creamos un asteroide L. El otro 75% de los casos creamos langosta Moorph Asteroid es del tipo enemigo fácil morfos de langosta son enemigos duros que transforman y se separan por lo que tardan más Quiero agregar un nuevo enemigo más a menudo. Digamos que cada 1 segundo quiero poder hacer que los enemigos se muevan más rápido o más lento. Hicimos lo mismo con los proyectiles de jugador. Yo creo una propiedad de modificador de velocidad. Quiero ralentizarlos. En este caso, modificador de velocidad será un número aleatorio entre 0.1 y 0.6 Entonces incluyo el modificador de velocidad aquí, donde calculamos la velocidad horizontal y vertical. Estoy escalando el vector aquí. Prefiero el juego así. Tenemos muchos más enemigos en la pantalla, pero todos se mueven más despacio, dándonos más tiempo para elaborar estrategias Depende de ti elegir si quieres que el juego sea más sobre reacciones rápidas o posicionamiento. Al final de la estrategia, puedes ajustar la frecuencia del enemigo y velocidad de movimiento en función del tipo de juego que prefieras ¿Qué tal el intervalo enemigo, 800 milisegundos y 30 vidas, 50 puntos ganadores Voy a ajustar estos valores más adelante. 47. L18 Beetlemorph Enemigo: Construí otro juego espacial recientemente inspirado en los clásicos Space Invaders Me aseguré de que las hojas de cálculo de ese juego sean compatibles. Aquí déjame mostrarte cómo podemos tomarlos y usarlos fácilmente en este proyecto. Si sigues esa clase, ya tienes las hojas de sprid Si no, puedes descargarlos en la sección de recursos a continuación. Importo hoja de cálculo de Beetle Morph en el proyecto. Agregar más tipos de enemigos a este juego es extremadamente fácil porque ya escribimos toda la lógica. Sólo puedo copiar este bloque de código. Le renombro a Beetlemorph. Ajusto esta propiedad de imagen de punto, reviso la hoja de cálculo, marco máximo será de tres y las vidas serán una. Este enemigo es fácil de derrotar. Aquí abajo donde creo alberca enemiga, puedo decir si número aleatorio es más de 0.5 Añadir Beetle Moorph Tengo que tener cuidado con los corchetes aquí. Tengo que cambiar la condición aquí. Si el número aleatorio es menor a 0.25 creamos asteroide. Si el número es menor a 0.5 creamos Beetle Moorph. En el otro 50% de los casos, creamos un morph de langosta Eso fue fácil. Ahora el juego tiene aún más variedad. 48. L19 Rhinomorph enemigo: Ahora que entendemos el proceso, tomemos un tipo de enemigo más y lo usemos. Copio este bloque de código y creo tipo enemigo rinomorfo, el enemigo blindado Nuevamente, puedes descargar la hoja de cálculo abajo si no la tienes desde el proyecto Space Invaders Aquí agrego una L más si el número aleatorio es menor que 0.75, empujamos a Rhinomorph matriz de la piscina enemiga El editor de código coloreó este nombre de clase de manera diferente para recordarme que todavía no he escrito esa clase aquí arriba Copio este bloque de código. Le renombro Rinomorfo. Señalo su esta imagen de punto a la nueva hoja de cálculo. Max frame será de seis , tendrá cuatro vidas. Ahora nuestro juego tiene cuatro tipos diferentes de enemigos. Los enemigos simples con una vida serán asteroide y Beetle morph, un enemigo blindado que se divide Cuando lo golpeamos dicho intervalo enemigo a 1,200 milisegundos y juega vidas a diez si viste la clase extendida Space Invaders por También tenemos eneme inflable Squidmorphan que absorbe los proyectiles del jugador hasta que También tenemos a Eagle Moorph, un enemigo que sacrifica sus propios segmentos corporales y nos los escupe como proyectiles mutados hechos un enemigo que sacrifica sus propios segmentos corporales y nos los escupe como proyectiles mutados hechos de limo y tentáculos. Todos esos activos artísticos son totalmente compatibles con esta base de código. Un reto para ti es llevarlos si sigues la clase extendida e implementarlos en este juego. Si quieres, también puedes tomar al enemigo único de esta clase, el Lobsta Morph, e implementarlo en el juego Space Invaders Estos dos juegos son completamente compatibles entre sí. Solo quiero aumentar la puntuación si el juego terminado es falso. Así, es fácil de hacer y si necesitas mi orientación, implemento Lobster Morph en juego Space Invaders en la Si estás viendo esto en mi canal de Youtube, las clases extra extendidas más populares y recientes están enlazadas en la página acerca de. Espero que te hayas divertido en esta aventura espacial. Este fue un gran proyecto. Bien hecho. Si llegas hasta aquí, avísame si encontraste algún valor y aprendes algo nuevo dejando un comentario rápido, me pregunto qué parte de este proyecto fue la más interesante para ti. ¿Puedes dejar un comentario rápido y decirme cuál es tu tipo de enemigo favorito? De todos ellos, puede ser ya sea cómo se ve o su comportamiento y mecánica únicos. Con ganas de ver lo que piensas y cuál es tu favorito. Ver más adelante. 49. Proyecto 3: Juego para móviles con JavaScript: Sede. Construyamos un juego para móviles. No te acerques más a ese gigante gaseoso, algo se agita en la atmósfera superior. Primero escribiremos toda la lógica del juego e implementaremos todas las características principales. Puedes reutilizar esa versión más tarde para muchos juegos de pantalla completa basados en el tacto diferentes Y luego agregamos gráficos, animaciones y sonidos, convirtiéndolo en un juego de terror espacial de dibujos animados. Quiero que construyas proyectos de los que puedas estar orgulloso y puedas mostrarlos a tus amigos. Este será un juego de pantalla completa. Los eventos táctiles se ven colocables en cualquier dispositivo, desde una gran pantalla horizontal de computadora hasta un pequeño teléfono móvil vertical puedas llevarlo contigo si quieres mostrarle a todos lo que aprendiste y qué puedes hacer con Java Script Nuestra presencia desencadenó un frenesí de alimentación. Parecían sentirse atraídos por los signos de vida de nuestros tripulantes no mecánicos. retirada inmediata aconsejó endémica de las capas de fluido supercrítico de este gigante gaseoso distante Esta criatura vive en una presión extrema. Pueden enfrentar obstáculos sólidos. Tenemos que golpearlos cuando estén en su estado sólido para cronometrar tus ataques con cuidado. 50. Configuración de Project 3: En índice HTML, creo una simple página web en blanco. Le doy un titulo, vinculo hoja de estilo CS, creo HML five canvas element con un ID de canvas uno Y vinculo mi archivo Java Script en estilo CSS. Realizo un reinicio global para asegurarme de que obtenemos un comportamiento consistente en diferentes navegadores. Al eliminar los estilos predeterminados, le doy al lienzo una posición de borde absoluta. Lo centro en el centro de la página vertical y horizontalmente. Puedes descargar dos imágenes de fondo en la sección de recursos. Abajo. Tomo el fondo horizontal y lo configuro como URL de fondo. Aquí, el tamaño del fondo es de cobertura, lo que escalará la imagen al tamaño más pequeño posible Eso todavía cubre completamente el ancho y la altura, sin dejar espacio vacío. Creo un diff con una clase de controles. Tendremos un botón de pantalla completa y un botón de reinicio. También controlaremos pantalla completa y restableceremos con teclado, pero necesitaremos estos patrones para dispositivos móviles. Te daré estos botones con altura, fondo negro, y texto blanco si quieres. También puedes usar una fuente personalizada. Por ejemplo, puedo ir aquí a Google Fonts. Busco una fuente llamada bangers. Selecciono la fuente aquí y me da enlaces que necesitan ser incluidos en el índice HTML. Idealmente, aquí arriba antes de incluir estilo CSS como este. Ahora puedo usar bangers por toda mi base de código. Por ejemplo aquí para fuente en los botones, también les doy un borde blanco. 51. Haz que todo sea receptivo: Escribiremos toda la lógica aquí con script Java en el archivo JS principal. A lo mejor voy a dividir esto en varios archivos más tarde. Aquí podemos definir nuestras clases, pero nos aseguramos de que solo las instanciemos después de que se haya disparado el evento load Después de que todos los recursos estén cargados y disponibles. Todas las hojas de estilo, scripts, elementos HTML, imágenes y así sucesivamente. Una vez que la página se haya cargado y sepamos que el elemento HTML cannavas está disponible, podemos configurar nuestro proyecto Señalo el script Java hacia mi elemento canvas usando Get element by ID, defino CTX Eso almacenará una instancia de lienzo construido en la API D. Dije lienzo ancho y alto para cubrir toda la ventana del navegador en sty Sss, dije fondo posición a centro para asegurarse de que el planeta esté siempre en el medio En cualquier tamaño de pantalla, coloco los controles a la izquierda abajo Así. También puedo organizar los botones verticalmente usando flex box, fondo transparente, y sin borde en los controles. Aquí, margen en botones cinco pixeles, puedo darle sombra de cuadro y sombra de texto. Por ejemplo, transición 0.3 segundos al pasar el mouse, activo y enfoque Animamos sombra de caja y sombra de texto, y también la clase de juego de fondo de botón será el cerebro principal de la base de código Gestionará el estado de nuestro proyecto. Se esperará cannavas como argumento. En su interior la convertimos a una propiedad de clase, y de ahí extraemos ancho y alto, porque quiero asegurarme de que la anchura y la altura del juego sea siempre la misma que la anchura y la altura de las cannavas Le damos un método de redimensionamiento personalizado. Se esperará ancho y alto como argumentos. Y vamos a cambiar el tamaño del elemento canavas a estos nuevos valores. También establecerá las propiedades width y height desde las líneas 8.9 hasta estos nuevos valores. Creo una instancia de mi clase de juego espera Canvas. Así que le paso lienzo variable de la línea 20. Cuando creamos una instancia de una clase, todo el código dentro un constructor de clase se ejecuta automáticamente línea por línea. Puedo aprovechar eso y puedo poner mi código aquí que quiero ejecutar. En el momento en que se crea una instancia de esta clase de juego usando la nueva palabra clave, incluso puedo poner oyentes de eventos aquí Escucharemos para cambiar el tamaño del evento. El único problema es que el oyente de eventos está en el objeto window y lo estoy colocando léxicamente dentro de mi clase de juego Si quiero acceder a esta palabra clave desde dentro de esta devolución de llamada, se referirá al objeto window Por esa razón, cuando intento cambiar el tamaño de la ventana de mi navegador y los desencadenadores de eventos de redimensionamiento integrados Usar este punto cambiar el tamaño aquí resultará en El objeto de juego no es visible desde adentro aquí. Necesitamos esta palabra clave aquí para apuntar hacia el objeto del juego para permitirnos acceder a las propiedades y métodos que se encuentran en la clase del juego, incluido nuestro método de cambio de tamaño personalizado Puedo hacer eso usando las funciones de seis flechas de ES aquí en lugar de una función de devolución de llamada regular Una característica especial de las funciones de flecha es que heredan esto del ámbito padre Ahora usando este punto aquí apuntará hacia el objeto del juego que es exactamente lo que necesitamos. Ahora, cuando se activa el evento de redimensionamiento, estamos configurando el ancho y la altura del juego de navas en 150 veces 150 píxeles Si consologe el propio objeto de evento generado automáticamente , puedo explorarlo Este objeto nos da todo tipo de información sobre el evento que ocurrió, en este caso el evento resize Por ejemplo, aquí dentro del objetivo, podemos encontrar en propiedades de ancho y altura interior que nos darán las nuevas dimensiones a las que redimensionamos Puedo tomar estos valores y cambiar el tamaño mis canavas y juego a estos Ahora cuando creamos una instancia de clase de juego en la línea 29, se ejecuta todo el código dentro del constructor. Redimensionar el listener de eventos se aplica automáticamente. Ahora puedo cambiar el tamaño de mi navegador y las canavas también cubrirán toda el área del navegador disponible También podemos hacer clic aquí para emular dispositivos móviles. No siempre es 100% preciso en comparación con probar su código en ese dispositivo de forma nativa. Pero está lo suficientemente cerca como para empezar. Este es el navegador Google Chrome, por cierto. Podría verse diferente para ti si estás usando un navegador diferente. Recomiendo usar Google Chrome para este curso. Ahora puedo seleccionar diferentes dispositivos de este menú desplegable, y puedo ver cómo se verá el juego. A veces la vista previa falla. Cuando intercambiamos entre dispositivos, necesito actualizar manualmente la página. Esto realmente no importa porque en el mundo real, nadie va a estar intercambiando entre iphone y teléfono Samsung sin recargar la página Recargar la página siempre soluciona los problemas de posicionamiento. Si surgen, puedes ver que nuestro sencillo prototipo ya funciona relativamente bien. Sigamos trabajando en esto. También puedo crear una media query para tamaños de pantalla pequeños. Asumiré que se trata de un teléfono móvil. Y en ese caso, utilizaré una imagen de fondo diferente que sea más vertical, adecuada para pantallas móviles verticales. Te estoy dando estas dos imágenes de alta resolución en la sección de recursos a continuación. También puedes recortar esta imagen y usarla para otros proyectos si quieres. Ahora tenemos un mundo de juego responsivo muy sencillo que funciona tanto en pantallas horizontales anchas como en pantallas verticales altas. Perfecto. 52. Clase de enemigos: Para que esta base de código sea más fácil de navegar, crearé otro archivo de script Java llamado enemigo JS. Aquí tendremos nuestra clase enemiga y la extenderemos a múltiples tipos de enemigos. Podría usar módulos Javascript aquí, pero luego tendría que mostrarte cómo ejecutar un servidor local. Voy a mantener esto simple y sólo voy a incluir el archivo MGS aquí así Tiene que venir antes del GS principal. Cuando lo hacemos de esta manera, el orden en el que incluimos los archivos Javascript individuales puede ser importante. Puedo cortar clase enemiga de maina JS y la pego aquí dentro de NMS, enemigo esperará una referencia apuntando al objeto principal del juego como argumento En su interior lo convertimos en una propiedad de clase. A través de esta referencia, podremos ver y acceder a todas las propiedades y métodos de la clase principal del juego. Cada enemigo necesitará las coordenadas X e Y, para que el script Java sepa dónde en lienzo dibujarlo, necesita velocidad x y velocidad Y. Y dentro de la altura, lo pondré en 50 50. Inicialmente, el método de sorteo personalizado atraerá al enemigo en la pantalla. El juego tendrá un método de render que atraerá a todos los enemigos así como a todos los demás objetos del juego. A medida que construimos nuestro proyecto, es bueno probarlo paso a paso para que captemos cualquier error potencial al principio, render esperará contexto como argumento. A partir de ahí llamaremos método de rectángulo de relleno integrado para dibujar una forma rectangular simple. Yo llamo a este método de renderizado usando la variable de juego de la línea 28. Se espera contexto. Lo paso CTX de la línea 24. El estilo de relleno predeterminado, si no lo anulamos, es negro. Estamos dibujando un rectángulo negro en estas coordenadas x e y. Con este ancho y alto, puedo establecer el estilo de relleno a un color diferente. Aquí, por ejemplo, verde. Estamos construyendo un juego y las cosas se irán moviendo. Lo logramos dibujando, actualizando y redibujando los objetos una y otra vez para crear una ilusión de movimiento Método personalizado al que llamo, por ejemplo, animar. Voy a llamar a game dot render desde adentro. Y llamo construido en el método de marco de animación de solicitud se sienta en el objeto de ventana. Pero no necesitamos esta parte Javascript, sabremos dónde encontrar ese método automáticamente. Le paso animar, y lo llamo aquí para empezar a animar. Ya estamos animando. Puedo demostrarlo creando la propiedad position x, usándola aquí como posición X en el rectángulo. Yo aumento la posición x en uno para cada fotograma de animación. Vemos pintura vieja. También puedo borrar el lienzo de la pintura vieja entre cada marco de animación usando el método de rectángulo transparente incorporado. Ahora tenemos un rectángulo verde en movimiento que estamos animando. Puedo eliminar esto cuando cambio el tamaño de la ventana del navegador Cambiar el tamaño del oyente de eventos activará nuestro método de cambio de tamaño personalizado que establece el lienzo en el nuevo dentro de la altura Como efecto secundario, cuando cambiamos el tamaño del lienzo, establece el estado del lienzo como predeterminado Todas las propiedades se restablecerán, incluido el estilo de relleno, que se restablecerá al color de bloque predeterminado. Por esa razón, cuando redimensionamos, necesitamos redeclare estos estilos Una forma de resolver esto es asegurarnos de que tenemos acceso a esta variable CTX desde nuestro método de cambio de tamaño personalizado para asegurarnos de que se aplique el estilo de relleno verde Incluso después de cambiar el tamaño, puedo pasar CTX de línea 24 constructor de clase de juego como el segundo argumento aquí arriba, me aseguro de que se espera, lo convierto a una propiedad de clase Ahora puedo llamar a métodos y propiedades de dibujo de lienzo incorporado a partir de esto. Mientras está dentro de la clase de juego, esta línea se sobrescribe cuando se dispara el método de cambio de tamaño En cambio, voy a poner el estilo de rellenos en verde aquí. Después de cambiar el tamaño, le daré a nuestro juego un método de inicio personalizado que se ejecutará solo una vez cuando comience el juego o cuando se reinicie Y configurará todo primero. Yo solo llamo resize para asegurarme de que el estilo de relleno verde se aplique cuando el juego se carga con el que espera y la altura como argumentos Inicialmente, le paso ventana en ancho y ventana en altura. Lo mismo que estamos automáticamente se aplican en el oyente de eventos aquí También activaremos automáticamente este inicio cuando se cree una instancia del objeto de juego en la nueva palabra clave aquí. Ahora el color que le damos al estilo Phil se quedará incluso a medida que cambiemos el tamaño de la ventana del navegador Perfecto. Ahora puedo tomar este Dogamex de aquí Acceso a través de esta propiedad llamo Phil rectángulo que representa al eneme Lo pasamos xy con en altura del objeto enemigo. También puedo darle al eneme un estilo diferente de Phil aquí. Tenemos que darle algunas coordenadas X e Y. Aquí estoy incluyendo al enemigo GS antes que al GS principal. clase enemiga debería estar disponible desde dentro de la clase de juego. Aquí creo una propiedad llamada punto enemigo uno y la configuro una instancia de nuestra clase enemiga personalizada. Se espera juego como argumento. Aquí, aquí estamos. Dentro de ese objeto de juego. Le paso esta palabra clave a través de la cual el objeto enemigo tendrá acceso a todas las propiedades y métodos de la clase de juego. Desde render, quiero llamar al método de dibujo que se define en la clase enemiga. Digo este enemigo uno de la línea siete, dibujar de la línea 11. Aquí, bonito, estamos dibujando un enemigo. Y esto confirma que hemos establecido con éxito una conexión de dos vías entre las clases de juego y enemigas. Buen trabajo. Este método de renderizado se llama una y otra vez para cada fotograma de animación. Aquí solo estamos redibujando al mismo enemigo rojo en la misma posición Pero también podemos moverlo llamando a su método de actualización. Aún no existe. Yo lo defino aquí. Por cada fotograma de animación quiero aumentar la posición horizontal del enemigo por velocidad x y posición vertical por velocidad y más un píxel en el eje y vertical dará como resultado un movimiento de arriba a abajo. También se puede decir si enemigo se movió todo el camino hasta el fondo, puso su posición vertical de nuevo a la parte superior, hago que se mueva más rápido. También puedo aleatomizar la exposición horizontal inicial. También lo alazar cada vez que reiniciamos. Puedo crear múltiples enemigos así. No tienes que hacer esta parte. Sólo te estoy mostrando una forma muy ineficiente de cómo hacer esto Podría funcionar si tenemos solo uno o dos objetos tal vez. Pero en este juego quiero tener decenas, o tal vez incluso cientos de enemigos. Puedo aleatomizar la velocidad Y para que se muevan a diferentes velocidades Si aleatomizo esto en el primer lo, comienzo desde una posición vertical diferente, puedo hacer que se reinicien todo el camino detrás del borde superior Por lo que se deslizan desde el exterior del área de lona. Sabemos que todo funciona. Y tenemos una clase enemiga que se puede instanciar varias veces para crear un enjambre Borro todo esto porque hay una mejor manera de estructurar este código. 53. Patrón de diseño de piscina de objetos: Voy a convertir cada objeto enemigo en un miembro del grupo de objetos reutilizables. Significa que lo tomaremos del pool de objetos y lo activaremos cuando lo necesitemos. Y cuando hayamos terminado con él, simplemente lo devolveremos al pool de objetos en lugar de eliminarlo, y restableceremos sus propiedades. Reutilizando nuestros objetos. El uso de un patrón de diseño de grupo de objetos como este puede mejorar masivamente el rendimiento de nuestras bases de código Porque evitamos problemas relacionados con la asignación automatizada de memoria y los procesos de recolección de basura que se ejecutan automáticamente. Cuando creamos y destruimos muchos objetos en una base de código, reutilizaremos nuestros objetos enemigos una y otra vez ahorrando memoria. Cada miembro del grupo de objetos necesita tener una propiedad que indique si actualmente está activa o inactiva. Yo lo llamaré libre. Si el objeto está libre, está inactivo esperando en el grupo de objetos para ser utilizado. Si free es false, significa que el objeto está actualmente activo en el juego. Se está dibujando y animando y no es gratis. No está disponible para ser sacado de la piscina. Cada miembro del grupo de objetos necesita dos métodos de ayuda. Método de inicio que se ejecutará. Cuando lo activamos sitio, establecemos el método de reinicio gratuito a false que se ejecutará cuando hayamos terminado con el objeto. Por ejemplo, cuando el enemigo es destruido, método reset hará que la propiedad libre vuelva a ser verdadera, devolviendo al enemigo un charco de objetos libres y disponibles. Sólo actualizaremos y dibujaremos a los enemigos activos. Ejecutaremos los métodos update y draw solo si esta propiedad dot free es false. Enemy es un miembro del grupo de objetos. Tiene una propiedad que indica si es gratuita o si se está utilizando actualmente. Tiene un método para activarlo y un método para desactivarlo, actualizarlo y dibujar Métodos Sólo ejecutar su código para objetos activos Cuando el enemigo se mueve fuera de la pantalla, llamamos método de reinicio para desactivar al enemigo y devolverlo a la piscina de objetos algunas propiedades en el objeto enemigo Por ejemplo, las coordenadas x e y necesitan restablecerse cada vez que el objeto enemigo pasa por su ciclo de vida. Podemos restablecerlos en el punto donde sacamos el objeto de la piscina. O en el punto en que lo devolvemos de nuevo a la piscina. Voy a elegir restablecer las propiedades. Dentro del método de inicio, vamos a alazar al enemigo horizontalmente, y lo pondremos justo detrás del borde superior de la lona Así es como se convierte un objeto regular en un miembro del grupo de objetos. La segunda parte de esto sería administrar el propio pool de objetos. Lo haremos desde nuestra clase de gestión estatal. El grupo enemigo será una matriz que contiene todos los objetos enemigos activos e inactivos. Tenemos que elegir cuántos enemigos creamos. Este número será estático. Tiene que ser lo suficientemente alto para que no nos quedemos sin, pero tampoco demasiado alto porque estaríamos desperdiciando memoria. Podemos ajustarlo más tarde Cuando el juego es jugable, 50 parece que una clase de juego de números razonables necesitará dos métodos de ayuda para administrar el grupo de objetos Un método para llenar la piscina con 50 objetos enemigos. Vamos a ejecutar un bucle for. Cada vez que se ejecuta, toma la matriz de atracción enemiga y empujará a un nuevo enemigo. Ahí dentro, espera el juego como argumento. Le paso esta palabra clave aquí. Eso ya lo hicimos antes. 54. Activadores periódicos: Quiero llamar a crear piscina enemiga automáticamente. Cuando creamos una instancia de clase de juego voy a contras registrar ese grupo enemigo Después de que lo creamos. Necesito usar este punto aquí en consola puedo ver que tenemos una matriz de 50 objetos enemigos. Abro uno para verificar, no vemos ningún valor indefinido o nulo que pueda indicar un problema. Ahora quiero activar periódicamente a un nuevo enemigo. Digamos que cada 1 segundo, cada 1,000 milisegundos. Estamos utilizando request animation frame, que está generando automáticamente marcas de tiempo para nosotros. Vamos a usarlas. Voy a crear una variable auxiliar a la que llamo la última vez. Aquí tendremos la marca de tiempo de la solicitud de bucle de animación anterior. El método de marco de animación es la generación automática de marcas de tiempo. Todo lo que tengo que hacer para acceder a ellos es asignarles un nombre de variable. Aquí sabrá que queremos ese valor de sello de tiempo. Si hacemos esto para cada fotograma de animación, quiero calcular el tiempo delta, que es la diferencia en milisegundos entre la marca de tiempo de este bucle de animación y la marca de tiempo del bucle de animación anterior Después de calcular el tiempo delta, estableceremos la última hora la marca de tiempo actual para que pueda usarse como la antigua marca de tiempo. En el siguiente bucle de animación, tiempo delta nos dice cuántos milisegundos le tomó a nuestra computadora generar un fotograma de animación Entonces podemos pasar ese valor de tiempo delta para renderizar, donde escribiremos alguna lógica simple que acumulará los pequeños valores de milisegundos hasta que se alcance cierto punto de interrupción Entonces podemos activar una solicitud de evento. fotograma de animación ajusta automáticamente la velocidad a la que sirve el siguiente fotograma de animación a la frecuencia de actualización de la pantalla porque sería un desperdicio calcular posiciones para fotogramas que su monitor o pantalla de su teléfono no pueden mostrar. En la mayoría de los casos eso sería de 60 fotogramas por segundo. En render, me aseguro de que se espera tiempo delta. Yo consologatee mil milisegundos divididos por 60 fotogramas por segundo es alrededor de 16.6 Se tarda mi computadora alrededor de 16.6 milisegundos para calcular y volver a divididos por 60 fotogramas por segundo es alrededor de 16.6 Se tarda mi computadora alrededor de 16.6 milisegundos para calcular y volver a dibujar cada fotograma de animación. Perfecto. A medida que se ejecuta el código, estamos generando marcas de tiempo, calculando el tiempo delta y pasando eso al método render. A partir de ahí, podemos hacer que este valor esté disponible en toda nuestra base de código para crear cualquier tipo de evento que se repita periódicamente. Déjame mostrarte lo fácil que es. Necesitaremos dos variables de ayuda, temporizador de anime e intervalo de anime. Animate timer acumulará delta tiempo 0-1 mil. Entonces activará a un enemigo y volverá a empezar a contar. Necesitamos un método de ayuda más que al ser llamado, buscará en la piscina enemiga un objeto enemigo libre y disponible y nos lo dará. En el interior corremos un bucle for sobre la piscina enemiga. Tan pronto como encontremos el primer objeto en la piscina con propiedad libre establecida en true el primer objeto inactivo, devolveremos ese objeto. Puedo poner esa lógica que activa nuevo enemigo cada mil milisegundos directamente en el método de render Pero para mantener el código un poco más limpio, lo escribiré en un identificador de método separado. Los enemigos esperarán como argumento el tiempo delta. Si el temporizador enemigo es menor que el intervalo enemigo, el temporizador enemigo comienza desde cero y el intervalo enemigo es 1,000 siempre que el temporizador sea menor que el intervalo, seguimos aumentando el temporizador por tiempo delta, lo contrario significa que el temporizador ha acumulado 1,000 o más milisegundos Lo volvimos a poner a cero para que pueda contar de nuevo. Y llamamos a conseguir enemigo para activar un miembro del objeto, configurarlo en una variable, y yo llamo método de inicio en ese objeto enemigo inactivo para activarlo. Restablecerá las propiedades del objeto a valores predeterminados y lo marcará como activo. Existe la posibilidad de que no tengamos enemigos inactivos en la piscina cuando se ejecuta este código lo que rompería nuestro código. Solo quiero activar el método de inicio si el método get enemigo fue realmente capaz encontrar un objeto enemigo inactivo disponible. Borro este consolo, voy a llamar a manejar enemigos desde dentro método de render Lo que significa que se llamará una y otra vez por cada fotograma de animación que consolaré enemigo Aquí veo que está activando correctamente a un enemigo. Cada 1 segundo borro el consolo. Cada objeto enemigo tiene métodos de actualización y sorteo. Estos métodos sólo se ejecutan cuando ese objeto está activo, cuando su propiedad free se establece en false. Ahora puedo correr por cada método en el charco enemigo y por cada objeto enemigo, tanto los activos como los inactivos. Llamamos a sus métodos de actualización y dibujo. Pongo con y altura aquí arriba. Quiero que la posición vertical inicial sea menos altura así. Así es como se implementa un patrón de diseño de grupo de objetos simple con el script Java. Estamos activando nuevo objeto enemigo cada 1.000 milisegundos. Los estamos devolviendo al pool de objetos cuando se mueven fuera de la pantalla. Reutilizar nuestros objetos así, en lugar de crear constantemente nuevos y eliminar los antiguos, mejorará el rendimiento de nuestra base de código Especialmente en proyectos donde tenemos grandes cantidades de objetos, puedo hacerlos flotar a la vista muy rápido y luego comenzar a moverse a su velocidad regular así. Tan pronto como todo el objeto sea completamente visible, disminuirá la velocidad. Este pequeño detalle se verá aún mejor cuando agreguemos gráficos y hojas de cálculo animadas Queremos que este proyecto sea totalmente receptivo. Cuando los usuarios juegan en dispositivos móviles, no podrán cambiar el tamaño de la pantalla, pero podríamos tener alguna situación en pantallas de computadora o dispositivos donde estemos intercambiando entre modo horizontal y vertical O cuando estamos cambiando a pantalla completa, a mitad de juego. Donde el área de juego cambia de tamaño. Para dar cuenta de estos escenarios improbables pero posibles, me aseguraré de que todos los enemigos activos estén siempre dentro del área de juego visible. Si el borde derecho del rectángulo enemigo está más allá del borde derecho del área de juego, muévelo. Ahora bien, si el área de juego cambia de tamaño a mitad del juego, simplemente empujará a los enemigos al área de juego visible, asegurándose de que no tengamos enemigos activos flotando fuera de la pantalla donde no podamos golpearlos. 55. Controles de ratón: Golpearemos a los enemigos por el ratón y por eventos táctiles. Crearemos un objeto de ratón de cliente y le daremos propiedades X, Y y prensadas. Esto nos ayudará a que posición del mouse esté disponible en toda la base de código, no solo dentro de los oyentes de eventos Lo mismo que hicimos con el tamaño Re. Aplicaré automáticamente el oyente de eventos mouse down aquí dentro del constructor de clase de juego Necesito usar la función ES six arrow aquí para asegurarme de que esta palabra clave apunte al objeto del juego desde dentro de su llamada de vuelta dentro. Podría llamar a prevenir default en caso de que el evento esté haciendo algo que no nos gusta. Podría ser más relevante Más tarde con eventos táctiles lo probaremos y veremos por ahora no creo que lo necesite aquí. Desde aquí establecí la posición X e Y del evento mouse down a las propiedades X e Y de nuestro objeto mouse personalizado, haciéndolas disponibles de una manera más fácil. Por toda la base de código consultaré el objeto de evento autogenerado Y aquí estamos accediendo a estas propiedades X e Y. Si su lienzo no cubre la ventana completa del navegador, tendríamos que usar offset x y offset y desde aquí. En cambio, consulgaré estos valores solo para comprobar si todo está funcionando, doy click y estoy obteniendo x e y del click. Bonito. 56. Detección de colisiones: Crearé un método reutilizable de verificación de colisión. Lo usaremos para verificar colisiones entre enemigo y mouse, pero también se puede usar para otras características más adelante El método personalizado de colisión de verificación tomará rectángulo uno y el rectángulo dos como argumentos. Utilizará las cuatro líneas habituales de código para verificar si chocan dos objetos rectangulares Ambos objetos pasaron a este método. Ambos rectángulos necesitan tener propiedades de posición x e y, y anchura y altura Para que esto funcione, evaluaré directamente la expresión y simplemente volveré verdadero o falso cada vez que se llame a este método. La fórmula común habitual para detección de colisiones entre dos ejes alineados, es decir, rectángulos no girados Comprueba si el lado izquierdo del rectángulo uno está a la izquierda del lado derecho del rectángulo dos. Al mismo tiempo, comprobamos si el lado derecho del rectángulo uno está a la derecha del lado izquierdo del rectángulo dos. Si ambas comprobaciones son ciertas, ya sabemos que los rectángulos chocan horizontalmente, pero aún pueden estar muy separados Necesitamos dos cheques más al mismo tiempo. Necesitamos verificar si el borde superior del rectángulo uno está por encima del borde inferior del rectángulo dos. Al mismo tiempo, también necesitamos verificar si el borde inferior del rectángulo uno está debajo del borde superior del rectángulo dos. Sólo si las cuatro comprobaciones son ciertas, tenemos una colisión. Aunque una de estas cuatro comprobaciones sea falsa, sabemos que los rectángulos están muy separados y no pueden chocar Toda esta expresión devolverá falsa. Ahora necesitamos asegurarnos de que cualquier objeto que pasemos para verificar colisión como rectángulo uno o rectángulo dos necesita tener propiedades x, y, width y height. De lo contrario, la comprobación de colisión no funcionaría. Revisaremos cuatro colisiones. Aquí estamos dentro del método de actualización en la clase enemiga. Si el método check collision que se define entre este objeto enemigo y el objeto mouse devuelve true, llamamos a reset en el enemigo devolviéndolo de nuevo a la piscina de objetos. Me sale un error. asegurarme de que el objeto del mouse tenga propiedades with y height. Será sólo un rectángulo muy pequeño, una vez un píxel. El problema es que cuando hago clic en algún lugar y alejo el mouse, ese evento mousedown colocó ahí mi objeto de mouse personalizado y sigue colisionando Cuando se dispara el evento mousedown, configuramos mouse pulsado el true, creo otro oyente de eventos para mouse up event También actualizaré las coordenadas X e y a esa última ubicación conocida. Y voy a poner el ratón presionado a falso. Ahora puedo agregar una condición más. Si este enemigo y los objetos del ratón chocan, y solo si se presiona el mouse al mismo tiempo, solo entonces reinicia al enemigo Ahora puedo hacer clic en los enemigos y ellos se restablecerán correctamente y volverán al pool de objetos. Podría hacer otras cosas aquí. Por ejemplo, cuando hago clic en ellos, se deslizan así hacia abajo. usaremos para algo más adelante con nuestro alienígena cambiante de dimensión gradual Vuelvo a resetear aquí. Puedo limpiar este código. Ya que estamos definiendo valores de x e y dentro del método start, podemos simplemente declarar las propiedades aquí sin ningún valor. Porque sabemos que el método start necesita ejecutarse de todos modos antes de que el objeto sea dibujado en el juego. Está más limpio porque ahora si quiero hacer algún cambio a estos valores, solo lo hago dentro del método start. No tengo que verificar estos valores en dos lugares separados. Los enemigos también pueden tener múltiples vidas. Sólo puedo definirlo aquí, pero sólo le asignaré valor en método de inicio lateral. De esa manera, cuando reutilizamos un viejo objeto enemigo, sus vidas se restablecerán de nuevo al valor original. Cada Neme tendrá dos vidas. Tenemos que darle dos veces. Dibujaré las vidas aquí usando el método de texto de relleno incorporado. Le pasamos tres argumentos, el texto que queremos dibujar, y las coordenadas x e y del punto de anclaje en relación con el que queremos dibujar ese texto. Pondré ese punto de anclaje exactamente en medio del rectángulo de enema rojo. Necesitamos establecer el estilo de relleno del texto en un color diferente. Por ejemplo, azul en Maine GS. Dentro del método resize, que también se ejecuta en la primera carga de página, estableceremos la propiedad de fuente cannavas en 50 píxeles bangers Ponemos el punto de anclaje del texto exactamente en medio de enemigos. Hay dos propiedades Navas que manejan la relación entre el texto y su punto de anclaje Podemos establecer el texto align al centro para alinearlo horizontalmente. Podemos establecer la línea base del texto en el medio para alinear el texto verticalmente exactamente sobre las coordenadas del punto de anclaje como las definimos en la línea 49 en el archivo GS enemigo. Ahora en lugar de restablecer inmediatamente al enemigo, cuando le hagamos clic, reduciremos sus vidas en uno La colisión se registra una vez por fotograma de animación, lo que las vidas disminuyen muy rápido. Antes de arreglar eso, también decimos si la vida del enemigo es menor a una, restablecemos a ese enemigo devolviéndolo al fondo de objetos. Necesitaremos este cheque más adelante en varios lugares para que el código sea más fácil de leer. Lo pondré dentro de un ayudante separado. El método está vivo, el método evaluará directamente esta expresión y volverá verdadera si el enemigo está vivo y cae si sus vidas son menores de una. Si el enemigo no está vivo, restablece al enemigo. Ahora necesitamos una bandera que indique si el clic del ratón ya se utilizó para disparar nuestra arma. Debido a que solo queremos disparar una vez por clic, le doy a mouse una nueva propiedad llamada fired. Inicialmente, será falso. Aún no se ha disparado el disparo. Solo disminuimos vidas enemigas en uno si disparado es falso. Y luego inmediatamente prendimos fuego a la verdad. Para asegurarnos de que este bloque de código se ejecute solo una vez, entonces necesitamos recargar nuestra arma y ponerla disparada de nuevo a false para que podamos disparar de nuevo Creo que podemos poner disparado de nuevo a falso ya sea con el mouse hacia abajo o con el mouse hacia arriba. Vamos a intentarlo. Sí, esto funciona. Ahora eliminamos solo una vida por clic. Tenemos que hacer clic dos veces a cada enemigo para agotar sus vidas y restablecerlo. Elimino el estilo de relleno aquí. En cambio, aquí lo puse en blanco. También estableceré el estilo de trazo en blanco también. Es mejor para el rendimiento poner estas declaraciones aquí dentro del método de resize que se ejecuta solo una vez en la primera carga O cuando cambiamos el tamaño, el método de dibujo de pantalla se ejecuta 60 veces por segundo Declarar el estilo de relleno aquí es menos eficiente cuando se trata de rendimiento Elimino este estilo de relleno, fuente más pequeña, tenemos un mundo responsive que redimensiona para acomodarse a cualquier tamaño de pantalla, vertical u horizontal Funcionará en una pantalla de computadora masiva y súper ancha. Funcionará en tabletas. Y funcionará tanto en móvil en horizontal como en modo retrato. Hagamos que sea aún más amigable para dispositivos móviles al incluir eventos táctiles. 57. Eventos táctiles: Podría crear un objeto táctil personalizado separado, pero en realidad usaré el objeto mouse que funcionará perfectamente aquí. Funcionará automáticamente tanto para los clics del mouse como para los eventos táctiles. inicio táctil básicamente hará lo mismo que el mouse hacia abajo. El evento Touch End hará lo mismo que el evento mouse up. Esto no funcionará todavía porque para eventos táctiles, este objeto de evento autogenerado estructura la información sobre el evento de una manera diferente Consologato aquí en el navegador Google Chrome, hago clic en la barra de herramientas Dispositivo para activar la vista de pantalla responsive, lo que nos permitirá emular eventos táctiles en la Hago clic en la pantalla, y sé que funcionó porque estamos consolando el objeto de evento táctil Busco las coordenadas X e Y del evento touchstart Y puedo ver que el navegador organiza los puntos de contacto con una superficie táctil dentro de esta propiedad de lista táctil. Creo que podemos tratarlo como una matriz. Eso lo probaremos más adelante. Puedo ver que para acceder a coordenadas x e y del evento touch, necesito acceder al evento cambiado toca elemento con un índice de cero punto pagex y pagey También hago lo mismo para el evento touch end. 58. Texto del juego: Ahora tenemos un mundo de juego receptivo. Y podemos usar eventos táctiles para disparar a los enemigos. Deshabilitaré la barra de herramientas del dispositivo en Google Chrome. Ahora estamos viendo nuestro juego en una vista regular del navegador de computadora. Puedo poner el código para mostrar la partitura y otro texto del juego directamente dentro del render, pero quiero mantener esto limpio. Nuevamente, lo pondré en un método separado, haré que nuestra base de código sea más fácil de navegar. Dentro del método de texto de estado de dibujo, llamaré método de texto de relleno incorporado y le paso el texto que quiero dibujar y las coordenadas X e Y del punto de anclaje en relación con cual quiero dibujar ese texto. Yo lo llamo desde adentro render, puedo ver que la partitura no es completamente visible. Quiero que el texto que muestra vidas enemigas se mantenga centrado, pero quiero que el texto de la partitura quede alineado. Lo envuelvo entre métodos integrados seguros y de restauración que restringirán cualquier cambio que realice al estado de lienzo a esta área. Todo el código de dibujo fuera este bloque no se verá afectado De esa manera puedo dejar la línea de texto para la puntuación y línea de texto establecer el centro para la puntuación de vidas enemigas. El texto fluirá ahora hacia la derecha desde este ancla x e y. De hecho, vamos a contar el puntaje. Lo defino aquí, método de inicio adentro, establecemos puntaje a cero porque esto también se usará como método de reinicio. Ahora agrego esta variable aquí, fuente más pequeña. Cuando agotamos vidas enemigas disparándole, restablecemos al enemigo Devolviéndola de nuevo al grupo de objetos. Aumentamos la puntuación del juego en uno. Necesito deletrear la puntuación correctamente si tenemos múltiples tipos de enemigos. También podríamos aquí a una puntuación que sea relativa a vidas enemigas por ejemplo. O cada tipo de enemigo puede tener su propia propiedad de puntuación que definirá la puntuación que otorga. Ahora cuando hacemos clic en enemigos, obtenemos puntos de puntuación. También quiero darle vida al juego. Estas son las vidas de los jugadores. Si los perdemos, terminamos un juego. No lo confundas con vidas de cada enemigo. Estos indican cuántas veces tenemos que golpear a cada enemigo. Estas son vidas de jugadores. Cuando iniciamos o reiniciamos el juego, establecemos vidas de jugadores a, por ejemplo, diez. Dentro del texto de estado del sorteo, crearé un bucle de cuatro que se ejecutará una vez por cada vida. Dibujaré un rectángulo simple que representa cada vida. Al principio necesita x, y, ancho y alto. Ahora los estoy dibujando a todos uno encima del otro, ¿de acuerdo? Esto es x, y, ancho y alto. Este valor será el margen de la izquierda. Multiplico la posición x horizontal por el índice de los cuatro bucles, que los alinearán en fila uno al lado del otro. Si el ancho de cada rectángulo de vida es mayor o igual al espacio en valor, se verá como una barra de salud continua, que podría ser algo que quieras. Si el espacio adentro es más que el ancho llegamos a ver vidas individuales. Voy a ir por este punto de vista porque después quiero cambiar cada vida en una especie extraterrestre humanoide diferente Agregará más apuestas a nuestro juego de terror espacial porque no queremos que los enemigos se coman a nuestros tripulantes únicos. Si esto no está claro, solo juega con los valores. Mira lo que sucede. Es bastante sencillo si enemigo logra viajar todo el camino a través de la pantalla, todo el camino hacia abajo y no lo interrumpimos, no lo disparamos. Logró ponerse al día con nuestra flota que se escapa y se comerá a uno de nuestros tripulantes Reiniciamos al enemigo y reducimos la vida del juego en uno. Si cambiamos el número de vidas, aparecerán los rectángulos que representan cada vida Esto funciona bien. Cerremos el bucle del juego. La puntuación ganadora será de tres. Mostraremos tres mensajes en pantalla. Cuando termine el juego, tendremos tres conjuntos de estos mensajes. Un mensaje se mostrará solo en la primera carga. Antes de presionar el botón para iniciar el juego, elimino este valor de Game Over. Cuando empezamos el juego, colocamos el juego a falso. Crearé un juego de activación separado sobre el método para asegurarme de que este bloque de código se ejecute solo una vez. En este punto, podría poner esta lógica dentro del render, pero si la pongo en un método separado, hará que sea más fácil tocar un sonido especial de terror más adelante cuando ganemos y cuando perdamos el juego. Se ejecutará sólo una vez cuando se le llame. Y nos aseguramos de eso solo ejecutando su código cuando game over es falso por dentro, inmediatamente configuramos game over a true. Activamos el juego terminado, lo que podría significar que perdimos el juego. vida de los jugadores es menor que uno o ganamos el juego cuando la puntuación es mayor o igual que la puntuación ganadora. Cuando perdemos, establecemos algún texto único para el mensaje uno y el mensaje dos. Si ganamos damos a esas mismas variables diferentes textos entonces tenemos que llamar a ese método. Puedo hacerlo desde render o desde el texto de estado del sorteo aquí, si esta vida es menor que una o si esta puntuación es más o igual a la puntuación ganadora, llamamos juego de activación sobre el cual pondrá el juego a true y configurará los mensajes. Entonces si el juego terminado es cierto, queremos dibujar el mensaje 12.3 en medio de la pantalla Centro de alineación de texto, fuente grande puede ser de 80 píxeles bangers, llamamos llenar mensaje de texto uno, coordenadas x e y serán la mitad del área de juego Mensaje dos y mensaje tres. Nosotros hacemos lo mismo. Ajusto sus coordenadas verticales para intentar alinearlas. -25 mensaje uno, más 25 para mensaje dos y más 50 para mensaje tres Yo juego. Espero hasta que perdamos. Bien, eso funcionó. Quiero que los mensajes 2.3 se dibujen en una fuente más pequeña. Intento 20 pixeles, ese es el mensaje perdedor. Refresco la página, vuelvo a jugar, y gano ganar mensaje. Tenemos un mensaje más, haremos una exhibición en un minuto. Dije juego a la verdad. 59. Comienza a reiniciar: He creado este botón de reinicio aquí, quiero que active el método de inicio al hacer clic Primero creo una propiedad en el objeto principal del juego y apunté hacia ese elemento button usando su ID. Después agrego el oyente de eventos en ese botón. Escuchamos un evento de clic. Nuevamente, tengo que usar una función de flecha aquí para asegurarnos de que tenemos acceso a este punto desde dentro del oyente de eventos para asegurarnos de que esta palabra clave apunta hacia el objeto del juego A partir de ahí llamo a esto Do Start. Ahora puedo ver los mensajes de inicio, pero no están formateados. Bueno, lo arreglo llamando a esto, redimensionar automáticamente desde dentro del constructor de clase de juego Al hacer esto, se corrigen los ajustes de estilo de relleno y línea base de texto, pero ahora la puntuación no está definida. Inicialmente lo pondré a cero aquí dentro del método de manejar enemigos, periódicamente activamos nuevo objeto enemigo y los agregamos al juego. Solo quiero que sigan viniendo nuevos enemigos si el juego terminado es falso. Ahora cuando refresque la página, solo vemos el conjunto inicial de mensajes y no pasa nada hasta que hacemos clic en el botón R aquí para iniciar el juego. Tener un botón que inicia el juego por interacción del usuario también resolverá problemas con los sonidos del juego. Hablaremos de eso más adelante. Pero básicamente en los navegadores modernos, los sonidos se reproducirán solo después de que el usuario interactuara con el documento El usuario haga clic en este botón contará como una interacción que permitirá que se jueguen los sonidos de nuestro juego. Yo creo otro oyente de eventos. Esta vez para un evento key up, caso registro el objeto de evento. Estamos buscando esta propiedad clave. Yo digo si tecla, la tecla que se presionó en el teclado es Enter, o si la tecla es R, utilizo al método minúscula aquí para dar cuenta tanto de minúsculas como de mayúsculas que se está presionando. Ejecutamos un método de inicio para iniciar el juego. R necesita estar entre comillas aquí. este momento, el método de inicio básicamente solo restablecerá la puntuación a cero y repondrá nuestras vidas También quiero resetear a todos los enemigos activos. Yo atropello todos los objetos dentro matriz de piscinas enemigas y llamo reset en ellos. A lo mejor quiero activar a dos enemigos rápidamente. Inmediatamente cuando comienza el juego. Podría hacer un bucle de cuatro como este. Intenté conseguir enemigo si había un objeto enemigo libre disponible, encontramos que nos activamos llamando al método de inicio enemigo. No te confundas aquí. Método de inicio que se sienta en el objeto del juego se inicia y se reinicia El método de inicio del juego que definimos dentro del GS enemigo en el objeto enemigo activa al miembro del grupo de objetos enemigos dos métodos diferentes. Ahora podemos iniciar o reiniciar el juego en cualquier momento haciendo clic o tocando este botón R o presionando Enter o letra R en el teclado. Esto es solo para mostrarte cómo puedes dar a tus jugadores diferentes opciones. También podemos crear una ola inicial más rápida activando inmediatamente a algunos enemigos al principio aquí. Entonces el resto vendrá en intervalos de 1 segundo. 60. Juegos a pantalla completa: El desarrollo web moderno tiene muchas características geniales. ¿Has intentado usar la funcionalidad de pantalla completa incorporada antes? Básicamente, simplemente elimina los botones del navegador y la barra de navegación, y ampliará tu aplicación para ocupar la pantalla completa. Esto funcionará en monitores de computadora, en tabletas, así como en pantallas móviles. Lo usaremos para hacer un juego de pantalla completa adecuado. Ahora, es muy sencillo implementarlo. Déjame mostrarte, ¿te interesan los juegos de pantalla completa? Házmelo saber dejando un comentario. Y si a la gente le gusta, puedo usar esta función más a menudo para otros proyectos. Puedes ir a esta dirección web para leer la documentación si quieres más detalles. Todo lo que necesitamos para que esto funcione es así de simple si la declaración L que básicamente dice, si pantalla completa actualmente no está activa activada. Si actualmente está activo, desactivado. Todas estas propiedades están integradas en todos los navegadores modernos. Así que simplemente puedes copiar este pequeño bloque de código que pegué aquí en la clase de juego principal togal método de pantalla completa Siempre que se llame a este método desde cualquier parte de nuestra base de código, comprobará que esté activo a pantalla completa, si no está activado. Si está activa, desactivada, probamos si funciona aquí, de lo contrario si la tecla que se presionó es barra espaciadora o si la tecla que se presionó es letra Siéntase libre de usar diferentes claves de control aquí si sabe lo que está haciendo. Ahora presiono carta o barra espaciadora y mi juego alterna entre pantalla completa y la vista normal del navegador Realmente no lo estoy mostrando bien aquí porque no estoy grabando los bordes de mi pantalla. Pero si estás codificando solo, puedes hacerlo tú mismo con tu base de código y ver que esta es una funcionalidad de pantalla completa muy agradable y adecuada. También queremos poder alternar a pantalla completa en dispositivos móviles donde no haya teclado que tengamos, este botón de pantalla completa que creé anteriormente. También puede habilitar y deshabilitar la pantalla completa mediante gestos, ejemplo, deslizando hacia arriba y hacia abajo. Si el swipe es más largo que un número establecido de píxeles, cuando está en una dirección específica, podemos activar nuestro código personalizado, que podría ser, por ejemplo, habilitar y deshabilitar la pantalla completa Lo hice en un tutorial diferente para este sencillo juego de corredor sin fin y jumper amigable para dispositivos móviles . Fue hace algún tiempo en mi Youtube. Igual que hicimos con el botón de reinicio. Yo hago referencia al botón de pantalla completa en mi código. Le adjunto oyentes de eventos. En la devolución de llamada, llamo a togal full screen method game is fully playable in the full screen mode Lo que ves ahora no es del todo correcto porque no estoy grabando toda mi pantalla. El planeta y el texto están un poco de centro y los botones no están aquí. Pero si estás codificando, puedes ver en tu proyecto que todo está funcionando bien. Tenemos una pizarra en blanco de un juego con un conjunto completo de características, juego completo, loop, ganar y perder estado, teclado del mouse y controles táctiles, reinicio y funcionalidad de pantalla completa. Este juego funcionará en grandes monitores horizontales de computadora, así como en pequeñas pantallas móviles verticales. Puedes ajustar la puntuación ganadora en la línea 16 o vidas en la línea 77 para ajustar las condiciones de ganar y perder en la descripción del video Te estoy dando un código fuente completo del proyecto. En esta etapa, puedes compararlo con tu propio código si encuentras algunos problemas. Puedes tomar este código y usarlo para crear muchos juegos diferentes, ser creativo con los temas y gráficos, y hacerlo tuyo. En la siguiente parte, convertiré esto en juego de terror espacial de dibujos animados con sonidos y tripulación aleatorizada que tenemos que proteger de la fase de alienígenas que comen carne Con esto concluye la parte amigable para principiantes del curso. En la siguiente parte, les mostraré una implementación mejorada del patrón de diseño estatal e iré un poco más rápido. Bien hecho. Si llegas tan lejos, avísame dejándome un comentario diciendo, terminé el proyecto para principiantes. 61. Miembros de la tripulación simples: Tenemos un juego que funciona en pantallas pequeñas y grandes, retrato y paisaje, vertical y horizontal. Ahora es el momento de la diversión. Agregamos algunos gráficos y animaciones. Puse vidas a 15. Yo creo un diff con una idea de activos dentro. Pondré nuestras imágenes y sonidos de proyecto. Puedes descargar todo lo que estoy usando aquí. En la sección de recursos a continuación tripulación imagen PNG y el ID será Tripulación Doy activos, mostrar ninguno. Queremos esconder todo ahí dentro. Solo queremos cargar esas imágenes en la memoria para que podamos dibujarlas con Javascript en Canvas. Quiero dibujar vidas como pequeños tripulantes. Empecemos simples. Traigo la imagen de la tripulación mi proyecto usando get element by ID aquí. Dentro del método de estadísticas de sorteo, dibujamos rectángulos para representar la vida de los jugadores. En su lugar, voy a utilizar construido en dibujar el método de imagen. Le pasamos la imagen que queremos dibujar y las coordenadas X e Y. Dónde dibujarlo, me ajusto con y me escondo aquí para que coincida con la imagen que estamos usando. Este es el margen izquierdo, este es el espacio entre los pequeños tripulantes. Puedo hacer que tengan espacio alrededor o los hago pegados más cerca uno encima del otro. Cuando perdemos a todos los tripulantes cuando fueron comidos por los extraterrestres, perdimos el juego. 62. Tipo de enemigo simple: Quiero crear diferentes tipos de enemigos. Esta es la principal clase enemiga. Contiene todos los métodos y propiedades que se compartirán entre todos los tipos de enemigos. Primero haré un enemigo simple con la implementación más básica, pero mi objetivo final es mostrarte cómo usar a un enemigo complejo con múltiples estados. Y cómo estructurar su código donde cada estado está en su propio bloque de código dedicado usando el llamado patrón de diseño de estado. Enemigo simple básico con una vida, sin características especiales, sin habilidades especiales e interacción. Yo lo llamo Beetle Moorph. Volveré a mi juego Space Invaders desde un tutorial diferente y obtendré la hoja de cálculo que usamos allí en este proyecto Los enemigos son un poco más grandes. Si estás viendo la versión extendida, te estoy dando todas las exportaciones más grandes de todos los tipos de enemigos que usamos en toda esta serie de juegos espaciales. Si quieres, puedes jugar con esto más tarde e implementar el que quieras Por ahora, solo voy a agarrar la hoja de cálculo Beetle Moorph en la sección de recursos y la pondré en mi carpeta subclase Beetle Moorph extiende el constructor de superclase enemiga esperará el juego Y lo pasaré al constructor de superclase cuando lo llamemos aquí estoy llamando constructor de clase en la clase enemiga padre que estamos extendiendo Esa clase se define aquí que hará que todo este código aquí se ejecute primero. Estas son todas las propiedades compartidas. Las propiedades que son las mismas para todos los tipos de enemigos en la subclase de morph de escarabajo Sólo voy a definir propiedades específicas de este tipo de enemigo en particular. Tendrá una imagen única que he importado aquí. Enlace de descarga en la sección de recursos a continuación, apunto Javascript hacia la imagen usando get element by ID. También necesitará su propio método de inicio. Primero, llamaremos al método de inicio en la clase enemiga padre. En la superclase así, cuando llamamos al método start en el enemigo Beetlemorph, primero ejecutará todo el código dentro de este Aquí desde la línea 13, copio velocidad x y velocidad Y. Cuando llamemos a start on betlemorph, les voy a dar valores Quiero que este tipo de enemigo se mueva directamente hacia abajo velocidad x cero y algo velocidad positiva Y. Elimino estos valores en las líneas 8.9 Se establecerán en algo diferente para cada tipo de enemigo dentro de sus métodos de inicio asociados. También elimino vidas del método de inicio compartido, porque cada tipo de enemigo tendrá un número diferente de vidas. En la versión extendida, también vuelvo a proyectos Space Invaders y Planet Defense, que son todos parte del mismo gran curso, incluyendo este juego y otros Estoy agregando lecciones extra implementando la nueva fase en tipo enemigo que solo puede ser golpeada cuando está en la misma dimensión que el jugador. Estamos extendiendo una clase de propiedades compartidas se sientan en la superclase enemiga Las propiedades únicas de cada tipo de enemigo se ubicarán en la subclase Primero activamos constructor y método de inicio en la superclase en la clase padre Después agregamos algunas propiedades y valores específicos este tipo de enemigo en particular dentro del método de actualización. Aquí comprobamos si hay colisión entre enemigo y ratón. Voy a cortar esto y lo pondré en un método separado. Es principalmente porque tendremos una fase en tipo enemigo que no siempre puede ser golpeada. Necesito poder controlar mejor cuando verifiquemos si hay colisión. Ahora está en un método de hit separado. Todo este código dentro de la actualización será compartido y ejecutado para todos los diferentes tipos de enemigos en nuestro juego. Beetle Moorph tendrá su propio método de actualización que lo anulará Si este objeto enemigo está actualmente activo, llamamos a todo el código dentro del método de actualización desde la línea 31. Entonces si este enemigo está vivo, llamamos método de golpe comprobando colisión entre este enemigo y ratón. Ahora necesito llamar a esa subclase. Aquí empujamos a New Beetle morph a la piscina enemiga. Cierto, se me olvidó aquí tengo que llamar al método de actualización en la clase enemiga padre. Eso funciona. 63. Animación de sprite: Ahora queremos dibujar la hoja de cálculo. Puedo poner este código dentro del método de sorteo en la clase enemiga padre compartida. Porque todos los tipos de enemigos tendrán una hoja de cálculo que yo llamo método de dibujar imagen incorporada Y le paso la imagen que quiero dibujar y las coordenadas x e y, Dónde dibujarla. Esto simplemente dibujará toda la hoja de cálculo con todos sus marcos en su tamaño original. Podemos darle ancho y alto opcionales aquí, que exprimirán o estirarán la hoja extendida en el área que definimos. Necesitamos la versión más larga del método draw image con nueve argumentos. Nos dará un control completo sobre esta hoja de cálculo. Le pasamos la imagen, queremos dibujar la fuente x, la fuente y, la anchura de la fuente y la altura de la fuente del área. Queremos recortar de la imagen de origen y el destino X, el destino Y, el ancho de destino y la altura de destino para definir en qué lugar del lienzo de destino queremos colocar esa pieza recortada de imagen en las hojas de sprites que estamos usando, tienen tamaño de fotogramas individuales en 100 veces 100 píxeles Yo defino esas propiedades aquí que establecí dentro de la altura relativa a esa. Estos dos valores son para cultivo en. Estos dos valores determinan el tamaño al que dibujaremos la imagen en Destination Canvas. Estos dos conjuntos de valores no necesitan ser los mismos. Podemos escalar esto y lo haremos más adelante. Y si quiero recortar el marco superior izquierdo de 00 a sprite con altura de sprite Niza, Estamos utilizando el método de dibujar imagen para recortar este marco de la hoja de sprite La propiedad Frame Y nos ayudará a viajar alrededor de la hoja de cálculo verticalmente La forma en que organicé esta hoja de cálculo, puedes ver que nos dará diferentes variaciones del mismo tipo de enemigo Todos los enemigos tendrán cuatro filas de sprites, cuatro variaciones. Así puedo aleatorizar el marco Y aquí dentro del método de inicio compartido Si cada enemigo tuviera una hoja de cálculo diferente, tendríamos que poner esto en cada método de subclase enemiga separado veces al azar cuatro envuelto en piso de método significa que cada vez se ejecuta el método start y sacamos a un enemigo de la piscina, se le asignará aleatoriamente frame y cero o uno o dos, o tres El argumento Y de origen pasado para dibujar método de imagen determinará el recorte vertical en coordenadas. Entonces lo pasamos frame y times sprite height. marco y puede ser 01230 veces la altura del sprite, es esta una vez la altura del sprite es esta dos veces la altura del sprite, tres veces la altura del sprite tres Esta es una forma sencilla de aleatorizar un poco tus hojas de sprites y darnos algunas variaciones del mismo tipo de enemigo Todos tienen las mismas características y paleta de color, pero tienen diferentes detalles lo hace más interesante. Creo que también necesitaremos el marco X para navegar por la hoja de sprites horizontalmente También tendré propiedad last frame para definir cuántos fotogramas horizontales tenemos por fila cuando el método start on enemies runs Para activarlo, establecemos el frame X en cero. Cada enemigo podría tener un Last frame diferente Beetle morph tiene una hoja de sprites muy simple, solo tres fotogramas por fila. Dentro del método de actualización compartida, manejaremos la navegación horizontal de sprites cuando el enemigo pierda todas las vidas Cuando este punto está vivo es falso, seguimos aumentando el fotograma X en uno. Cuando el fotograma X es más que el último fotograma, sabemos que se han visualizado todos los fotogramas de animación de explosión . Y sabemos que es seguro reiniciar al enemigo y devolverlo al fondo de objetos. Si el juego terminado es falso, aumentamos la puntuación en uno, y puedo eliminar esto. Finalmente, a medida que el argumento source x pasó el método draw image que determina la coordenada horizontal del área de recorte, pasamos frame x times sprite width Cuando el marco x es 00 veces el ancho brillante, dibujamos este marco una vez el ancho del sprite, dos veces el ancho brillante Espero que tenga sentido. Yo juego, hago clic en cada animación dos veces, agoto sus vidas, y las obras de animación que 64. Tiempo de animación: Se está jugando muy rápido. Quiero poder controlar qué tan rápido saltamos de cuadro a cuadro. Al animar nuestras hojas de sprites, haremos otro evento periódico, igual que lo hicimos antes cuando activamos nuevo enemigo cada 1,000 Actualizaremos todas las hojas de sprites en nuestro juego. Cada, digamos 200 milisegundos. La actualización de Sprite será falsa. Cada vez que el sprite cuente hasta 200, establecerá la actualización de sprite Puedo poner esa lógica directamente en mi método de render, pero para mantener nuestro código más limpio, lo separaré a su propio bloque de código. Yo lo llamaré, por ejemplo, manejar temporizador de sprite. Se esperará como argumento el tiempo delta. Si el temporizador de sprites es menor que el intervalo de sprites, seguimos acumulando contrario una vez que acumulamos lo suficiente, reiniciamos el temporizador de nuevo a cero para que pueda contar de nuevo. Y estableceremos la actualización de sprites en true, permitiendo que nuestras hojas de sprites salten de fotograma a fotograma Será cierto sólo para ese único fotograma de animación. Todas las demás veces se pondrá de nuevo a la cara. Ahora llamamos temporizador de sprite handle aquí, este valor de tiempo delta se pasará a lo largo Eso debería estar todo bien aquí. Dentro del método de actualización en clase enemiga solo si el enemigo no está vivo y si la actualización de sprites es verdadera solo entonces salta de cuadro en cuadro en la hoja de sprites Voy a ajustar este bloque de código. Tenemos que tener mucho cuidado con los corchetes aquí. Es fácil romper nuestro código con un solo soporte que falta. No funciona porque tengo que usar sprite update aquí. En realidad, un error más que cometí en alguna parte. Veo que he escrito mal el intervalo de sprites aquí en la línea 24. Puedo ralentizarlo o puedo acelerarlo dando diferentes valores al intervalo de sprite aquí, 1,000 milisegundos los harán animar Muy 50 milisegundos es muy rápido. 150 milisegundos parece una buena relación calidad-precio aquí. Por ahora. Ahora todo funciona bien. Aquí tenemos un comportamiento que no me gusta. Si dos enemigos se superponen y los hacemos clic, destruye al enemigo en la espalda. ¿Por qué es esto y cómo hacemos que destruya al enemigo en el frente? Estamos en bicicleta sobre la piscina enemiga y llamando a actualizar y dibujar. Estamos dibujando a todos los enemigos en mismo elemento de lienzo en un solo marco de animación. A medida que ejecutamos esto para cada método atraemos enemigos uno por uno enemigo. Dibujamos primero aparecerá detrás del enemigo que fue dibujado después. Al mismo tiempo, las comprobaciones de colisión dentro del método de actualización se ejecutarán primero para el enemigo que fue dibujado primero y ese enemigo es destruido. Una forma de arreglar esto sería atropellar a la piscina enemiga desde una dirección para llamar a actualización y luego desde la dirección opuesta para llamar a dibujar. De esa manera los enemigos que se dibujan últimos, es decir, en la parte superior, serán revisados por colisión primero Con esta estructura de código. Cuando dos enemigos se superponen y los hago clic, el de arriba, se destruye el frente. Eso es mejor ya que separamos el tamaño del cultivo y el tamaño del dibujo. En realidad podemos hacer algunas escalaciones aquí. Puedo multiplicar ancho y alto por el mismo valor modificador de tamaño. Puedo hacer que los sprites sean más grandes o más pequeños. ¿Qué tal un modificador de tamaño aleatorio entre 0.4 y 1.2 Hace que el juego sea un poco más interesante, tal vez. ¿Qué tal un valor aleatorio entre 0.6 y 1.3? 65. Modo de depuración: Quiero poder mostrar y ocultar algunos elementos de ayuda en el juego como estas cajas blancas de colisión y números que muestran vidas enemigas. Vamos a crear un modo de depuración en la clase principal del juego. Creé esta propiedad de error e inicialmente la configuré en true inside key. Si la tecla que se presionó en minúscula es la letra D, volveremos a establecer su valor opuesto. Si es verdad, lo establecemos en falso. Si es falso, lo establecemos en true. Dentro del método de sorteo en clase enemiga, solo dibujamos el rectángulo blanco alrededor de los enemigos y vidas enemigas. Si la bolsa es verdadera, me aseguro de guardar los cambios en todos mis archivos y ahora puedo presionar D en mi teclado para alternar el modo de depuración. De vez en cuando le doy a Beetle Morph sólo una vida. Porque cuando no mostramos vidas enemigas, ese primer clic no tiene ningún feedback visual y no sabemos si golpeamos al enemigo. Ajusto rango de modificador de tamaño aquí en la línea seis, dije ganar puntaje 220. Inicialmente, back se establecerá en false. Si estás contento con tener enemigos simples como este, puedes implementarlos de la manera que acabamos de hacer. Creé una serie de juegos y proyectos con este estilo artístico, y todos los activos artísticos son compatibles y se pueden utilizar entre los proyectos. Si quieren, ahora agarraré a uno de los enemigos más complejos que implementamos antes e intentaré implementarlo de la misma manera que acabamos de hacer. Después agregaremos un tercer tipo de enemigo usando el patrón de diseño del estado. Lo cual tiene un poco más de complejidad pero nos da mucha más libertad a la vez que mantenemos nuestro código limpio, modular y organizado. En los otros proyectos, tenemos por ejemplo, un enemigo blindado, enemigo inflable, enemigo vengativo que nos devuelve el tiro Tenemos un tipo de enemigo bost de gran tamaño con una enorme piscina de salud Tenemos un enemigo cargando que acelerará y nos golpeará. Voy a agarrar, por ejemplo, a éste, un enemigo que se divide en clones cuando lo golpeamos Porque tengo una idea interesante de cómo usarlo en este proyecto en particular con este tipo de jugabilidad. 66. Variedad de enemigos: En esta clase, estamos usando hojas de sprites ligeramente más grandes con marcos 100 por 100 píxeles Puedes usar los sprites que ya tienes si seguiste a las otras clases, tienen fotogramas 80 por 80 píxeles En la versión extendida de este proyecto, también incluiré exportaciones de todos los tipos de enemigos en tamaño de fotograma 100 veces 100 para que coincida mejor con este proyecto. Si quieres jugar con esta base de código más tarde e incluir también a los otros tipos de enemigos, puedes usar esas hojas de sprites más grandes Incluí mi imagen en el índice HTML y la hice referencia aquí. El último fotograma será de 14. De nuevo, el método de inicio primero activará el inicio en la clase enemiga padre. Velocidad horizontal x cero. Velocidad y será un valor aleatorio entre 0.2 y 0.7 Este enemigo tendrá tres vidas, así que necesito asegurarme de que sea un poco más lento para darle al jugador el tiempo suficiente para lidiar con ello. Mi plan es que este enemigo tenga tres vidas. Le pegamos una vez. Los frames jugarán hasta aquí, lo volvemos a golpear. Los frames se jugarán hasta este punto que lleguemos tercera vez y el resto de la hoja de cálculo se jugará Esto se implementaría mejor usando el patrón de diseño de estado, cuando cada uno de estos estados tiene su propio bloque de código. Pero voy a tratar de implementarlo de alguna manera una manera sencilla como lo hicimos con el escarabajo morph Entonces podemos compararlo con el último enemigo, donde en realidad te muestro cómo usar el patrón de diseño estatal. Se puede comparar la complejidad relativa entre estos dos enfoques y verá las ventajas del enfoque final. El método Update primero ejecutará todo el código dentro del método de actualización en la superclase padre Entonces, si este objeto enemigo está actualmente activo, si el enemigo está vivo, lo haremos reaccionar al ser golpeado por el ratón. Tendremos un rango de fotogramas horizontal definido por las variables de ayuda de fotograma min y max frame. Si el fotograma X es menor que el fotograma máximo, si la actualización de sprite es verdadera, aumentamos el fotograma X en uno Bien, lo que estoy haciendo aquí, marco Y maneja la navegación vertical de sprites El marco X maneja la navegación horizontal del sprite. El último fotograma es el último marco horizontal. Una vez que se mostró, sabemos que el enemigo puede ser desactivado Mint frame y Max frame definirán algún rango de animación con el que estamos trabajando actualmente. Dependiendo en qué estado se encuentre actualmente el enemigo. Hay muchas maneras en las que puedo implementar esto. Quiero hacerlo de la manera más sencilla posible. Lo vamos a hacer si más si las vidas enemigas son más o iguales a tres. Max frame es cero, si las vidas enemigas son dos. Max frame es tres Cuando golpeamos una vez al enemigo, debido a esta línea de código, se animará desde aquí y se detendrá aquí De lo contrario, si las vidas enemigas son una, dejaremos que la animación vaya hasta el máximo fotograma siete. Creo que eso podría ser suficiente porque tenemos algún otro código de animación dentro del método de actualización compartida que cuando el enemigo no está vivo y tiene menos de una vida, animará el resto de la hoja de cálculo, los fotogramas de explosión automáticamente Veamos aquí donde creo el charco enemigo. Quiero crear aleatoriamente tipos morph de escarabajo o morph de langosta Puedo, por ejemplo, rodar un número aleatorio 0-1 El número aleatorio es menor 0.8 En aproximadamente el 80% de los casos creamos un morph de langosta En el otro 20% de los casos creamos una morph de escarabajo. Bien, vienen ambos tipos de enemigos. Hago clic al enemigo una, dos veces, tres veces. Todo eso funcionó perfecto. Ajusto el intervalo de sprites para hacer la animación un poco más rápida Este bloque de código está funcionando bien, pero cuando escribes tu código, siempre piensa en qué código se está ejecutando una vez y qué código se ejecuta una y otra vez. Este código se está ejecutando todo el tiempo y estoy configurando max frame al mismo valor 60 veces por segundo, una y otra vez. Entonces, ¿y si quiero tocar un sonido en particular cuando el enemigo se transforma en la siguiente etapa Realmente no tengo un buen lugar para poner ese código aquí, ya que este bloque de código se está ejecutando una y otra vez. Entonces ese sonido se reproduciría una y otra vez si lo llamo aquí. Idealmente, necesito algún bloque de código que se ejecute solo una vez para reproducir ese sonido para mí. Resolveremos todos estos temas en un minuto. Primero, usemos hojas de cálculo para los miembros de la tripulación. 67. L18 Equipo espacial aleatorio: Ahora mismo, todos los miembros de nuestra tripulación tienen el mismo aspecto. Vamos a darles un poco más de personalidad. Un equipo intergaláctico de diferentes tipos de tripulantes humanoides y robóticos Todos los activos de arte están disponibles para descargar en la sección de recursos. Abajo, traigo sprite de tripulación al proyecto, le doy alguna identificación y señalo imagen de la tripulación a esta nueva hoja de cálculo. ¿Cómo hacemos esto? De hecho, tendré que mantener las coordenadas aleatorias a estos marcos en alguna parte. Tal vez aquí dentro de la propiedad de los miembros de la tripulación, lo puse igual a una matriz vacía. Yo creo un método helper. Yo llamo generar tripulación adentro. Pongo a los miembros de la tripulación a una matriz vacía, borrando todo lo que hay ahí en caso de que no esté vacío. Cuando llamamos a este método, entonces corro for loop. La vida de cada jugador tendrá asignada una imagen aleatoria de miembro de la tripulación. Cada miembro de la tripulación será un objeto simple con el marco X y la propiedad del marco Y. El fotograma X será un número aleatorio 0-5 y usamos el método floor para redondearlo al entero inferior más cercano Será cero o uno o dos, o tres, o 44 fotograma Y. Cada vez que empujemos a un nuevo miembro de la tripulación, obtendrá algunas coordenadas aleatorias en algún lugar de la hoja de cálculo Llamaremos a este método cada vez que iniciemos o reiniciemos el juego. Después establecemos el valor de dist vidas. Eso es importante aquí en el texto de estado de fila, defino con y altura de un solo cuadro en la hoja de cálculo de los miembros de la tripulación Necesito agregar cuatro argumentos más para definir el área de recorte. Por ejemplo, puedo recortar el marco superior izquierdo del Corint 00 a ancho y alto También usaré con y esconderé aquí en el ancho de destino y los argumentos de destino para definir el tamaño al que se dibujarán sobre lienzo. Siempre tenemos que comenzar nuestro juego para ver las vidas presionando Enter letra R en el teclado o haciendo clic o tocando este botón R. Bien, Ahora usemos coordenadas de marco de la matriz de miembros de la tripulación. Creamos esa propiedad aquí en la línea 100 A medida que hacemos un ciclo en este cuatro bucle 15 veces, porque tenemos un total de 15 vidas, usamos el índice de ese bucle de cuatro, accedemos al elemento correspondiente dentro de la matriz de miembros de la tripulación. Y accedemos a esas coordenadas frame x e frame y. Los multiplicamos por el ancho y por la altura de un solo marco para recorrer correctamente la hoja de cálculo Ahora cada vez que inicio o reinicio un juego, generamos un conjunto de especies alienígenas humanoides y robóticas Espero que ponga un poco más de presión sobre el jugador para tratar asegurarse de que ninguno de nuestros miembros únicos de la tripulación comido por los alienígenas. Este valor aquí define el margen izquierdo. Este valor aquí es el espaciado entre las imágenes. Tenemos un tipo de enemigo simple y un tipo enemigo más complejo implementado de la manera más sencilla que se me ocurra. Tenemos un grupo aleatorio de tripulantes y necesitamos protegerlos de comer extraterrestres Si estás viendo la versión extendida, hay un código fuente disponible para hacer desde esta etapa del proyecto. Si tienes problemas con algo, no dudes en usarlo y comparar tu código con el mío. Si el espaciado aquí es un poco menor que el ancho de un solo marco, los conseguiremos atascados uno detrás del otro. Puedes jugar con estos valores y usar lo que te quede bien. 68. Gestión del estado en los juegos: Ahora es el momento de aprender a usar el patrón de diseño de estado para implementar un tipo enemigo de fase que solo puede ser golpeado cuando está en la misma dimensión que el jugador, cuando está en estado sólido Si golpeas al enemigo cuando está fase cuando está en el estado invisible, lo impulsará y lo acelerará, haciéndolo aún más peligroso También adjuntaremos sonidos a diferentes fases y acciones. Porque es importante que nuestros juegos den comentarios a los jugadores cuando haces clic o interactúas con algo. Daremos retroalimentación visual animando a los enemigos. También obtendremos comentarios de audio al reproducir un sonido específico. Pequeño dólar para arreglar. Solo quiero reducir la vida de los jugadores cuando el juego terminado es falso. Así, traigo una hoja de cálculo fantasma morph al proyecto y le doy una identificación Aquí tenemos la clase de enema compartida con todos los métodos y propiedades compartidos. Lo estamos extendiendo en un simple eneme de morph de escarabajo y un enema de morph de langosta más complejo Ahora lo extenderemos al tipo de enema de morph fantasma más complejo Me estaba divirtiendo mucho con esto y si estás viendo la clase extendida, volví a los proyectos de Space Invaders y Planet Defense y agregué una lección extra a cada uno Así como el código fuente completo donde implemento este tipo de enemigo Phantom Morph phasing en ambos proyectos También puedes simplemente tomar la hoja de cálculo que te estoy dando aquí e intentar implementarla tú mismo si sigues esas clases Pero si necesitas mi ayuda, esas lecciones están ahí para ti. Phantom Morph tendrá el último fotograma 14, algo de velocidad x y velocidad Y. Los cambiaremos en un minuto Sólo tendrá una vida, pero no será tan fácil. Porque solo puedes golpearlo cuando no está faseando cuando es sólido Si lo golpeas en el momento equivocado, accidentalmente puedes darle un impulso y hacer que uno de nuestros miembros de la tripulación sea comido. Y probablemente no va a ser bonito porque si escuchas los sonidos que van a sonar. Cuando eso suceda, voy al archivo GS principal aquí dentro de crear piscina enemiga y sólo voy a crear morfos fantasmas Tengo que tener cuidado con los corchetes. Yo guardo cambios en todos mis archivos y aquí vamos. Voy a copiar el método de actualización del bloque de código Beetle Morph y lo uso aquí Ahora cuando le golpee una vez, solo jugará el resto de la hoja de cálculo. Como era de esperar, la velocidad vertical será un valor aleatorio entre 0.2 y 0.7 también quiero que se muevan hacia la izquierda y hacia la derecha. La velocidad horizontal será un valor aleatorio entre menos uno y más uno. Queremos asegurarnos de que no puedan moverse fuera de la pantalla, así que los haré rebotar de izquierda a derecha si la coordenada x horizontal del enemigo es menor o igual a cero. O si el borde derecho del rectángulo enemigo está tocando el borde derecho del área de juego visible. En ambos casos, simplemente voltear la velocidad horizontal a su valor opuesto. Haciendo rebotar al enemigo, moverse en el otro, en sentido contrario. Perfecto que funcionó. Los otros tipos de enemigos eran estáticos, siempre mostrando un solo fotograma de animación. A menos que estuvieran animando a otro estado. Este tipo de enemigo será diferente. Quiero que siempre se animen en un cierto rango de fotogramas. Inicialmente en la trama la trama mínima será cero y la trama máxima será de dos. Le doy a este tipo enemigo su propio método especial que manejará fotogramas de animación. Para mí, si la actualización de spray es verdadera, movemos marco horizontal. Si el cuadro X es menor que el cuadro máximo, seguimos aumentando el cuadro X, alcanzamos el cuadro máximo, lo configuramos de nuevo a cuadro mínimo para que el ciclo pueda continuar. Voy a llamar a manejar frames desde dentro actualizar aquí. Bonito. Este será el rango de fotogramas para el estado volador básico. Intento de tres a cinco probablemente. Ese será el rango de animación del estado de fase. Bien, si eres principiante, ahora viene la parte complicada. Vamos a implementar un patrón de diseño de estado que nos permitirá definir las propiedades y comportamientos de este enemigo para cada estado por separado en su propio bloque de código. 69. Patrón de diseño de estado: Creo una clase completamente separada llamada constructor Estado enemigo esperará juego y enemigo como argumentos. Los necesitaremos para poder apuntar y acceder a nuestro objeto principal de juego y también al objeto enemigo particular que estará en estos diferentes estados. Entonces definiremos cada estado en su propia subclase. Todas estas tres subclases extenderán la principal clase estatal enemiga Aquí almacenaremos las propiedades compartidas dentro de cada uno de estos bloques de código. Vamos a definir propiedades, valores y comportamientos que queremos que tenga el enemigo. Sólo mientras esté en estos estados particulares. Nos da mucha libertad y flexibilidad. Y hace que nuestro código sea más fácil de depurar. Porque si obtenemos un error y sabemos en qué estado estaba el objeto, sabemos en qué parte del código buscar para solucionarlo. El único reto para los principiantes será hacer un seguimiento de cómo todos estos objetos conectan y se comunican entre sí. Si eres principiante, la siguiente parte será un reto, así que no te preocupes demasiado por ello. Esta es una de esas técnicas que tienes que usar algunas veces para crear un mapa mental. Ayuda si usas esto para algunos proyectos diferentes. Se vuelve más fácil navegar eventualmente a medida que te acostumbras a él. Todo lo que necesitas entender ahora es que tenemos una clase estatal enemiga separada. Lo estamos extendiendo en tres subclases estatales, volando, faseando e implosionando Cada una de estas clases tendrá su propio método de inicio, donde definimos, por ejemplo, el rango de fotogramas para la animación mientras el enemigo está en ese estado. También podemos tocar un sonido específico desde aquí. Cuando el enemigo entre en ese estado, cada estado también tendrá su propio método de actualización que primero llamará al método de actualización compartida en la clase enemiga principal. Entonces agregará un poco de lógica única que sólo se aplicará cuando el enemigo se encuentre en ese estado particular. Por ejemplo, tal vez solo queremos verificar la detección de colisiones cuando el enemigo está volando. Cuando el enemigo está escalonando, no puede ser golpeado, por lo que no ejecutaremos el método check collision desde ahí. Cosas así. Cada objeto enemigo tendrá una matriz de todos los estados posibles en los que pueda estar dentro. Instanciamos las tres subclases estatales así. Aquí en la línea 174, puedo ver constructor de estado enemigo espera argumentos de juego y enemigo S. Le paso esta referencia de juego. A lo largo de esta referencia, podremos ver la puntuación de vidas del jugador o juego sobre estado, todas las cosas que podamos necesitar que se sientan en el objeto principal del juego. Entonces le pasaré esta palabra clave que representa a este objeto enemigo en particular. Esta palabra clave utilizada aquí en este bloque de corte significa el enemigo. A través de esta segunda referencia, podremos acceder a todos los métodos y propiedades que se sientan en el objeto enemigo, en la clase enemiga. Aquí es probablemente donde se está complicando un poco más para los principiantes, solo sigue adelante. Voy a tratar de guiarte ahora. Phantom Morph tendrá una propiedad estatal actual que estará apuntando hacia diferentes estados que tenemos aquí dentro de esta matriz de estados A través de esta conexión, podremos colocar al enemigo en un estado diferente. El método de estado de conjunto personalizado esperará estado como argumento. Este estado será un número que representa el índice. En estos estados la matriz de 93090 será estado volador porque esta es una matriz y las matrices comienzan desde el índice de 01 será el estado de fase y dos serán estado implosionante Cuando llamamos a set state, tomamos esta propiedad de estado actual de punto de 940 y apuntamos hacia uno de los objetos dentro esta matriz de estados de punto cero, o uno o dos. En ese punto, este estado actual de punto contiene una instancia completa de digamos clase voladora. Le dimos a esa clase su propio método de inicio. Dije y estado a clase voladora y llamo a su inicio asociado. Definimos ese método aquí en la línea 186. Y el trabajo de este método es establecer propiedades en el eneme morph fantasma a los valores que necesiten ser Cuando el tipo enemigo Phantommorph entra en este estado, necesitamos asegurarnos de que cada subclase estatal tenga Tenemos múltiples capas aquí. Ahora cuando llamamos a start on the Phantom morph enemies type para activarlo desde el pool de objetos, primero ejecutamos el código que se comparte para todos los tipos de enemigos Entonces ejecutamos el código que es específico sólo para el tipo de enemigo fantasma. Entonces llamamos método set state. Establecimos el estado actual una instancia de subclase de estado volador A partir de ahí llamamos método de inicio en el estado volador que establecerá propiedades a ese estado de transformación fantasma particular Será lo mismo dentro del método de actualización cuando llamemos a actualización en la clase enemiga Phantom Moorph Primero llamamos a todo el código dentro del método de actualización en la clase enemiga padre compartida. Después agregamos algún código específico solo al tipo de enemigo fantasma morph Encima de eso activamos ese último bit de código específico solo para el estado actual enemigo fantasma en el que se encuentra Dependiendo de a qué bloque de código esté apuntando el estado actual en este momento. Podría ser este bloque de código de 191 o este bloque de código de la línea 200, o este de la línea 209. Ahora tenemos todas las capas y puedes colocar tu lógica donde sea que la necesites. ¿Tienes una idea para un comportamiento que es específico solo de uno de estos estados? Creamos un espacio para que introduzcas ese código. Te voy a mostrar algunos ejemplos de cómo se hace esto. Este es todo el bucle lógico de diseño de estado. No estoy seguro si lo expliqué lo suficientemente bien. Si aún no lo entiendes completamente, déjame expandir cada bloque de código y ver qué pasa. Ojalá, mientras trabajamos con esto, quede más claro. Esta estructura de código, tenemos un control completo y detallado sobre nuestros objetos. Podemos hacer tantas cosas diferentes. Básicamente, podemos hacer lo que queramos con nuestras criaturas espaciales. Ahora podemos acceder a propiedades sobre el enemigo a través de esta referencia enemiga. Cuando ingresamos al estado volador, establecemos el marco mínimo en cero y el cuadro máximo. Esto significa que puedo eliminar esos valores de aquí porque se activarán cuando esta línea se ejecute de todos modos. Cuando ingresamos al estado de orientación, estableceremos el cuadro mínimo tres y el cuadro máximo en cinco cuando ingresemos al estado de implosión en el cuadro será seis y el cuadro máximo será igual al último fotograma más uno A lo mejor cuando activamos al enemigo se establece en estado cero estado volador. Este método de inicio establece el rango de fotogramas como acabamos de definir. También podemos hacer que comiencen desde el estado de fase. Puedo ver que parten del fotograma X cero. De hecho quiero que comiencen desde lo que sea que el marco min esté en ese rango. Puedo decir sdtenemetframex es igual a didot enemigo en marco hago lo Ahora son inmediatamente invisibles. Comienzan desde el rango de cuadro correcto. Cuando entran a un estado en particular, quiero que sean al azar, comenzando ya sea por volar o desde el estado de fase Quiero cero o uno aquí. Matemáticas en tiempos aleatorios dos envueltos en piso de Mathot lo harán por nosotros Ahora conseguimos que algunos enemigos empiecen invisibles, algunos comienzan invisibles. Quiero que los enemigos cambien automáticamente entre el estado volador y el estado de fase Ya usamos eventos periódicos para activar al enemigo cada 1 segundo, y también para permitir que las hojas de sprites actualicen cada 120 milisegundos Podemos usar la misma técnica para esto. Cada enemigo tendrá su propio interruptor, temporizador e intervalo de conmutación Los pongo en cada objeto eneme. Cada enemigo que tenga sus propios temporizadores nos permitirá, por ejemplo, darle a cada enemigo un intervalo diferente Si quisiera que todos los enemigos cambiaran de estado al mismo tiempo por razones de rendimiento, tendría sentido poner esta lógica en la clase principal del juego. Por ahora, quiero que cada eneme cambie de estado cada 2 segundos, cada 2000 milisegundos Randomizaremos este valor en un minuto. Cuando entreguemos este trabajo, puedo poner esa lógica aquí. El enemigo está vivo. Haremos que cambie periódicamente entre estado volador y fase Si el temporizador del interruptor es menor que el intervalo del interruptor, siga aumentando el temporizador del interruptor en el tiempo delta, de lo contrario, es decir, si el temporizador del interruptor acumuló suficientes milisegundos, ajuste el temporizador del interruptor de nuevo a cero para que pueda contar nuevamente para el siguiente evento periódico Y llamar al método de conmutación personalizado, que vamos a definir. Ahora el método switch simplemente comprobará si este estado actual de punto de 940 es igual a este índice de estados de punto cero que es el estado volador como lo definimos aquí en la línea 139, llamamos set state y lo pasamos index one phasing que reasignará el estado actual a Se llamará inicio para ajustar el rango de fotogramas y así sucesivamente. El índice uno está aquí. Podría haber creado un objeto Enum traduciendo 01.2 índices en algo más legible, pero creo que como solo tenemos tres estados, podemos trabajar Solo tenemos que recordar, el índice cero está volando, el índice uno está escalonando, el índice dos está implosionando else, es decir, el estado actual es la fase índice dos está implosionando else, es decir, el estado actual es la Establecemos el estado dos para activar el estado implosionante. Sólo para ver qué pasa, estamos usando el tiempo delta. Aquí lo estamos calculando en la línea 215 dentro de GS principal. Lo estamos pasando al método render, luego lo estamos pasando para actualizar el método en objetos enemigos que va aquí dentro del enemigo GS. Está disponible para el método principal de actualización de la clase enemiga, pero realmente no lo necesitamos aquí. Podemos simplemente ir directamente a la clase Phantom Morph y acceder a delta time aquí Entonces estará disponible para nuestra lógica de conmutación de estado. Estamos cambiando de estado entre volar e implosionar, que resulta en esto De hecho, queremos cambiar entre volar y hacer fases. Eso es mejor. Puedo hacer al azar este intervalo para hacerlo más Quiero que el enemigo sea destruido cuando le hagamos clic. Sólo en estado volador. Si le pegamos cuando está haciendo fases, le daremos un impulso de velocidad Esto animará al jugador a cronometrar los clics correctamente. Aquí en la subclase Phantommorph, si el enemigo está vivo, si el enemigo está vivo, llamamos a este método que se asienta en De hecho voy a interceptar esto. Le daré su propio método de golpe que primero ejecutará todo el código dentro del método de golpe en la clase enemiga padre, aquí en la línea 34, para verificar la colisión entre el ratón y el objeto enemigo. Cuando golpeamos al enemigo pinchándolo con el ratón o golpeándolo con nuestros eventos táctiles, comprobaremos si el enemigo no está vivo. Lo cambiaremos al estado implosionante conjunto estado dos. Voy a cortar este bloque de código. Queremos llamar al método hit solo desde mientras estamos dentro del estado de fase o dentro del estado volador Voy a quitar el método hit del estado de fase. A lo mejor el enemigo no puede ser golpeado cuando está escalonando, cuando es invisible, sólo puede ser golpeado cuando está volando Puedo ver que estamos mostrando fotogramas de animación de explosión demasiado rápido. Voy a eliminar este método de marcos de manejo que ciclos entre min frame y max frame. Sólo lo llamaré mientras estemos en estado volador o en estado de fase cuando el enemigo implosiona Ya no necesitamos manejar fotogramas porque este bloque de código en la clase enemiga padre compartida se hará cargo. Y cuando el enemigo no está vivo, las vidas son menos de cero. Seguimos aumentando el fotograma X hasta llegar al último fotograma y luego restablecemos al enemigo. Bien, consigue que limpiemos eso. Aquí puedo hacer todo tipo de cosas. A lo mejor cuando hacemos clic en el enemigo, copio este bloque de código, lo pongo aquí dentro del estado de fase Si hacemos clic en el enemigo mientras estamos en estado de fase, dale un impulso de velocidad Lo empujamos verticalmente por 25 píxeles. A lo mejor pueda quitar este cheque de ratón despedido por esto. Estamos dentro de la subclase estatal tengo que decir este anime aquí Si hacemos clic en un enemigo mientras está haciendo fases, lo hacemos saltar hacia adelante en lugar de destruirlo. Si hacemos clic cuando está en estado volador, lo cambiamos al estado de implosión. Y cuando los fotogramas de animación de explosión terminan de reproducirse, obtenemos un punto de puntuación. Podría, por ejemplo, copiar estas dos líneas. Cada vez que entramos en estado volador, agrego enemigo aquí y aquí. Cada vez que entramos en estado de fase también aleatorizamos velocidad x y la velocidad Y nuevamente para asegurarnos de que el enemigo Si hacemos clic en el enemigo mientras estamos en estado de fase, hacemos que se mueva directamente hacia abajo velocidad X cero Y la velocidad Y será de dos píxeles por fotograma de animación. Cuando hacemos clic en el enemigo, cuando se supone que no debemos hacerlo, le estamos dando un impulso de velocidad haciendo que sea más probable que se coma a uno de nuestros miembros de la tripulación. Bien, tenemos una clase enemiga compartida. Lo estamos extendiendo a una subclase Phantom morph. Este tipo de enemigo puede estar en estado volador, gradual o implosionante Estamos cambiando entre estos estados al señalar propiedad del estado actual a diferentes subclases de estado Usando este método set state, estamos llamando a update en la subclase de estado activo actual desde aquí Con esta estructura de código, tenemos un bloque de código separado para cada estado. Y aquí podemos agregar cualquier comportamiento y propiedades al enemigo. Esto nos permite controlar cada detalle y mantiene nuestro código limpio y organizado. Le dimos a nuestro enemigo un estado volador donde animamos este rango de fotogramas y el enemigo puede ser golpeado y destruido Estado de fase donde animamos este rango de fotogramas. Enemigo obtendrá un impulso si le pegamos en este estado e implosionando Lo que simplemente animará la destrucción del enemigo. La forma en que estructuré mi código, necesitamos mantener aquí este método de actualización o nuestro código se rompería si no lo quieres aquí, porque no lo estamos usando para nada en este momento. También puedes ponerlo en la clase de estado enemigo padre, y la herencia de Javascript asegurará que el código siga funcionando. Estructurar nuestro código de esta manera tiene muchas ventajas. Preparé un conjunto de efectos de sonido para este juego. Déjame mostrarte lo fácil que es implementarlo ahora. 70. Sonidos L21: Puedes descargar mi set de sonidos de terror en la sección de recursos a continuación. Los llevaremos al proyecto. Aquí dentro de los activos rígidos, creo un elemento de audio HTML y apunté hacia mi nuevo juego P tres archivo, y le doy una identificación de nuevo juego. Uno más. Tomemos boom uno P tres ID será boom uno. Aquí arriba en GS principal, creo una clase llamada Audio Control Constructor Solo tendrá una propiedad para cada sonido apuntando al elemento de audio usando su ID. Creo una instancia de esta clase, lo que los sonidos estarán disponibles desde disdotoundpperty sentado Cuando comenzamos el juego, simplemente puedo tomar didotundegame y llamo método de juego integrado La mayoría de los navegadores modernos no reproducirán audio automáticamente. Cuando una página web se carga en nuestro proyecto, el usuario tiene que presionar Enter R o hacer clic en este botón R para iniciar el juego. Eso se considera una acción del usuario, y permitirá que se reproduzca el audio del juego. Tenemos otro sonido llamado Boom One. Aquí voy a los NEMG aquí abajo dentro del estado implosionante. Cuando el enemigo entra en este estado y se ejecuta el método de inicio, ya podemos reproducirlo, notarás un problema cuando rápidamente destruya múltiples enemigos porque estamos accediendo al mismo elemento de audio. El sonido no comenzará a reproducirse hasta que termine el bucle anterior. Quiero que suene el sonido cada vez que destruimos a un enemigo. Podríamos crear múltiples instancias de ese sonido, lo que no sería amigable para el rendimiento. Creo que aquí puedo usar API de audio web, lo que nos da mucho mejor control sobre el sonido. O la solución más simple aquí es simplemente rebobinar el sonido al principio cada vez que se supone que debe tocar Puede sonar extraño, pero en realidad va a crear el efecto que queremos. Déjame mostrarte. Yo creo un método de juego personalizado aquí. Este método tomará como argumento el audio , que será uno de estos. Lo rebobinará al principio estableciendo su propiedad de tiempo actual en cero, y solo después de eso llamará automáticamente al método de reproducción incorporado Necesito llamar al método de reproducción personalizado así, y necesito pasarle el audio. Quiero rebobinar y jugar así. Ahora, incluso cuando rápidamente destruyo enemigos, el audio reacciona. Mejoraremos aún más este sonido. Pero antes de hacer eso, importo la diapositiva P tres. Le doy una idea de diapositiva. Quiero tener retroalimentación visual y de audio para cada acción importante que realice el enemigo. Lo traigo al proyecto aquí dentro del estado de fase Si hacemos clic en el enemigo mientras es invisible, mientras está escalonando, jugaremos la diapositiva Sonido Ahora, si los jugadores hacen clic en el enemigo cuando no deberían, mientras el enemigo está haciendo fases, le dará al enemigo un impulso de velocidad Y también obtendremos una confirmación de audio de que sucedió al escuchar la diapositiva. Sonido traigo más efectos boom, boom, 234. Y le doy ideas de boom, 234. Los traigo al proyecto aquí dentro de la clase de control de audio. Cuando destruimos a un enemigo, quiero tocar Boom two, una versión ligeramente diferente del sonido. Guardo los cambios en todos mis archivos, presiono Enter o son para iniciar el juego. Bien ahora, boom para jugar. Vamos a probar, Boom tres. Agregué algunos matices de limo blandos cuando mezclé este para hacerlo extra El boom cuatro tiene algunos ruidos de agrietamiento y aleteo ahí dentro. Permítanme describir a un enemigo. Quiero que uno de estos cuatro sonidos se reproduzca al azar. Yo creo una matriz llamada Boom Sounds, y los pongo ahí como elementos los cuatro. Cuando destruimos a un enemigo. El sonido que quiero tocar es uno de los índices en boom Sounds array. En lugar de codificar duro el índice uno, quiero un número aleatorio 0-3 Cuando haga esto, me dará un entero, ya sea cero, o uno, o dos, o tres Destruyo enemigos y los sonidos aleatorios están sonando perfectos. También tenemos un sonido de victoria cuando ganamos un juego, un sonido de terror aterrador cuando perdemos. Y tenemos un grito de horror cuando se come a uno de los miembros de nuestra tripulación Para averiguar dónde colocar el código que reproduce estos sonidos, necesitamos entender qué bloques de código se están ejecutando una y otra vez para cada fotograma de animación y qué bloques de código se ejecutan solo una vez. Quiero que el sonido perdedor se juegue solo una vez cuando termine el juego. Puedo hacerlo aquí dentro nuestro método personalizado de juego de disparo sobre. Si el jugador perdió todas las vidas, si se comieron a todos los tripulantes, jugamos a la perdida. Sonido Si el marcador es más que un puntaje ganador, jugamos el triunfo. Sonido Ve al GS enemigo aquí en la línea 57, dentro del método de actualización en clase enemiga. Si el enemigo se movió por toda la pantalla de arriba a abajo y no la destruimos, jugador perderá una vida. Un tripulante será comido, y nosotros tocaremos el grito Sonido Bien, puse la puntuación ganadora en tres para que podamos probar esto fácilmente. Tenemos 15 vidas aquí en la línea 108. 15 tripulantes. Cuando los enemigos lleguen al fondo, comenzarán a comerse. Es un poco extraño cuando múltiples enemigos llegan al fondo, The stream Sound se restablece desde el principio tal como lo diseñamos, pero no creo que funcione bien en este escenario en particular. Cuando perdemos la vida, nos perdemos. Sonido Eso funcionó. Reinicio el juego y maté a tres enemigos. Yo gano el juego. Y suena el sonido ganador. Tenemos un buen conjunto de sonidos y no solo tienen un efecto estético, también tienen un efecto de jugabilidad, dando al reproductor una confirmación de audio de que la interacción fue registrada y actuada. No quiero rebobinar el grito, así que solo quiero que vuelva a sonar cuando el grito anterior termine de tocarse por completo, voy a tocar Perdamos algunos miembros de la tripulación y veamos ahora cuando varios enemigos llegan al fondo, no conseguimos un sonido de grito por cada miembro de la tripulación perdido si se pierden demasiado rápido Pero creo que está limpio cuando no estamos reiniciando este sonido en particular hasta que esté terminado Ahora podría ser una buena idea ajustar tu puntaje ganador y jugar tus vidas para hacer el juego tan difícil o tan fácil como quieras que sea. Si lo estás probando en un dispositivo móvil, puedes dejar un comentario rápido y dejarme saber si te funcionó, o si tuviste algún problema en algunos teléfonos móviles específicos y qué navegador se utilizó. Recomiendo usar Google Chrome para esto. Si es posible, solo puedes dejar un comentario rápido. Por ejemplo, el navegador Galaxy S 22 Firefox, funciona, o algo así. Si haces eso y pruebas este juego en tu teléfono, me ayudaría a mejorar los siguientes tutoriales de juegos móviles. Podemos probar nuestro código y emular otros dispositivos en Google Chrome, pero la emulación podría no ser 100% precisa La única manera de estar absolutamente seguro es ejecutar el código en un dispositivo real. Este fue un proyecto grande, bien hecho, si lograste llegar hasta aquí.