Haz un juego inspirado en Megaman en Construct 2

Voy a guiarte a través de la creación de un juego de acción y plataformas inspirado en Megaman. Estaremos más enfocados en los aspectos de disparo del juego en lugar de la plataforma. En este tutorial usaré Construct 2 como la herramienta para hacer el juego, pero explicaré la lógica usando pseudocódigo para que puedas seguir este tutorial en cualquier idioma o motor de tu elección..

Artículos Relacionados
  • Crea un juego de mesa para "Tácticas pequeñas" para dos jugadores en la versión 2: Parte 1
  • Crea un juego inspirado en Bomberman en Construct 2: The Player and the Level
  • Haz un juego de Match 3 en Construct 2: The Basics
  • Errores comunes de los recién llegados a la comunidad de Construct 2 Gamedev

Haz clic en el juego para enfocarlo, luego usa las teclas de flecha para moverte y saltar, y Z para disparar.

Para centrarme en la implementación del juego, no explicaré todas las características de Construct 2; Voy a suponer que sabes lo básico, como cargar un sprite, colisión básica o reproducir sonidos. Con eso dicho, vamos a empezar a hacer el juego..


Preparar la obra de arte

Lo primero es lo primero, necesitamos tener sprites para nuestro juego. Afortunadamente, opengameart nos ha cubierto con su maravillosa colección de arte de juegos legales. Necesitamos cuatro conjuntos de sprites; Un héroe, un enemigo y fichas para plataformas..

  • Héroe (por Redshrike):
    http://opengameart.org/content/xeon-ultimate-smash-friends
  • Enemigo (también por Redshrike):
    http://opengameart.org/content/fighting-robot-for-ultimate-smash-friends
  • Azulejos (por robotalidad):
    http://opengameart.org/content/prototyping-2d-pixelart-tilesets

Para usarlos en Construct 2, recorte los sprites del héroe en marcos individuales utilizando GIMP.


Movimiento basico

Usaré el comportamiento de la plataforma de Construct 2 para el resto del tutorial, de modo que pueda centrarme en los disparos y en la parte de IA del juego, que fue el enfoque principal de este tutorial..


Construir el comportamiento de la plataforma 2.

Movimiento básico explicado

Si está trabajando en otro idioma, o desea implementar su propio movimiento básico de plataformas en lugar de utilizar el comportamiento incorporado. Solo necesita usar el código en esta sección si no va a usar el comportamiento incorporado de Construct 2.

Para empezar, debemos considerar tres formas en que nuestro héroe puede moverse; Caminando a la derecha, caminando a la izquierda, o saltando. Cada fotograma, actualizamos la simulación del juego..

número moveSpeed ​​= 50; función de actualización () moveHero (); 

Para actualizar el personaje del jugador, implementamos movimientos básicos como este:

La función moveHero () // jugador está presionando esta tecla hacia abajo si (keyDown (KEY_LEFT)) hero.x - = moveSpeed ​​* deltaTime; hero.animate ("walkLeft");  if (keyDown (KEY_RIGHT)) hero.x + = moveSpeed ​​* deltaTime; hero.animate ("walkRight");  if (keyDown (KEY_UP)) hero.jump (); hero.animate ("saltar");  // una tecla que simplemente no fue presionada si (keyReleased (KEY_LEFT)) hero.animate ("standLeft");  if (keyReleased (KEY_RIGHT)) hero.animate ("standRight"); 

Utilizo otra función para hacer saltos porque saltar no es solo una cuestión de cambiar el valor y, sino también de calcular la gravedad. También tendremos una función que escuchará si una tecla se acaba de liberar o no para devolver nuestra animación de héroe a una animación de pie..

Vamos a hablar de cómo hacer saltar al jugador. El héroe necesitará saber si está saltando actualmente o no, y también si está cayendo o no. Así que declararemos dos nuevas variables: isJumping y isFalling. Por defecto, ambos son falsos, lo que significa que el héroe se encuentra en una plataforma.

Para realizar un salto, primero debemos verificar si ambos valores son falsos o no, y luego hacer que isJump sea verdadero.

Función jump () if (! IsJumping &&! IsFalling) isJumping = True

Para que el héroe pueda saltar, necesitamos una variable llamada jumpPower y gravedad. El valor predeterminado de jumpPower es -20 y la gravedad es 1. La lógica es agregar el valor del poder de salto a la posición Y del héroe y agregar la gravedad al valor de poder de salto.

Hacemos esto cada tick. Tal vez esta no sea la física de gravedad más realista que existe, pero los juegos no necesitan ser realistas, solo necesitan ser creíbles, es por eso que algunos juegos tienen saltos super humanos y saltos dobles. El siguiente código pertenece a la función de actualización.

Si (isJumping || isFalling) hero.y + = hero.jumpPower; hero.jumpPower + = hero.gravity;  // eventualmente el poder de salto será mayor que cero, y eso significa que el héroe está cayendo si (hero.jumpPower> = 0) isJumping = False; isFalling = True;  // para detener la caída, haga algo cuando el héroe se superponga a la plataforma si (hero.isOverlapping (plataforma1)) // plataforma1 es la plataforma en la que nuestro héroe puede pisar // restablece la variable a su valor predeterminado isJumping = False; isFalling = False; hero.jumpPower = -20;  // y luego está la caída libre, cuando el jugador cae sobre el borde de una plataforma si (! hero.isOverlapping (platform1) && hero.jumpPower < 0 && !isJumping)  // !hero.isOverlapping(platform1) checks whether or not our hero is standing on a platform // and if jumpPower is less than zero and the player is not currently jumping, then that means // he's falling // setting these two values like this will make the player fall. hero.jumpPower = 0; isFalling = true; 

El comportamiento de la plataforma incorporada de Construct 2 replica el código de ejemplo anterior, que se brinda solo a los que trabajan en otro idioma..


Implementando el tiro

Ahora viene la parte de tiro del juego. En la serie Megaman hay tres tipos de tomas: tomas normales, tomas cargadas y tiros de energía de jefe..

Los disparos normales son auto explicativos. Los disparos cargados son disparos que se cargan primero antes de ser lanzados, estos disparos cargados vienen en dos tipos: medio cargado y completamente cargado. Estos ataques cargados son más fuertes que los tiros normales, con los completamente cargados se convierten en los más fuertes.

Los tiros de energía de jefe son tiros con poder que el jugador adquirió después de derrotar a cada jefe. El daño es el mismo que el normal pero tienen propiedades especiales que los disparos normales no tienen.

Ahora que conocemos el tipo de cada disparo, comencemos a hacerlos. Primero veamos la lógica detrás de cómo usamos cada disparo. Aquí asumimos que el botón Z en el teclado se usa para disparar un tiro. Implementaremos dos comportamientos diferentes:

  • Disparos normales: el jugador presiona z y luego lo libera inmediatamente (toque el botón). La bala se disparará una vez cada toque. La animación cambiará para disparar la animación antes de cambiar inmediatamente a la animación en pie.
  • Disparos cargados: el jugador presiona Z. Se disparará la primera bala normal. La animación cambiará para dispararse antes de cambiar inmediatamente a la animación de pie. Si se sigue presionando Z, se agregará un efecto de carga en la parte superior de la animación de reproducción (de pie, caminando). Si se suelta el botón Z en menos de 5 segundos desde la primera carga, se disparará una bala medio cargada. Si se suelta el botón Z después de 5 segundos, se disparará una bala completamente cargada.
  • Disparos de energía de jefe: nuestro héroe debe primero equipar la bala que adquirió después de derrotar a un jefe. Después de equipar, el jugador presionará otro botón para disparar esta bala. Este comportamiento de la bala varía y debe codificarse de forma única para cada bala.

Ahora, comencemos a codificar. Debido a que nuestro héroe puede disparar hacia la izquierda y hacia la derecha, necesitamos saber a qué dirección se enfrenta actualmente. Vamos a declarar una nueva variable llamada cara que almacena un valor de cadena de si el héroe se enfrenta a la izquierda o la derecha.

String facing = "right"; // en qué dirección está enfrentando el héroe actualmente la función moveHero () // player está presionando esta tecla hacia abajo si (keyDown (KEY_LEFT)) hero.x - = moveSpeed ​​* deltaTime; hero.animate ("walkLeft"); facing = "left";  if (keyDown (KEY_RIGHT)) hero.x + = moveSpeed ​​* deltaTime; hero.animate ("walkRight"); facing = "right";  // ... la continuación de la función moveHero () va aquí función update () // ... el código de actualización que escribimos anteriormente va aquí ... // jugador, presione esta tecla una vez si (tecla presionada (TECLA_Z)) si == "a la derecha") hero.animate ("Shoot"); // agregaremos la función de disparo aquí más adelante else if (ante == "left") hero.animate ("Shoot"); hero.mirrorSprite (); // esta función voltea el sprite horizontalmente if (keyReleased (KEY_Z)) if (facing == "right") hero.animate ("standRight");  else if (ante == "left") hero.animate ("standLeft"); hero.mirrorSprite (); // necesitamos volver a llamar a esto porque el sprite estaba duplicado // si no volvemos a reflejar el sprite, standLeft se verá como standRight

Mueve eventos en Construct 2

Antes de disparar una bala, debemos observar las propiedades que tiene la bala:

  • Poder: atacar el poder de la bala, el daño que causará al enemigo.
  • Velocidad: qué tan rápido va la bala
  • Ángulo: el ángulo de disparo, determina en qué dirección va la bala.

Estas propiedades serán diferentes para cada bala. En particular, la propiedad de poder será diferente. La propiedad de ángulo es normalmente solo uno de dos valores; si la bala se dispara hacia la derecha o hacia la izquierda, a menos que sea una bala de energía principal que pueda dispararse en un ángulo único.

Las variaciones de los disparos se tratarán más adelante, así que ahora solo cubriré los disparos básicos. La siguiente es la pieza de código que dispara una bala..

// primero, creamos una función que crea un nuevo lanzamiento de la función bullet (cadena pathToSprite, número bulletPower, número bulletSpeed, número bulletAngle) myBullet = new Bullet (pathToSprite); myBullet.power = bulletPower; // la clase u objeto de la bala tiene dos variables privadas que lo mueven de acuerdo con su ángulo // más explicación a estas dos líneas que necesitan más matemáticas, así que elijo no explicar // supongo que su motor de elección tiene una manera de mover un objeto de acuerdo con su ángulo ySpeed ​​= Math.sin (bulletAngle) * bulletSpeed; xSpeed ​​= Math.cos (bulletAngle) * bulletSpeed;  // esta es una función de la clase Bullet que se llama cada fotograma, esto mueve la viñeta de acuerdo con su función de ángulo moveBullet () x + = xSpeed ​​* deltaTime; y + = ySpeed ​​* deltaTime;  // y esta es la modificación de nuestra función de actualización update () update () // ... el código de actualización que escribimos anteriormente va aquí ... // jugador, presione esta tecla una vez si (tecla presionada (KEY_Z)) if ( cara == "derecha") hero.animate ("Shoot"); hero.shoot ("path / to / sprite.png", 10, 400, 0);  else if (ante == "left") hero.animate ("Shoot"); hero.mirrorSprite (); // esta función voltea el sprite horizontalmente hero.shoot ("path / to / sprite.png", 10, 400, 180); // el ángulo es 180 para que la bala salga a la izquierda //… la continuación del código de actualización va aquí ...

Añadiendo el comportamiento de bala a los sprites de bala.
El código de disparo donde generamos un nuevo objeto de bala.

Disparos cargados

Algunas balas pueden ser más poderosas que otras. Para crear un tiro cargado, necesitamos una variable llamada chargedTime, que incrementará cada segundo que el jugador mantenga presionada Z y regresará a cero cuando se dispare la bala. Los cambios al código de actualización son los siguientes:

// el jugador acaba de lanzar la tecla z si (keyReleased (KEY_Z)) if (chargedTime> 0 && chargedTime <= 5)  if (facing == "right")  hero.animate("Shoot"); hero.shoot("path/to/halfChargedBullet.png", 20, 400, 0); chargedTime = 0;  else if (facing == "left")  hero.animate("Shoot"); hero.mirrorSprite(); // this function flips the sprite horizontally hero.shoot("path/to/halfChargedBullet.png", 20, 400, 180); chargedTime = 0;   else if (chargedTime > 5) if (ante == "right") hero.animate ("Shoot"); hero.shoot ("path / to / fullChargedBullet.png", 40, 400, 0); chargedTime = 0;  else if (ante == "left") hero.animate ("Shoot"); hero.mirrorSprite (); // esta función voltea el sprite horizontalmente hero.shoot ("path / to / fullChargedBullet.png", 40, 400, 180); chargedTime = 0;  if (facing == "right") hero.animate ("standRight");  else if (ante == "left") hero.animate ("standLeft"); hero.mirrorSprite (); // necesitamos volver a llamar a esto porque el sprite estaba duplicado // si no duplicamos el sprite otra vez, standLeft se verá como standRight // el jugador está presionando esta tecla hacia abajo si (keyDown (KEY_Z)) // esta es la función que agrega el valor de tiempo de carga cada segundo // este bloque de código clave se ejecutará en cada fotograma, que es menos de un segundo // su motor de elección debe tener una manera de saber si un segundo ha pasado o no addChargedTime (); 

Nuestro personaje héroe se mueve a la izquierda, a la derecha y salta de acuerdo con nuestra información, y también dispara balas, ya sea normal, medio cargado o completamente cargado.


Implementando enemigos

Ahora tenemos un héroe controlable. Llamémoslo Xeon por simplicidad. Puede realizar algunos movimientos básicos como caminar, saltar y disparar. ¡Eso es genial! Pero ¿de qué sirve la capacidad de disparar sin algo a lo que disparar, verdad? Es por eso que esta vez vamos a hacer nuestro primer enemigo..

Diseñemos los atributos de nuestro enemigo antes de comenzar a codificarlo..

  • Salud: la cantidad de salud que tiene nuestro enemigo determina cuántos disparos (y qué tipo) se necesitan para destruirlo.

  • Poder: poder de ataque de Enemy, cuánto daño le hace a nuestro jugador.

  • ShotAngle: en qué dirección el enemigo dispara la bala, puede ser hacia la izquierda o hacia la derecha o en cualquier lugar que queramos..

Eso es más o menos lo que necesitamos para nuestro enemigo, ahora hagamos la clase / objeto enemigo.

La clase / objeto Enemigo es bastante similar a la clase / objeto del jugador, excepto que el enemigo no escucha la entrada del jugador. Por eso necesitamos reemplazar las partes donde el héroe escucha la entrada del jugador, la IA / lógica enemiga.


Ataque enemigo AI

Para empezar, manejemos la IA básica del enemigo. El enemigo disparará al jugador cuando lo vea..

Para determinar si el enemigo "ve" al jugador, necesitaremos definir una variable para el objeto enemigo llamado cara que es una cadena que almacena uno de dos valores, "izquierda" o "derecha".

El enemigo también necesita algún tipo de rango de visión, por lo que vamos a hacer otra variable llamada rango. Si el jugador está dentro de este rango, eso significa que el enemigo "ve" al jugador. El pseudocódigo es el siguiente:

función boolean checkSees () si (frente a == "izquierda" && hero.x> = enemy.x - range) return true;  if (ante == "right" && hero.x <= enemy.x + range)  return true;  return false; 

Función checkSees ()

Tal vez hayas notado algo en este pseudocódigo: no tiene en cuenta la posición y del héroe, por lo que el enemigo seguirá disparando al héroe incluso si están en plataformas con diferentes alturas..

Por ahora esto será suficiente, porque hacer un algoritmo de línea de vista está fuera del alcance de este tutorial. En su propio juego, es posible que desee agregar una tolerancia Y en la función anterior que verificará si la posición y del héroe se encuentra entre dos puntos que definen las alturas del enemigo.


Haciendo disparar a los enemigos

El pseudocódigo para disparos enemigos es el siguiente:

// puede estar en update () o en algún otro lugar donde se ejecute cada función de frame update () if (checkSees ()) shoot ("path / to / bulletSprite.png", enemyPower, 400, shotAngle); 

Como puedes ver, la función de disparar () del enemigo es similar a la del jugador. Toma la ruta del sprite, el poder de ataque, la velocidad de bala y el ángulo de disparo como parámetros.


Movimiento enemigo AI

¿Cuándo el enemigo cambia de cara izquierda a cara derecha? Para nuestro héroe, usamos la información del jugador para cambiar la dirección a la que se enfrenta nuestro héroe. Para nuestro enemigo, tenemos dos opciones: usar algún tipo de temporizador para cambiar la dirección de frente cada pocos segundos mientras el enemigo se queda quieto, o hacer que el enemigo camine a un lugar determinado y luego cambie su dirección de frente y luego camine a otro lugar para cambiar su orientación de nuevo.

Este segundo método se puede utilizar como una IA de patrullaje. Por supuesto, podemos hacer que el enemigo camine en una dirección y nunca retroceder..

El pseudocódigo para el primer método es el siguiente:

function switchingAI () // elapsedTime () es una función que cuenta cuántos segundos han pasado desde que se restableció su valor // Supongo que su motor de elección tiene este tipo de funcionalidad si (elapsedTime ()> 4.0) if (hacia == "izquierda") enfrenta = "derecha"; shotAngle = 0;  if (facing == "right") facing = "left"; shotAngle = 180;  enemy.mirrorSprite (); // también voltea el sprite resetTime (); // restablece el tiempo que cuenta en elapsedTime ()

Cambiando las funciones de la IA

Enemigo que patrulla la inteligencia artificial

Para realizar la IA de patrullaje, necesitamos hacer dos objetos invisibles que se encuentran al final de ambos caminos de la ruta de patrullaje del enemigo, y hacer que el enemigo se mueva de otra manera si choca con ellos.


Patrullando la inteligencia artificial

Ahora escribamos nuestro pseudocódigo para la IA que patrulla el enemigo:

function patrollingAI () if (facing == "right") walkRight (); // igual que el que está en el objeto / clase del jugador if (collidesWith (rightPatrolBorder)) facing = "left"; enemy.mirrorSprite ();  if (ante == "left") walkLeft (); if (collidesWith (leftPatrolBorder)) facing = "right"; enemy.mirrorSprite (); 

Después de esto, el enemigo patrullará entre dos puntos como queramos..

Para configurar qué IA utiliza el enemigo, agregaremos una variable más con un tipo de cuerda para nuestro enemigo: la IA enemiga. Esto determinará qué AI utilizaremos cada cuadro, así:

if (enemyAI == "switching") switchingAI ();  else if (enemyAI == "patrolling") patrollingAI (); 

Por supuesto, puedes agregar más tipo de enemigo AI si quieres..


Variación de disparo

Veamos cómo podemos hacer variaciones de tiro tanto para el jugador como para el enemigo. Estamos haciendo variaciones de disparo al cambiar dos cosas: el ángulo de disparo y el número de disparos de bala.

De esta manera podemos hacer un simple disparo de bala, o un disparo de bala de tres direcciones. Antes de hacer esto, vamos a hacer otra variable para el objeto / clase enemigo llamado shotAI, que es una cadena. Usaremos esto en nuestro checkSees () verificando si está bloqueado, donde el enemigo dispara. Los cambios a ese bloque de código serán así:

// puede estar en update () o en cualquier otro lugar donde se ejecute cada función de frame update () if (checkSees ()) if (shotAI == "simple") shoot ("path / to / bulletSprite.png", enemyPower , 400, shotAngle);  if (shotAI == "threeBullets") shootThreeBullets (); 

Por supuesto, el nombre de la IA y el tipo de disparo que dispararía el enemigo depende de usted, esto es solo un ejemplo..

Ahora, profundicemos en lo que está dentro de la función shootThreeBullets ().

Función shootThreeBullets () if (facing == "right") shoot ("path / to / bulletSprite.png", enemyPower, 400, 0); // esta bala va directamente al disparo derecho ("path / to / bulletSprite.png", enemyPower, 400, 330); // esto aumenta en disparos de 30 grados ("path / to / bulletSprite.png", enemyPower, 400, 30); // esto baja en 30 grados si (frente a == "izquierda") disparar ("path / to / bulletSprite.png", enemyPower, 400, 180); // esta bala va directamente al disparo izquierdo ("path / to / bulletSprite.png", enemyPower, 400, 210); // esto aumenta en disparos de 30 grados ("path / to / bulletSprite.png", enemyPower, 400, 150); // esto baja en 30 grados

Si no está seguro de por qué 0 va a la derecha y 180 va a la izquierda, es porque la dirección de 0 grados va directamente al lado derecho de la pantalla, 90 grados va a la parte inferior de la pantalla, y así sucesivamente hasta que golpea 360 grados. Una vez que sepa qué valor va a dónde, puede crear su propia variación de disparo..

También podemos hacer una variable shotAI para el jugador, pero prefiero que la llamemos choza seleccionada, porque nuestro jugador elegirá la bala en lugar de la programada desde el principio. T

La lógica en megaman es que cada vez que megaman derrota a un jefe, obtiene el poder de ese jefe como un nuevo disparo. Voy a tratar de recrear esa lógica. Para hacer esto, necesitamos una matriz que contenga las tomas del jugador, incluidas las tomas normales. El pseudocódigo es así:

var shotArr = ["normalShot", "boss1", "boss2"]; var shotIndex = 0; var selectedShot = "normalShot"; función update () // este es el bloque de código en la función de actualización del jugador donde el jugador dispara una bala // esta función cambia la bala que el jugador dispara changeBullet (); // jugador, presione esta tecla una vez si (tecla presionada (KEY_Z)) si (frente a == "derecha") hero.animato ("Disparar"); if (selectedShot == "normalShot") hero.shoot ("path / to / sprite.png", 10, 400, 0);  else if (selectedShot == "boss1") // agregar códigos para disparar el tipo de tiro que recibió el jugador después de derrotar al jefe 1 función changeBullet () // cambia shotIndex según el botón presionado si (tecla presionada (KEY_E)) shotIndex + = 1;  if (keyPressed (KEY_Q)) shotIndex - = 1;  // arregla shotIndex si está fuera de la longitud de la matriz if (shotIndex == shotArr.length) shotIndex = 0;  if (shotIndex < 0)  shotIndex = shotArr.length -- 1;  selectedShot = shotArr[shotIndex]; 

Necesitamos hacer un seguimiento de dos nuevas variables:


Cómo agregar una matriz en Construct 2

Presionaremos nuevos elementos a shotArr cuando el jugador derrote a un jefe..


Actualización de las balas de jugador

Al igual que el dispararThreeBullet () del enemigo, puedes ser creativo y crear tus propias variaciones de disparo. Ya que esta es la bala del héroe, démosle algo especial..

Hagamos un tipo de disparo para ser efectivo contra un jefe específico, para que cause más daño. Para hacer esto, crearemos una variable para el objeto de viñeta llamada strongAgainst que es otra variable de tipo de cadena que contiene el nombre del jefe contra el cual esta bala es efectiva. Agregaremos esta funcionalidad de daño más importante cuando discutamos la parte principal del juego..


Salud y muerte

Aquí es donde todas las variaciones de tiro que hacemos realmente comienzan a importar. Aquí es donde nuestro héroe daña y mata al enemigo, y al revés..

Para comenzar, hagamos una variable tanto para el héroe como para el objeto enemigo, llamada salud que es un int, y otra variable solo para el héroe llamado vidas. Echemos un vistazo al pseudocódigo:

if (bullet.collidesWith (héroe)) hero.health - = bullet.power; createExplosion (); // por ahora no tenemos un sprite de explosión, así que esto actuará como un recordatorio // comprueba si el héroe está muerto si (hero.health <= 0)  hero.lives -= 1; // decreases hero's total number of lives. destroyHero(); 

Haremos el mismo pseudocódigo para dañar a los enemigos, así:

if (bullet.collidesWith (enemigo)) enemy.health - = bullet.power; createExplosion ();  si (enemigo.Salud <= 0)  destroyEnemy(); 

Manejo de colisiones de balas entre jugador y enemigo.

La barra de salud GUI

Ahora, si lo dejo así, no sería interesante. Así que haré un sprite de rectángulo en la esquina superior izquierda de la pantalla que actúa como la barra de salud de nuestro héroe..

La longitud de esta barra de salud cambiará dependiendo de la salud actual de nuestro héroe. La fórmula para cambiar la longitud de la barra de salud es esta:

// esto está en la función de actualización healthBar.width = (hero.health / hero.maxHealth) * 100;

Necesitamos una variable más para nuestro héroe llamado maxHealth; El valor de salud completo de nuestro héroe. Por ahora, este valor no se puede cambiar, pero tal vez en el futuro podamos crear un elemento que incremente la cantidad de heroHealth del héroe..


Crea el mundo del juego

Ahora que hemos creado nuestras variaciones de héroe, enemigo y disparo, debemos crear múltiples niveles y jefes..

Tener niveles múltiples significa que en algún punto del juego el jugador llegará a uno o más puntos de control que cambian el juego del nivel 1-1 al nivel 1-2 al nivel 1-3 y así sucesivamente hasta que alcancen al jefe.

Cuando el jugador muere en algún lugar del nivel 1-2, no necesita volver a jugar desde el principio del nivel 1-1. ¿Cómo hago esto? Primero vamos a hacer el nivel, no voy a explicar mucho sobre el diseño de niveles, pero aquí está el nivel de ejemplo en Construct 2.


diseño de nivel de muestra

La imagen en la esquina superior izquierda es la capa HUD. Se desplazará, siguiendo al héroe cuando se juegue el juego..


Puertas y puestos de control

Un sprite al que debes prestar atención es el sprite verde en la parte superior derecha del nivel. Es el punto de control en este nivel cuando el héroe choca con él, transferiremos el juego al nivel 1-2..

Para manejar múltiples niveles necesitamos tres variables: currentLevel, levelName y nextLevel.

La variable currentLevel se crea en la clase / objeto hero. El levelName se crea en el objeto de la escena del juego (level) para cada nivel. La variable nextLevel se crea en el objeto sprite verde.

La lógica es la siguiente: cuando el héroe choca con el sprite verde (yo lo llamo greenDoor), cambiaremos nuestro nivel a la escena del juego en la que levelName es el mismo que la variable nextLevel. Después de que cambiemos el nivel, cambiaremos el valor de la variable currentLevel del héroe al mismo nivel que el nombre de la escena del juego. Aquí está el pseudocódigo:

// esto está dentro de la función de actualización del juego si (hero.collidesWith (greenDoor)) changeLevelTo (greenDoor.nextLevel); 

Cambia de nivel cuando tocamos la puerta verde.

Inicializando un nuevo nivel

Aquí está el pseudocódigo para tratar cuando el siguiente nivel está cargado y listo para jugar.

// esta es la función que se activa cuando se carga el nuevo nivel función onStart () hero.currentLevel = scene.LevelName; hero.x = startPos.x; hero.y = startPos.y; 

Las posiciones de inicio del jugador

Ahora que hemos cambiado a un nuevo nivel, explicaré el sprite naranja detrás de nuestro héroe en la imagen de diseño de nivel anterior. Ese sprite naranja es un objeto que llamo startPos. Se utiliza para marcar la posición inicial de cada nivel..

Nos referimos a este objeto cuando el héroe acaba de cambiar de nivel o muere, para que sepamos dónde engendrarlo..

Aquí está el pseudocódigo para manejar cuando el héroe muere:

// la función que se activa cuando se destruye el héroe Function onDestroyed () // revive hero si el héroe aún tiene vidas. Si (hero.lives> 0) var newHero = new Hero (); newHero.x = startPos.x; newHero.y = startPos.y; 

Ahora podemos tener múltiples niveles y también podemos reaparecer al héroe después de que muera..

Puedes crear tantos niveles como desees, o tal vez incluso crear dos objetos green Door en un nivel en el que uno de ellos vuelva al nivel 1-1 si el jugador resuelve un rompecabezas de manera incorrecta..


Batallas de jefes

Finalmente es hora de implementar el propio jefe. Hacer un nivel de jefe es tan simple como hacer otro nivel que genere un jefe en lugar de enemigos regulares.

La parte difícil es crear AI para el jefe porque cada jefe tendrá una AI única. Por lo tanto, para que sea fácil de entender y duplicar para muchos jefes, voy a hacer que el jefe de la IA sea dependiente del tiempo después de su creación. Lo que significa que van a hacer A durante x segundos, luego cambian a B durante y segundos y luego hacen C durante z segundos antes de volver a A. El pseudocódigo tendrá un aspecto similar al siguiente:

// este código está dentro de la función de actualización del jefe, por lo que se ejecuta en cada fotograma si (elapsedTime ()> 2.0) // este bloque if se ejecuta durante 3 segundos, porque la diferencia en el tiempo con el bloque if debajo de // es tres segundos. BossShot1 (); // variación de disparo que se ejecutará esta vez else if (elapsedTime ()> 5.0) bossShot2 ();  else if (elapsedTime ()> 6.0) bossShot3 ();  if (elapsedTime ()> 7.0) // restablece el tiempo para que el jefe ejecute la primera acción de nuevo resetsTime (); 

La definición de las funciones de disparo de jefe está abajo. Siéntase libre de cambiarlo para que se ajuste a lo que quiere para una pelea de jefe:

function bossShot1 () // un sencillo disparo directo bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle); // shotAngle es 180 función bossShot2 () // a balas de tres direcciones shot bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle); bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle + 30); bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle - 30);  function bossShot3 () // para este, voy a hacer un tiro circular, por lo que las balas formarán un círculo para (var i = 0; i <= 9; i++)  bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, 36 * i); // change the shotAngle  

Depende de