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í.