Haz un juego de Tower Defense en AS3 Enemies and Basic AI

Hola desarrolladores de Flash, bienvenidos a la segunda parte de mi tutorial del juego de defensa de la torre. En la primera parte, desarrollamos el mecanismo básico de crear torretas y hacer que disparen hacia el punto de clic del mouse. ¡Pero para eso no son las torretas! En esta parte extenderemos el juego para incluir enemigos, inteligencia artificial básica (AI) en torretas y algunos elementos más del juego. Estás listo?


Vista previa del resultado final

Este es el juego que vamos a crear en este tutorial:

Haga clic en los círculos de color naranja para colocar torretas. Los círculos rojos son enemigos, y el número en cada uno representa sus puntos de golpe..


Paso 1: Resumen

En el tutorial anterior desarrollamos un juego que tenía marcadores de posición para las torretas. Podríamos desplegar torretas haciendo clic en esos marcadores de posición, y las torretas apuntaban al puntero del mouse y disparaban balas hacia el punto donde el usuario hacía clic..

Terminamos con un Principal Clase que tenía el bucle de juego y la lógica del juego. Aparte de eso tuvimos el Torreta clase que no tenía mucho, excepto el actualizar Función que hizo girar la torreta..


Paso 2: una clase de bala separada

Previamente creamos las balas en. Principal clase y adjunto un ENTER_FRAME oyente para moverlo. La bala no tenía suficientes propiedades antes para considerarla como una clase separada. Pero en un juego así, las balas pueden tener muchas variedades como la velocidad, el daño, etc., por lo que es una buena idea sacar el código de la viñeta y encapsularlo en un espacio separado. Bala clase. Vamos a hacerlo.

Crear una nueva clase llamada Bala, extendiendo el Duende clase. El código básico para esta clase debe ser:

 paquete import flash.display.Sprite; La clase pública Bullet extiende Sprite public function Bullet () 

A continuación ponemos el código para dibujar el gráfico de bala, tomado de Principal, en Bala. Como hicimos con el Torreta clase, creamos una función llamada dibujar en el Bala clase:

 función privada draw (): void var g: Graphics = this.graphics; g.beginFill (0xEEEEEE); g.drawCircle (0, 0, 5); g.endFill (); 

Y a esta función la llamamos Bala constructor:

 función pública Bullet () draw (); 

Ahora agregamos algunas propiedades a la bala. Agrega cuatro variables: velocidad, velocidad_x, rápido y dañar, antes de Bala constructor:

 velocidad de var privado: Número; private var speed_x: Number; private var speed_y: Number; daño de var público: int;

¿Cuáles son estas variables para?

  • velocidad: Esta variable almacena la velocidad de la bala..
  • velocidad_x y rápido: Estos almacenan los componentes x e y de la velocidad, respectivamente, de modo que el cálculo de la división de la velocidad en sus componentes no tiene que hacerse una y otra vez.
  • dañar: Esta es la cantidad de daño que la bala puede hacer a un enemigo. Mantenemos esta variable pública, ya que la necesitaremos en nuestro bucle de juego en el Principal clase.

Inicializamos estas variables en el constructor. Actualiza tu Bala constructor:

 Función pública Bullet (ángulo: Número) velocidad = 5; daño = 1; speed_x = Math.cos (angle * Math.PI / 180) * speed; speed_y = Math.sin (angle * Math.PI / 180) * speed; dibujar(); 

Observe la ángulo Variable que recibimos en el constructor. Esta es la dirección (en grados) en que se moverá la bala. Acabamos de romper el velocidad en sus componentes xey, y los almacena en caché para uso futuro.

Lo último que queda en el Bala clase es tener un actualizar Función que se llamará desde el bucle del juego para actualizar (mover) la bala. Agregue la siguiente función al final de la Bala clase:

 actualización de la función pública (): void x + = speed_x; y + = speed_y; 

¡Bingo! Hemos terminado con nuestro Bala clase.


Paso 3: Actualización de la clase principal

Movimos un montón de código de bala de Principal clase a su propio Bala clase, por lo que una gran cantidad de código permanece sin uso en Principal y mucho necesita ser actualizado.

Primero, borra el crearBullet () y moveBullet () funciones También quite el bullet_speed variable.

A continuación, vaya a la disparar Funciona y actualízala con el siguiente código:

 función privada de disparo (e: MouseEvent): void para cada (var torreta: Torreta en torretas) var new_bullet: Bullet = new Bullet (turret.rotation); new_bullet.x = turret.x + Math.cos (turret.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin (turret.rotation * Math.PI / 180) * 25; addChild (new_bullet); 

Ya no usamos el crearBullet función para crear bala en lugar de utilizar el Bala constructor y pasar la torreta de rotación para ello, que es la dirección del movimiento de la bala, por lo que no necesitamos almacenarla en la bala. rotación propiedad como hicimos antes. Además, no adjuntamos ningún oyente a la bala ya que la bala se actualizará desde dentro del bucle del juego a continuación.


Paso 4: Guardar las referencias de viñetas

Ahora que necesitamos actualizar las viñetas del bucle del juego, necesitamos una referencia de ellas para almacenarlas en algún lugar. La solución es la misma que para las torretas: crear una nueva Formación llamado balas y empujar las balas en él como se crean.

Primero declare una matriz justo debajo de la torretas declaración de matriz:

 var privado ghost_turret: Torreta; torr de var privado: Array = []; balas var privadas: Array = [];

Ahora para poblar esta matriz. Lo hacemos cada vez que creamos una nueva bala, así que, en el disparar función. Agregue lo siguiente justo antes de agregar la bala al escenario:

 var new_bullet: Bullet = new Bullet (turret.rotation); new_bullet.x = turret.x + Math.cos (turret.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin (turret.rotation * Math.PI / 180) * 25; bullets.push (new_bullet); addChild (new_bullet);

Paso 5: Actualizar las balas

Al igual que la forma en que actualizamos las torretas en el bucle del juego, también actualizaremos las balas. Pero esta vez, en lugar de usar un para cada bucle, usaremos un básico para lazo. Antes de esto, debemos agregar dos variables a la parte superior del bucle del juego, de modo que sepamos qué variables se usan dentro del bucle del juego y las podemos liberar para la recolección de basura.

 torreta var: torreta; var bullet: Bullet;

Continúa y agrega el siguiente código al final del ciclo de juego:

 para (var i: int = bullets.length - 1; i> = 0; i--) bullet = bullets [i]; si (! bullet) continúa; bullet.update (); 

Aquí atravesamos todas las balas en el escenario cada fotograma y llamamos a sus actualizar Función que les hace moverse. Tenga en cuenta aquí que iteramos el balas matriz en sentido inverso. ¿Por qué? Veremos esto por delante.

Ahora que tenemos un torreta variable declarada en el exterior ya, no necesitamos declararla nuevamente dentro del para cada lazo de torretas. Modificarlo a:

 para cada (torreta en las torretas) turret.update (); 

Finalmente agregamos la condición de verificación de límites; esto fue previamente en la bala ENTER_FRAME Pero ahora lo comprobamos en el bucle de juego:

 si (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (i, 1); bullet.parent.removeChild (bullet); continuar; 

Verificamos si la bala está fuera del límite de la etapa, y si es así, primero eliminamos su referencia de balas matriz utilizando el empalme función, y luego retire la bala del escenario y continúe con la siguiente iteración. Así es como debería verse tu bucle de juego:

 función privada gameLoop (e: Event): void var turret: Turret; var bullet: Bullet; para cada (torreta en las torretas) turret.update ();  para (var i: int = bullets.length - 1; i> = 0; i--) bullet = bullets [i]; si (! bullet) continúa; bullet.update (); 

Si ahora ejecuta el juego, debería tener la misma funcionalidad que en la Parte 1, con un código que es mucho más limpio y organizado..


Paso 6: Presentando al enemigo

Ahora agregamos uno de los elementos más importantes del juego: el enemigo. Lo primero es crear una nueva clase llamada Enemigo extendiendo el Duende clase:

 paquete import flash.display.Sprite; clase pública Enemy extiende Sprite public function Enemy () 

Ahora agregamos algunas propiedades a la clase. Agrégalos antes de tu Enemigo constructor:

 private var speed_x: Number; private var speed_y: Number;

Iniciamos estas variables en la Enemigo constructor:

 función pública Enemigo () speed_x = -1.5; speed_y = 0; 

A continuación creamos el dibujar y actualizar funciones para el Enemigo clase. Estos son muy similares a los de Bala. Agregue el siguiente código:

 función privada draw (): void var g: Graphics = this.graphics; g.beginFill (0xff3333); g.drawCircle (0, 0, 15); g.endFill ();  actualización de la función pública (): void x + = speed_x; y + = speed_y; 

Paso 7: Programación de los eventos del juego

En nuestro juego necesitamos tener muchos eventos que tengan lugar en ciertos momentos o repetidamente en ciertos intervalos. Tal tiempo puede lograrse usando un contador de tiempo. El contador es solo una variable que se incrementa a medida que pasa el tiempo en el juego. Lo importante aquí es cuándo y en qué cantidad incrementar el contador. Hay dos formas en que la sincronización se realiza generalmente en los juegos: basado en el tiempo y basado en el marco.

La diferencia es que la unidad de juego paso a paso se basa en tiempo real (es decir, el número de milisegundos transcurridos), pero en un juego basado en cuadros, la unidad de paso se basa en unidades de cuadros (es decir, el número de cuadros pasados).

Para nuestro juego vamos a utilizar un contador basado en cuadros. Tendremos un contador que iremos incrementando en uno en el bucle del juego, que se ejecuta en cada cuadro, y básicamente nos dará la cantidad de cuadros que han pasado desde que comenzó el juego. Continúe y declare una variable después de las otras declaraciones de variables en el Principal clase:

 var privado ghost_turret: Torreta; torr de var privado: Array = []; balas var privadas: Array = []; private var global_time: Number = 0;

Incrementamos esta variable en el bucle de juego en la parte superior:

 global_time ++;

Ahora, en base a este contador, podemos hacer cosas como crear enemigos, que haremos a continuación..


Paso 8: Vamos a crear algunos enemigos

Lo que queremos hacer ahora es crear enemigos en el campo después de cada dos segundos. Pero estamos tratando con marcos aquí, ¿recuerdas? Entonces, ¿después de cuántos marcos debemos crear enemigos? Bueno, nuestro juego está funcionando a 30 FPS, incrementando así el tiempo global Contador 30 veces por segundo. Un simple cálculo nos dice que 3 segundos = 90 cuadros.

Al final del bucle de juego agrega lo siguiente Si bloquear:

 if (global_time% 90 == 0) 

¿Sobre qué es esa condición? Utilizamos el operador de módulo (%), que da el resto de una división, por lo que global_time% 90 nos da el resto cuando tiempo global se divide por 90. Verificamos si el resto es. 0, ya que este solo será el caso cuando tiempo global es un múltiplo de 90 - es decir, la condición vuelve cierto cuando tiempo global es igual a 0, 90, 180 y así sucesivamente ... De esta manera, logramos un disparo en cada 90 cuadros o 3 segundos.

Antes de que creamos al enemigo, declara otra matriz llamada enemigos justo debajo de la torretas y balas formación. Esto será usado para almacenar referencias a enemigos en el escenario..

 var privado ghost_turret: Torreta; torr de var privado: Array = []; balas var privadas: Array = []; enemigos var privados: Array = []; private var global_time: Number = 0;

También declarar un enemigo variable en la parte superior del bucle de juego:

 global_time ++; torreta var: torreta; var bullet: Bullet; enemigo enemigo: enemigo;

Finalmente agregue el siguiente código dentro del Si bloque que creamos anteriormente:

 enemigo = nuevo enemigo (); enemigo.x = 410; enemy.y = 30 + Math.random () * 370; enemigos.push (enemigo); addChild (enemigo);

Aquí creamos un nuevo enemigo, lo colocamos al azar a la derecha del escenario, lo empujamos en el enemigos Arreglo y agrégalo al escenario..


Paso 9: Actualizar a los enemigos

Al igual que actualizamos las balas en el bucle del juego, actualizamos a los enemigos. Pon el siguiente código debajo de la torreta para cada lazo:

 para (var j: int = enemigos.longitud - 1; j> = 0; j--) enemigo = enemigos [j]; enemy.update (); si (enemigo.x < 0)  enemies.splice(j, 1); enemy.parent.removeChild(enemy); continue;  

Al igual que hicimos un control de límites para las balas, también verificamos si hay enemigos. Pero para los enemigos, simplemente verificamos si salieron del lado izquierdo del escenario, ya que solo se mueven de derecha a izquierda. Deberías ver enemigos que vienen de la derecha si corres el juego ahora..


Paso 10: Dale un poco de salud a los enemigos

Todo enemigo tiene algo de vida / salud y también la nuestra. También mostraremos la salud restante sobre los enemigos. Permite declarar algunas variables en el Enemigo clase para las cosas de la salud:

 private var health_txt: TextField; salud var privada: int; private var speed_x: Number; private var speed_y: Number;

Inicializamos el salud Variable en el constructor siguiente. Agregue lo siguiente a la Enemigo constructor:

 salud = 2;

Ahora inicializamos la variable de texto de salud para mostrar en el centro del enemigo. Lo hacemos en el dibujar función:

 health_txt = new TextField (); health_txt.height = 20; health_txt.width = 15; health_txt.textColor = 0xffffff; health_txt.x = -5; health_txt.y = -8; health_txt.text = health + ""; addChild (health_txt);

Todo lo que hacemos es crear una nueva. Campo de texto, establecer su color, posicionarlo y establecer su texto en el valor actual de salud Finalmente agregamos una función para actualizar la salud del enemigo:

 función pública updateHealth (cantidad: int): int salud + = cantidad; health_txt.text = health + ""; salud de retorno 

La función acepta un entero para agregar al estado, actualiza el texto del estado y devuelve el estado final. Llamaremos a esta función desde nuestro circuito de juego para actualizar la salud de cada enemigo y detectar si aún está vivo..


Paso 11: Disparar a los enemigos.

Primero modifiquemos nuestro disparar funcionar un poco Reemplace el existente disparar Funcionar con el siguiente:

 función privada disparar (torreta: Torreta, enemigo: Enemigo): void var angle: Número = Math.atan2 (enemy.y - turret.y, enemy.x - turret.x) / Math.PI * 180; turret.rotation = ángulo; var new_bullet: Bullet = new Bullet (angle); new_bullet.x = turret.x + Math.cos (turret.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin (turret.rotation * Math.PI / 180) * 25; bullets.push (new_bullet); addChild (new_bullet); 

los disparar La función ahora acepta dos parámetros. La primera es una referencia a una torreta que hará el disparo; El segundo es una referencia a un enemigo hacia el que disparará..

El nuevo código aquí es similar al presente en el Torreta clase actualizar función, pero en lugar de la posición del ratón ahora usamos las coordenadas del enemigo. Así que ahora puedes eliminar todo el código de la actualizar función de la Torreta clase.

Ahora, ¿cómo hacer que las torretas disparen a los enemigos? Bueno, la lógica es simple para nuestro juego. Hacemos que todas las torretas disparen al primer enemigo en el. enemigos formación. ¿Qué? Vamos a poner un código y luego tratar de entender. Sume las siguientes líneas al final de la para cada bucle utilizado para actualizar las torretas:

 para cada (torreta en las torretas) turret.update (); para cada (enemigo en enemigos) disparar (torreta, enemigo); descanso; 

Para cada torreta ahora lo actualizamos, luego iteramos el enemigos array, dispara al primer enemigo en el array y rompe el loop. Así que, esencialmente, cada torreta dispara al primer enemigo creado, ya que siempre está al principio de la matriz. Intenta correr el juego y deberías ver torretas disparando a los enemigos..

Pero espera, ¿en qué fluye esa corriente de bala? Parece que están disparando muy rápido. Veamos porque.


Paso 12: Las torretas están disparando demasiado rápido

Como sabemos, el bucle de juego se ejecuta en cada fotograma, es decir, 30 veces por segundo en nuestro caso, por lo que la declaración de disparo que agregamos en el paso anterior se llama a la velocidad de nuestro bucle de juego y, por lo tanto, vemos un flujo de balas. Parece que también necesitamos un mecanismo de tiempo dentro de las torretas. Cambiar a la Torreta Clase y agregue el siguiente código:

 private var local_time: Number = 0; privado var reload_time: int;
  1. hora local: Nuestro contador se llama hora local en contraste con el tiempo global en el Principal clase. Esto es por dos razones: primero, porque esta variable es local a la Torreta clase; segundo, porque no siempre avanza como nuestro tiempo global variable - se reiniciará muchas veces durante el curso del juego.
  2. tiempo de recarga: Este es el tiempo requerido por la torreta para volver a cargar después de disparar una bala. Básicamente es la diferencia de tiempo entre dos disparos de bala por una torreta. Recuerda que todas las unidades de tiempo en nuestro juego son en términos de marcos..

Incrementar el hora local variable en el actualizar funciona e inicializa el tiempo de recarga en el constructor:

 actualización de la función pública (): void local_time ++; 
 función pública Turret () reload_time = 30; dibujar(); 

A continuación agregue las siguientes dos funciones al final de la Torreta clase:

 función pública isReady (): Boolean return local_time> reload_time;  función pública reset (): void local_time = 0; 

está listo devuelve verdadero solo cuando el actual hora local es mayor que el tiempo de recarga, es decir, cuando la torreta se ha recargado. Y el Reiniciar función simplemente restablece la hora local Variable, para iniciarla recargando de nuevo..

Ahora de vuelta en el Principal clase, modifique el código de disparo en el bucle del juego que agregamos en el paso anterior al siguiente:

 para cada (torreta en las torretas) turret.update (); si (! turret.isReady ()) continúa; para cada (enemigo en enemigos) disparar (torreta, enemigo); turret.reset (); descanso; 

Así que si ahora la torreta no está lista (está listo() devoluciones falso), continuamos con la siguiente iteración del bucle de la torreta. Verás que las torretas se disparan en un intervalo de 30 cuadros o 1 segundo ahora. Guay!


Paso 13: Limita el rango de la torreta

Todavía algo no está bien. Las torretas disparan a los enemigos independientemente de la distancia entre ellos. Lo que falta aquí es el distancia de una torreta. Cada torreta debe tener su propio rango dentro del cual puede disparar a un enemigo. Agrega otra variable a la Torreta clase llamada distancia y configurarlo para 120 dentro del constructor:

 privado var reload_time: int; private var local_time: Number = 0; rango de var privado: int;
 función pública Turret () reload_time = 30; rango = 120; dibujar(); 

También agrega una función llamada canShoot al final de la clase:

 función pública canShoot (enemigo: enemigo): booleano var dx: Number = enemy.x - x; var dy: Number = enemy.y - y; si (Math.sqrt (dx * dx + dy * dy) <= range) return true; else return false; 

Cada torreta puede disparar a un enemigo solo cuando cumple con ciertos criterios; por ejemplo, puede dejar que la torreta dispare solo a enemigos rojos con menos de la mitad de su vida y no más de 30px de distancia. Toda esa lógica para determinar si la torreta es capaz de disparar o no a un enemigo entrará en el canShoot función, que devuelve cierto o falso segun la logica.

Nuestra lógica es simple. Si el enemigo está dentro del rango de retorno. cierto; de lo contrario devuelve falso. Entonces, cuando la distancia entre la torreta y el enemigo (Math.sqrt (dx * dx + dy * dy)) es menor o igual que distancia, vuelve cierto. Un poco más de modificación en la sección de disparos del bucle de juego:

 para cada (torreta en las torretas) turret.update (); si (! turret.isReady ()) continúa; para cada (enemigo en enemigos) if (turret.canShoot (enemigo)) disparar (torreta, enemigo); turret.reset (); descanso; 

Ahora solo si el enemigo está dentro del alcance de la torreta, la torreta disparará.


Paso 14: Detección de colisiones

Una parte muy importante de cada juego es la detección de colisiones. En nuestro juego el control de colisión se realiza entre balas y enemigos. Estaremos agregando el código de detección de colisión dentro de la para cada bucle que actualiza las balas en el bucle del juego.

La lógica es simple. Por cada bala atravesamos el enemigos Arreglar y comprobar si hay una colisión entre ellos. Si es así, eliminamos la bala, actualizamos la salud del enemigo y salimos del bucle para controlar a otros enemigos. Vamos a añadir algún código:

 para (i = bullets.length - 1; i> = 0; i--) bullet = bullets [i]; // si la viñeta no está definida, continúe con la siguiente iteración si (! bullet) continúa; bullet.update (); si (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (i, 1); bullet.parent.removeChild (bullet); continuar;  para (var k: int = enemigos.longitud - 1; k> = 0; k--) enemigo = enemigos [k]; if (bullet.hitTestObject (enemigo)) bullets.splice (i, 1); bullet.parent.removeChild (bullet); if (enemy.updateHealth (-1) == 0) enemigos.splice (k, 1); enemy.parent.removeChild (enemigo);  rotura; 

Utilizamos ActionScript's hitTestObject Función para comprobar la colisión entre la bala y el enemigo. Si se produce la colisión, la bala se elimina de la misma manera que cuando abandona el escenario. La salud del enemigo se actualiza utilizando la actualizarSalud método, al cual balaes dañar Se pasa propiedad. Si el actualizarSalud función devuelve un número entero menor o igual que 0, esto significa que el enemigo está muerto y lo eliminamos de la misma manera que la bala.

Y nuestra detección de colisiones está hecha.!


Paso 15: Por qué revertir los bucles "For"?

Recuerda que atravesamos los enemigos y las balas en sentido inverso en nuestro circuito de juego. Vamos a entender por qué. Supongamos que utilizamos un ascendente para lazo. Estamos en el índice i = 3 y eliminamos una bala de la matriz. En la eliminación del artículo en la posición 3, su espacio es llenado por el elemento luego en la posición 4. Así que ahora el artículo previamente en la posición 4 Me senté 3. Después de la iteración yo incrementos por 1 y se convierte en 4 y así el artículo en la posición 4 está chequeado.

Vaya, ¿ves lo que acaba de pasar? Acabamos de perder el artículo ahora en la posición 3 que cambió de lugar como resultado del empalme. Y así usamos un revés para bucle que elimina este problema. Puedes ver porque.


Paso 16: Mostrar el rango de la torreta

Vamos a agregar algunas cosas extra para que el juego se vea bien. Agregaremos funcionalidad para mostrar el rango de una torreta cuando el mouse se encuentre sobre ella. Cambiar a la Torreta clase y añadir algunas variables a ella:

 rango de var privado: int; privado var reload_time: int; private var local_time: Number = 0; cuerpo var privado: Sprite; private var range_circle: Sprite;

Siguiente actualice el dibujar función a lo siguiente:

 función privada draw (): void range_circle = new Sprite (); g = range_circle.graphics; g.beginFill (0x00D700); g.drawCircle (0, 0, rango); g.endFill (); range_circle.alpha = 0.2; range_circle.visible = false; addChild (range_circle); cuerpo = nuevo Sprite (); var g: Graphics = body.graphics; g.beginFill (0xD7D700); g.drawCircle (0, 0, 20); g.beginFill (0x800000); g.drawRect (0, -5, 25, 10); g.endFill (); addChild (cuerpo); 

Rompemos los gráficos de la torreta en dos partes: el cuerpo y el gráfico de rango. Hacemos esto para dar un orden a las diferentes partes de la torreta. Aquí requerimos la range_circle para estar detrás del cuerpo de la torreta, y así lo agregamos primero al escenario. Finalmente, agregamos dos escuchas de mouse para alternar el gráfico de rango:

 función privada onMouseOver (e: MouseEvent): void range_circle.visible = true;  función privada onMouseOut (e: MouseEvent): void range_circle.visible = false; 

Ahora adjunte los oyentes a los eventos respectivos al final del constructor:

 body.addEventListener (MouseEvent.MOUSE_OVER, onMouseOver); body.addEventListener (MouseEvent.MOUSE_OUT, onMouseOut);

Si ejecutas el juego y tratas de desplegar una torreta, verás un parpadeo al pasar sobre los marcadores de posición. Porqué es eso?


Ver el parpadeo?

Paso 17: Eliminar el parpadeo

Recuerda que configuramos el ratón habilitado propiedad de la torre del fantasma de falso? Hicimos eso porque, la torreta fantasma estaba capturando eventos del mouse al interponerse entre el mouse y el marcador de posición. La misma situación ha vuelto a aparecer, ya que la torreta tiene dos hijos ahora, su cuerpo y el sprite de rango, que capturan los eventos del mouse en medio.

La solución es la misma. Podemos establecer su individualidad. ratón habilitado propiedades para falso. Pero una mejor solución es configurar la torre del fantasma. ratón niños propiedad a falso. Lo que esto hace es restringir a todos los hijos de la torre fantasma de recibir eventos del mouse. Ordenado, ¿eh? Adelante, ponlo en falso en el Principal constructor:

 ghost_turret = torreta nueva (); ghost_turret.alpha = 0.5; ghost_turret.mouseEnabled = false; ghost_turret.mouseChildren = false; ghost_turret.visible = falso; addChild (ghost_turret);

Problema resuelto.

Paso 18: ¿Qué sigue??

Podríamos ampliar esta demostración para incluir características mucho más avanzadas y convertirla en un juego jugable. Algunos de los cuales podrían ser:

  1. Mejor lógica de inteligencia artificial para seleccionar y disparar a los enemigos..
  2. Diferentes tipos de torretas, balas y enemigos en el juego..
  3. Trayectorias complejas del enemigo en lugar de líneas rectas..

Veamos qué puedes obtener de esta demostración básica. Estaré encantado de escuchar sus juegos de defensa de la torre y sus comentarios o sugerencias para la serie..