Advertisement
  1. Game Development
  2. Platformer
Gamedevelopment

Construyendo un Videojuegos de Palizas en Game Maker, Parte 3: ataque combinado, más inteligencia artificial y recoleccion de salud

by
Difficulty:IntermediateLength:LongLanguages:

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

En el último artículo, comenzamos a configurar el combate, dimos a nuestro jugador una barra de salud y le dimos a nuestros enemigos la capacidad de atacar al jugador. Este fue un buen comienzo, pero nuestros enemigos aún no pueden moverse hacia el jugador para atacar o caminar alrededor del campo de batalla. Obviamente, esto no va a cortar, así que tomemos un tiempo para construir nuestra IA.

Al igual que en el último artículo, asegúrese de descargar el paquete de activos para este artículo antes de comenzar.

Movimiento

Lo primero que haremos es darle a nuestro Enemigo la capacidad de avanzar hacia un destino objetivo. Para configurar esto, necesitaremos un evento FindTarget, que determina a dónde debe ir el Enemigo, y un evento Move que le dice al Enemigo que comience a moverse allí.

Esto será similar a cómo movemos nuestra cámara ya que usaremos las variables TargetX y TargetY como lo hicimos allí. Puede ver dónde se colocarán estos eventos en el evento Step si observa los casos PositionFront y PositionBehind.

The comments that we will eventually replace with the targetting and movement code

Primero, agregaremos nuestras variables objetivo.

  1. En el objeto OBJ_Enemy, abra el código para Crear evento.
  2. Agregue el siguiente código al final del evento:

A continuación, agregaremos el evento FindTarget. Por ahora, este evento encontrará una posición aleatoria cerca del Jugador, y establecerá el objetivo del Enemigo en esa posición. Una vez que sabemos que el evento Move funciona, podemos implementar una orientación más sofisticada con una instrucción switch.

  1. En el objeto Enemigo, elija Add Event > Other > User Defined > User 4. (Agregar evento> Otro> Definido por el usuario> Usuario 4.)
  2. Agregue acción Control > Code > Execute Code (Control > Código> Ejecutar código).    
  3. Agregue el siguiente código:

Como dije, este código elige una posición aleatoria cerca del Jugador, en este caso dentro de un radio de 10 píxeles a su alrededor, y establece la posición objetivo del Enemigo en ese punto. También usa una instrucción if para verificar si el jugador todavía está en AttackRange de la posición objetivo, o si la distancia entre el TargetY y la y del jugador es mayor que el LayerSize. De esta forma, si el jugador no se ha movido desde que se encontró la última posición objetivo, la posición objetivo no cambiará sin ningún motivo.

Ahora necesitamos hacer el código de movimiento.

  1. En el objeto Enemigo, elija Add Event > Other > User Defined > User 5.
  2. Agregue la accion Control > Code > Execute Code.
  3. Agregue el siguiente código:

Este código verifica cuán cerca está el Enemigo de su posición objetivo. Si la distancia al objetivo es mayor que su distancia de movimiento, el enemigo se mueve hacia la posición objetivo; de lo contrario, se detienen.

Antes de que podamos probar esto en el juego, necesitamos agregar estos eventos al evento Step en las ubicaciones que resalté anteriormente.

En el evento Step del Enemigo, reemplace los comentarios //Find Target position con el siguiente código:

Luego reemplace los comentarios //Move there con el siguiente código:

Ahora, cuando vayas al juego, los enemigos deberían comenzar a moverte hacia ti en el momento en que ingreses a BattleRegion.

El movimiento en sí parece funcionar bien, aparte del hecho de que no hay animación, pero es posible que notes algo extraño cuando atacas a los Enemigos. Si atacas al Enemigo mientras se están moviendo, no dejan de moverse mientras están en el estado Hit. Esto se debe a que la secuencia de comandos move_towards_position cambia la variable de velocidad incorporada del objeto (observe la s minúscula), que los mueve hacia adelante a una velocidad constante hasta que le indiquemos a la velocidad que cambie. Entonces, incluso después de que el Enemigo es golpeado, su velocidad continúa moviéndolos, ya que no les hemos dicho que se detengan.

Si no les dijéramos que se detuvieran, nunca se detendrían solos. Podemos solucionar esto modificando el evento Step para que su caso Hit establezca su velocidad en 0. Si no les dijéramos que se detuvieran, nunca se detendrían solos. Podemos solucionar esto modificando el evento Step para que su caso Hit establezca su velocidad en 0.

  1. En OBJ_Enemy, ve al evento Step.
  2. En el código, busque el caso Hit para la instrucción switch y reemplácelo con este código:

Ahora cuando va al juego y ataca a los enemigos, no se moverán durante su animación de golpe.

Agregar animación básica

Definitivamente estamos en un buen comienzo, pero todavía hay algunos problemas obvios con el movimiento. En primer lugar, los Enemigos no se animan en absoluto, sino que se deslizan por el campo de batalla hacia el Jugador. Esto no se ve muy bien, así que vamos al evento Animate que hicimos en el último tutorial para mejorarlo.

  1. En OBJ_Enemy, entra en User Defined 3.
  2. Reemplace el caso existente "PositionFront" y "PositionBehind" en la instrucción switch con este código:

En este nuevo código, lo primero que hacemos es establecer la image_xscale según la dirección en la que se mueve el Enemigo. Por lo tanto, si se mueven hacia la izquierda, mirarán hacia la izquierda, y si se mueven hacia la derecha, mirarán hacia la derecha. Entonces, si su velocidad no es igual a 0, establece su animación en la animación ambulante, y si lo hace, pone su animación a ralentí.

Ahora debería verse mucho mejor cuando lo pruebes en el juego.

PositionFront vs. PositionBehind

En este punto, puede estar pensando "¿Por qué necesitamos PositionFront y PositionBehind si hacen todas las mismas cosas de todos modos?" Bueno, si miras nuestro movimiento Enemigo actual, verás que hay mucho margen de mejora.

Tener dos estados separados nos permite tener cada aproximación enemiga desde diferentes lados, en lugar de tener todos los Enemigos atacando desde la misma ubicación. Esto hará que las batallas sean más desafiantes y más difíciles de escapar.

Para implementar esto, necesitamos modificar el código FindTarget para que tenga en cuenta el estado al elegir la posición objetivo. En lugar de agregar una instrucción if o una declaración switch, lo haremos usando la variable SideMod.

SideMod es en realidad una variable que hemos tenido desde el principio. Si miras hacia atrás al código Create y a la declaración switch en el evento Enemy's Step, verás que ya comenzamos a usar SideMod.

PositionFront and PositionBehind with SideMod being used

SideMod actuará como un modificador que compensará la posición objetivo del Enemigo, relativa a la posición del jugador. Entonces, si SideMod es positivo, el objetivo del Enemigo será el rango de posición de PlayerX +, y si es negativo, el objetivo del Enemigo será el rango de posición de PlayerX. De esta forma, ambos estados pueden usar el mismo código de segmentación, pero terminan con diferentes resultados.

Para ver esto en acción, editemos el evento Find Target.

  1. En OBJ_Enemy, vaya a User Defined 4.
  2. Reemplace la sentencia if con el siguiente código:

Este código mejora el código original de algunas maneras importantes. Para la segmentación en sí, el código de segmentación ahora incluye un búfer de 60 píxeles entre el Jugador y el Enemigo, para evitar que el Enemigo se mueva para pararse sobre el Jugador. Además de eso, el código de segmentación ahora usa SideMod para determinar en qué lado del jugador debería estar el Enemigo. Si SideMod es positivo, la posición del Enemigo sería PlayerX + PositionRange, y si es negativa, sería PlayerX - PositionRange.

También hicimos algunos cambios a la declaración if. La instrucción if ahora verifica si el Enemigo está demasiado cerca del Jugador, así como demasiado lejos, para mantener la distancia del búfer que agregamos. La declaración if también ahora verifica si el Enemigo está en el lado incorrecto del Jugador en caso de que el Jugador se moviera. Entonces, si el Estado del Enemigo es PositionFront, y el Jugador se mueve para que el Enemigo termine detrás del Jugador, ellos encontrarán una nueva posición objetivo.

Si vas al juego ahora, los Enemigos deberían comportarse de una manera mucho más interesante cuando intenten rodear al Jugador.

Better Enemy Behavior

Enfrentando al jugador

Esa fue una mejora bastante grande, pero todavía hay algunos problemas. Una de las más claras es obvia si dejas que los Enemigos te rodeen un par de veces, y se puede ver en la imagen de arriba. Esencialmente, los enemigos no ajustan la dirección a la que se enfrentan en relación con la posición del jugador. Entonces, si el Enemigo mira hacia la izquierda y el Jugador está a la derecha de ellos, cualquier ataque que usen irá en la dirección incorrecta. Sin embargo, este es un problema bastante simple de arreglar.

  1. En OBJ_Enemy, vaya a User Defined 3.
  2. En el código, reemplace el caso PositionFront/PositionBehind con el siguiente código:

Con el código original, el Enemigo siempre miraría en la dirección de la posición de Objetivo. Con el nuevo código, el Enemigo mira hacia su posición objetivo cuando están lejos, y comienza a mirar hacia el Jugador una vez que están a una distancia corta de su objetivo. Esto asegura que una vez que el Enemigo esté en rango de ataque, comenzarán a enfrentarse al Jugador en lugar de a la posición objetivo.

Ahora debería ver una mejora importante cuando ingrese al juego.

El estado de cola básica

Las cosas deberían comenzar a juntarse, pero aún tenemos un problema persistente. En este momento, nuestros enemigos no cambian su comportamiento en función de la cantidad de enemigos atacados o de otros enemigos.

Esto facilita a los enemigos rodear al jugador y abrumarlos. El jugador generalmente puede administrar de tres a cuatro enemigos como lo tenemos ahora, pero cualquier cosa más que eso se saldrá rápidamente de control. Para resolver estos problemas, vamos a agregar un nuevo estado llamado Queuing.

El estado de Cola actuará como un estado temporal para el Enemigo mientras esperan una apertura para atacar. Mientras el Enemigo esté en cola, permanecerá relativamente cerca del jugador, pero no se acercará lo suficiente como para atacar hasta que uno de los enemigos atacantes muera o cambie a un estado diferente. Si echas un vistazo al video a continuación, puedes ver algo como esto en acción:

En el video anterior, presta mucha atención al comportamiento de los Enemigos antes de que comiencen a atacar al Jugador. Como puede ver, los Enemigos no se acercan al Jugador hasta que el Jugador haya matado o noqueado al Enemigo con el que actualmente pelea. Este es el comportamiento que queremos emular en nuestro juego.

Puede ver una idea general de cómo funcionará nuestro nuevo Estado en el siguiente diagrama de estado:

The Enemy State Diagram with Queueing

Para que este sistema funcione, necesitamos tener una cola, o una lista, de los enemigos que están atacando actualmente. Entonces, cuando un Enemigo se encuentra dentro de cierto rango del Jugador, pueden mirar la lista y ver cuántos Enemigos hay en él. Si la lista ya se ha agotado, cambiarán al estado de cola y esperarán su turno. Si la lista no está completa, se agregarán a la lista de atacantes y continuarán acercándose al Jugador.

Entonces, lo primero que necesitaremos es la cola misma. Voy a usar una ds_list y agregarla al objeto Player.

Si nunca antes ha usado un objeto ds_list, puede obtener más información aquí. Para decirlo simplemente, una ds_list es una versión más flexible de una matriz que le brinda más funciones integradas, como Shuffle, Sort e Insert, y puede hacer que ciertas operaciones sean un poco más fáciles.

  1. Con OBJ_Player, vaya al evento Create.
  2. Agregue el siguiente código al final del código de evento de creación:

Ahora debemos asegurarnos de que los enemigos hagan referencia a esta lista cuando se preparen para atacar. Para hacer esto, agregaremos una instrucción if al final del evento Move que verifica si la lista enemiga está completa cuando están dentro del rango para agregarse. Si lo es, cambiarán de estado y, de lo contrario, se agregarán a la lista. 

  1. Con OBJ_Enemy, vaya a User Defined 5.
  2. Agregue el siguiente código al final del evento Move:

Esta declaración if comprueba tres cosas antes de agregar un Enemigo a la lista. Primero, verifica si el Enemigo está a menos de 200 píxeles de distancia. Luego verifica que la lista ya contenga menos de dos Enemigos. Finalmente, verifica para asegurarse de que no estén ya en la lista. Si las tres cosas son ciertas, el Enemigo se agrega a la lista.

En este punto, también debemos ingresar al evento de cambio de estado y asegurarnos de que un Enemigo cambie al Estado de cola si no puede atacar.

  1. Con OBJ_Enemy, vaya a User Defined 0.
  2. Agregue el siguiente caso a la declaración switch:

Esta declaración if verifica las mismas cosas que la que agregamos al evento anterior, excepto que verifica la distancia, ya que ya están en el estado PositionFront / PositionBehind. Si la instrucción if es verdadera, establece el estado del Enemigo en "Queuing" y establece su velocidad en 0.

Ahora intente ir al juego, y vea qué sucede cuando obtiene la atención de los tres Enemigos. Debería comenzar de la misma manera que antes, pero después de unos segundos, uno de los Enemigos debería dejar de acercarse al Jugador. Esto se debe a que han cambiado al estado de Cola y ya no se les dice que se muevan hacia su posición objetivo.

Animar el estado de cola

Este es un buen comienzo, pero hay algunos problemas evidentes que debemos abordar. En primer lugar, si miras al Enemigo que cambió al estado de Cola, verás que su animación de caminata continuará reproduciéndose incluso después de que dejen de moverse.

The Queueing state in action without a unique animation event

Este problema se produce porque el estado de cola no tiene ningún código de animación propio. Entonces, cualquiera que sea la animación que se estaba ejecutando cuando comenzó el estado de Cola es la animación, permanecerá hasta que el estado cambie a uno que active el código de animación.

Podemos solucionar esto agregando un código de animación para nuestro nuevo estado y agregando el estado de Cola a la declaración de cambio en el evento Step.   

  1. En OBJ_Enemy, vaya a User Defined 3.
  2. Vaya al evento de código y agregue este caso a la declaración de cambio:

Aparte de un pequeño cambio, este código de animación es exactamente el mismo que el código que tenemos para PositionFront y PositionBehind. La diferencia es que este código hace que el Enemigo empiece a mirar al jugador desde una distancia mayor que los otros dos estados. Esto deja en claro que el Enemigo se está enfocando en el Jugador, y no se está moviendo al azar.

Finalmente, necesitamos agregar un caso de Queueing a la declaración de cambio en el evento Step para que podamos asegurarnos de que se use el código de la animación.

  1. En OBJ_Enemy, ve al evento Step.
  2. Vaya al evento del código y agregue este caso a la declaración Switch.

Todo lo que este código hace es ejecutar el evento animado en cada paso.

Ahora bien, si vas al juego, el Enemigo debería detenerse por completo en el momento en que cambian al estado de Cola.

Reanudando el ataque

El próximo problema que tenemos aparecerá si intentas matar a cualquiera de los enemigos enemigos. Pase lo que pase, el enemigo de cola nunca cambia a los estados PositionFront o PositionBehind. Incluso si matas a los dos Enemigos atacantes y comienzas a atacar al Enemigo en cola restante, continuarán sin hacer nada.

Este problema es causado por varios factores. Primero, los enemigos nunca se eliminan de la lista de atacantes, incluso cuando mueren. Entonces, incluso después de que todos los Enemigos mueran, el Enemigo en cola nunca verá una apertura para comenzar a atacar. Además de eso, incluso si los Enemigos se eliminaron de la lista, no tenemos ningún código en el evento de estado de cambio para tratar con el estado de cola. Hagamos frente a estos problemas de a uno por vez.

Primero comencemos a eliminar Enemigos de la lista de atacantes después de que mueran. Podemos hacer esto con un evento Destruir que ejecuta el código de eliminación cuando se mata al Enemigo.

  1. Ve al objeto OBJ_Enemy.   
  2. Elija Add Event > Destroy.
  3. Elija Control > Execute Code.
  4. Agregue el siguiente código al evento de código:

Este código verifica si el Enemigo está en la lista de atacantes.

Si lo son, los elimina de la lista.Lo segundo que tenemos que hacer es conseguir que el Enemigo busque aperturas. Podemos hacer esto agregando código para el estado de cola en el evento Change State. El código que vamos a agregar verá la lista de atacantes y determinará cuántos Enemigos hay en ella. Si la lista no está completa, el Enemigo se agregará a la lista y cambiará a "PositionFront" o "PositionBehind", dependiendo de dónde se encuentren en relación con el jugador.

  1. En OBJ_Enemy, vaya a User Defined 0.
  2. Vaya al evento de código y agregue este caso a la declaración de cambio:

Este código hace exactamente lo que dije arriba. Si la lista Enemigo no está llena, el Enemigo se agregará a sí mismo y elegirá el mejor estado según su posición.

Ahora, si vas al juego deberías ver al enemigo Queueing comportándose correctamente.

Separar a los enemigos

Mientras que nuestro estado de colas está casi terminado por ahora, hay una última cosa que tenemos que hacer. Si juegas el juego por un tiempo, puedes notar que algo como esto sucede cuando múltiples enemigos te intentan atacar:

The issue with the Enemy positioning

El problema aquí es que ambos enemigos eligieron aleatoriamente el estado PositionBehind, y terminaron con posiciones Target muy similares, por lo que ahora están básicamente uno encima del otro. Esto hace que sea difícil para el Jugador ver cuántos enemigos están luchando, y hace que sea más fácil para el Jugador intentar y escapar o simplemente matar a ambos Enemigos simultáneamente. Idealmente, un Enemigo se acercaría desde la izquierda, y uno se acercaría desde la derecha.

Para resolver este problema, vamos a agregar una pieza más de código a nuestro evento Change State.

  1. Con OBJ_Enemy, vaya a User Defined 0.
  2. Agregue el siguiente código al comienzo del PostionFront, PositionBehind.

Su código debería verse así, con el nuevo código delineado en rojo:

The new positioning code for our Enemy

Este código hace dos cosas. Primero, verifica si hay otros Enemigos que se encuentran en / cerca del destino de destino. Si los hay, cambia de PositionFront a PositionBehind, o viceversa.

Lo importante que debe tener en cuenta sobre esta comprobación es que la declaración if también verifica que la instancia que está encontrando no es ella misma. Esto se debe a que el script que estamos usando, instance_position(), no tiene la capacidad de excluirse de los resultados.

Asegúrese de que este código aparezca antes del código que verifica si el Enemigo debe estar en cola.

Ahora, si vas al juego para probarlo, ya no deberías ver a varios Enemigos atacando desde el mismo lado.

Si bien nuestro estado de Cola sigue siendo un poco incompleto, y nuestra IA Enemiga necesita algunos ajustes más, creo que debemos cambiar de marcha para el resto de este artículo, y centrarnos en algo un poco más emocionante.

Ataques combinados

Los ataques combinados son un elemento básico de todos los juegos de acción clásicos. Enlazando múltiples ataques y combinando ataques de maneras únicas, mantiene el combate interesante a través de muchos niveles. Entonces, antes de que comencemos a ajustar la IA, construyamos un sistema combinado.

Antes de que podamos implementar cualquier combos, hay dos tipos principales de combo que debes entender: combos AB y combos A + B.

Los combos AB son combos que ocurren cuando el jugador usa dos o más ataques en un orden específico, dentro de un período de tiempo limitado. Un golpe ligero, luego un golpe fuerte en rápida sucesión sería un combo AB.

Los combos A + B son combos donde el jugador usa dos ataques diferentes simultáneamente. Un golpe ligero y un golpe pesado al mismo tiempo sería un combo A + B.

Estos también se pueden combinar en un tercer tipo de combo, AA + B, donde se usa un combo A + B como uno de los pasos en un combo AB. Un golpe ligero, luego un golpe de luz simultáneo y un golpe pesado sería un ejemplo de este tercer tipo.

Vamos a comenzar haciendo un combo A + B.

Haciendo el nuevo ataque

Para que funcione nuestro combo, necesitaremos un tercer ataque para el jugador. Este ataque será Uppercut y necesitará dos nuevos sprites, el sprite de ataque y el sprite de hitbox. Agregue dos nuevos sprites al juego, como se describe a continuación:

Nombre de Sprite

Imágenes

Origen

SPR_PlayerUppercut

PlayerUppercut1.png, PlayerUppercut2.png, PlayerUppercut3.png, PlayerUppercut4.png, PlayerUppercut5.png

X = 45, Y = 128

SPR_PlayerUppercut_Hitbox

PlayerUppercutHitBox1.png, PlayerUppercutHitBox2.png, PlayerUppercutHitBox3.png, PlayerUppercutHitBox4.png, PlayerUppercutHitBox5.png

X = 45, Y = 128

También importaremos un nuevo sonido para usar con el ataque Uppercut.

  1. Haga clic con el botón derecho en la carpeta Sounds y elija Create Sound.
  2. Establezca el nombre en SND_Uppercut.
  3. Importe el archivo de sonido HeavyPunch2.wav desde los activos del proyecto.
  4. Establezca la Sample Rate a 48000.    
  5. Establezca la Bit Rate en 320.
  6. Presiona Ok para guardar tu sonido.

A continuación, tenemos que crear el nuevo objeto de ataque. Siga estos pasos para hacer el objeto de ataque Uppercut:    

  1. Haga clic con el botón derecho en la carpeta Objects y elija Create Object.
  2. Denomine el objeto ATK_Uppercut.
  3. Establezca el sprite del objeto en SPR_PlayerUppercut_Hitbox.
  4. Establezca el elemento primario para el objeto en OBJ_ATK.
  5. Desmarque la casilla de verificación Visible.    
  6. Haga clic en Add Event > Create.
  7. Agregue el acción Control > Code > Execute Code.    
  8. Agregue el siguiente código:

En este momento, la única diferencia entre este ataque y el fuerte ataque es que el uppercut aturde al enemigo por un poco más de tiempo. Eventualmente, sin embargo, este ataque se hará más único con efectos de retroceso.

Puedes cerrar este objeto.

Modificar cómo atacamos

Ahora que tenemos un ataque para que se active nuestro combo, debemos comenzar a detectar cuándo se usa el combo. Para hacer esto, vamos a modificar el evento presionar <cualquier tecla> que hicimos en el primer tutorial.

The original Press Any Key event

En caso de que no recuerde, el código actual prueba para ver si está presionando cualquiera de los botones de ataque básicos, o cualquiera de los botones de ataque fuerte, y luego configura su AttackType en consecuencia. Luego, si estás en el suelo, llama al Evento User Event 2 para ejecutar el ataque real.

En este momento, solo estamos detectando presionar un botón a la vez, pero necesitamos detectar varios botones para manejar combinaciones A + B. Podríamos hacer esto haciendo otro si eso dice "si presiona uno de los botones de ataque fuerte, Y uno de los botones de ataque básicos, use uppercut", pero eso se iría rápidamente de las manos. A medida que añadimos más ataques, estos controles se volverían más complejos, más difíciles de leer de un vistazo, y tendríamos una serie de comprobaciones repetidas de los botones utilizados en múltiples combos.

Por lo tanto, en lugar de buscar las combinaciones de botones, cada botón que se presiona se agregará a una cadena de texto. Luego tendremos una declaración if en la parte inferior que dice: "Si la cadena dice 'Arriba + Ataque básico', use la opción A, y si dice 'Arriba + Ataque fuerte', use la opción B, etc.". , podemos verificar cada botón una vez, y el código será mucho más fácil de leer.

Para hacer esto, necesitaremos hacer un par de cambios. Primero, necesitamos agregar una cadena que almacenará nuestra lista de botones. Vamos a llamar a esta variable ButtonCombo.

  1. Vaya a OBJ_Player y abra el evento presionar <cualquier tecla>.
  2.    Agregue esta línea de código al comienzo del evento:

Luego, en el primero si, cambie el código a esto:

Y cambie el código dentro del segundo si a esto:

Finalmente, agregue este código antes de la declaración if que verifica si el jugador está en el suelo:

Tu código ahora debería verse así:

The updated Press Any Key event

En general, este código es básicamente el mismo que teníamos antes. La diferencia es que, en lugar de tener las sentencias if configuradas con AttackType directamente, todo lo que hacen es agregar una cadena única para cada botón que se presiona al final de la cadena ButtonCombo. Entonces, AttackType se establece en función de la cadena final de ButtonCombo. Al hacerlo de esta manera, la cadena final de ButtonCombo que se evalúa es una combinación de todos los botones que se presionan, y el ataque se basa en eso, en lugar de ser simplemente un tipo de ataque estático en función del botón individual que se presiona.

También tenemos una instrucción string_delete antes de la instrucción if que establece AttackType. Esta declaración elimina el signo + desde el comienzo de la cadena ButtonCombo para mejorar la legibilidad.

Haciendo el Combo A + B

Con este nuevo sistema, si presiona múltiples botones que el evento busca, todos aparecerán en la cadena. Entonces, todo lo que tiene que hacer para agregar un combo A + B es agregar un nuevo bloque else / if que compruebe el nuevo ataque al final de la sentencia if que determina AttackType. Por ejemplo, agregue el siguiente código al final de if / else que establece AttackType para implementar un combo A + B para Uppercut:

Tu código ahora debería verse así:

The new ButtonCombo code with Uppercut

Sin embargo, antes de que podamos ejecutar el Uppercut, también debemos ingresar al User Defined 2 y agregar este bloque de código al final de la declaración if que verifica en qué AttackType está establecido el jugador.

Tu código ahora debería verse así:

The modified attack creation code with Uppercut added

Si hiciste todo bien, puedes ingresar al juego y usar Uppercut presionando los botones Basic Punch y Strong Punch al mismo tiempo.

Hacer un Combo AB

A diferencia de los combos A + B, los combos AB requieren que sepamos el historial de ataques que usó el Reproductor, en lugar de solo el más reciente.

Para realizar un seguimiento de los movimientos que ya se han utilizado, crearemos lo que llamo la Lista de comandos. La lista de comandos será un objeto ds_list que realiza un seguimiento de las últimas cadenas de X ButtonCombo. Cada vez que utilizamos un movimiento, todo lo que tenemos que hacer es mirar los comandos más recientes en la lista y verificar si coinciden con alguno de nuestros combos. Esto es similar a lo que hacemos ahora, solo que miraremos las últimas cadenas de ButtonCombo, en lugar de solo las más recientes.

Antes de que podamos configurar nuestro nuevo Combo, necesitamos crear un nuevo objeto de ataque. Importe los siguientes recursos para el nuevo ataque:

Nombre de Sprite Imágenes Origen
SPR_PlayerTriplePunch PlayerTriplePunch1.png, PlayerTriplePunch2.png, PlayerTriplePunch3.png, PlayerTriplePunch4.png, PlayerTriplePunch5.png X = 46, Y = 129
SPR_PlayerTriplePunch_HitBox PlayerTriplePunchHitBox1.png, PlayerTriplePunchHitBox2.png, PlayerTriplePunchHitBox3.png, PlayerTriplePunchHitBox4.png, PlayerTriplePunchHitBox5.png X = 46, Y = 129

Vamos a importar un nuevo sonido para usar con el nuevo combo.

  1. Haga clic con el botón derecho en la carpeta Sounds y elija Create Sound.
  2. Establezca el nombre en SND_TriplePunch.
  3. Importe el archivo de sonido LightPunch2.wav desde los activos del proyecto.
  4.     Establezca la Sample Rate a 48000.
  5. Establezca la Bit Rate en 320.
  6. Presiona Ok para guardar tu sonido.

Finalmente, necesitamos crear el nuevo objeto de ataque. Este nuevo ataque se llamará Triple Punch:

  1.     Haga clic con el botón derecho en la carpeta Objects y elija Create Object.
  2. Denomine el objeto ATK_TriplePunch.
  3. Establezca el sprite del objeto en SPR_PlayerTriplePunch_Hitbox.   
  4. Establezca el elemento primario para el objeto en OBJ_ATK.
  5. Desmarque la casilla de verificación Visible.
  6. Haga clic en Add Event > Create.
  7. Agregue el acción Control > Code > Execute Code.   
  8. Agregue el siguiente código:

Por lo tanto, este ataque será un poco más fuerte que un ataque ligero estándar y aturdirá durante un período de tiempo más largo.

Ahora que tenemos nuestro nuevo ataque, podemos comenzar a modificar el código de ataque para implementar la Lista de comandos. Para comenzar, necesitamos agregar el objeto CommandList.

  1. Con OBJ_Player, entra en Create Event.
  2. Agregue el siguiente código al final del evento:

Eso es todo lo que tenemos que hacer para crear nuestra lista.

A continuación, tenemos que integrar la lista en nuestro sistema de combate.

  1. Con OBJ_Player, vaya al evento press <any key> .
  2. Reemplace la línea string_delete y la instrucción if/else if establece AttackType con el siguiente código:

Este código es muy similar a lo que teníamos antes. Primero, agrega la cadena ButtonCombo a CommandList. Entonces, dado que estoy limitando mi longitud de combo máximo a siete comandos, comprueba para asegurarse de que la lista de comandos sea menor o igual a siete elementos, y elimina cualquier elemento adicional en la lista. Finalmente, la sentencia if mira el elemento más reciente de la lista de la misma manera que miró a ButtonCombo y determina el AttackType.

Si prueba su juego ahora, debería funcionar exactamente como lo hacía antes de agregar la CommandList.

Tenga en cuenta que puede usar cualquier número que desee para la longitud máxima del combo. Elegí siete porque me pareció una buena opción, pero si quieres que tu juego tenga 10, 20 o incluso 100 combinaciones de acción, puedes hacerlo y el código funcionará bien. Personalmente, sugiero que lo aumente según sea necesario cuando realice combos nuevos, en lugar de tratar de anticipar lo que necesitará por adelantado.

Ahora todo lo que tenemos que hacer para crear un combo AB es crear un cheque que mire varios elementos en la lista, en lugar de solo uno. Entonces, digamos que quiero agregar mi triple golpe y hacerlo de modo que necesite hacer tres LightPunches en una fila para ejecutarlo. Para que funcione, necesitarías agregar este bloque de código al principio de la declaración if/else if que establece AttackType:

Al agregar este control a su juego, asegúrese de ponerlo al principio de la declaración if/else if. Si no lo hace, y lo deja al final, una o ambas verificaciones de los botones individuales se evaluarán como verdaderas antes de llegar a la verificación combinada AB, y la combinación nunca tendrá éxito. Entonces su if/else if modificado debería verse así:

The updated code to detect AB combos

Así que recuerda, cuando estás creando nuevos combos AB, cuantos más botones se necesiten para ejecutar el combo, más temprano debería estar en la sentencia if/else if. Si pones un combo AB en la parte inferior, casi nunca se ejecutará.

Recuerde también que las cadenas de ButtonCombo se agregan a la lista al final. Esto significa que el comando más reciente que se utilizará es el último elemento de la lista, no el primero. Entonces, si el combo fuera LAtk, SAtk, LAtk estaría en la posición Size-2, y SAtk estaría en la posición Size-1.

Finalmente, tenemos que regresar a User Defined 2 y agregar la declaración para crear el Triple Punch. Vaya a Definido por el usuario 2 y agregue este bloque de código al final de la declaración if que verifica en qué AttackType está establecido el jugador.

Ahora si vas al juego y usas tres Light Punches seguidos, deberías ejecutar con éxito un Triple Punch.

Borrar la lista de comandos

Finalmente, debemos borrar nuestra Lista de Comandos si el jugador tarda demasiado en continuar el combo. En este momento, siempre y cuando uses tres golpes básicos seguidos, ejecutarás un golpe triple. Incluso si hay una pausa de varios segundos entre cada golpe, no hará ninguna diferencia. Esto reduce en gran medida la habilidad requerida para ejecutar combinaciones potentes, por lo que debemos agregar un sistema que reinicie CommandList después de un cierto período. Utilizaremos una alarma para lograr esto.

Para crear la alarma, sigue estos pasos:

  1. En OBJ_Player, vaya a Add Event > Alarm > Alarm 0.
  2. Agregue la acción Control > Code > Execute Code.    
  3. Agregue el siguiente código:

Todo lo que hace este código es borrar completamente CommandList cuando la alarma se apaga.

A continuación, tenemos que ir al evento press <any key>y agregar código para activar esta alarma después del código que agrega la cadena ButtonCombo a CommandList, pero antes de la instrucción While. Puede ver exactamente dónde debe ir en la imagen siguiente:

The position where you should place the Alarm code

Agregue el siguiente código a esa ubicación en el evento press <any key>:

Este código inicia la alarma y la establece en 10 pasos o ciclos de juego. Si pasan 10 pasos antes de que el jugador presione un botón nuevo, la CommandList se borra; de lo contrario, el combo puede continuar.

Si vas al juego y pruebas el combo ahora, verás que solo funciona si presionas los botones en una sucesión relativamente rápida.

Aumentar la cantidad de pasos facilitará la combinación, y disminuirla hará que la combinación sea más difícil. Creo que 10 es una buena opción, pero necesitas encontrar el nivel de desafío adecuado para tu juego. Tus combos pueden ser muy complejos, por lo que puedes decidir si quieres darle al jugador más tiempo para terminarlos. Si realmente quieres disminuir el tiempo, también puedes hacerlo, pero tu juego será sustancialmente más desafiante para el jugador promedio con cada paso que restas.

Creando recolecciones

Lo último que veremos en este artículo es Recolecciones. Ningún peleador o plataforma está completo sin power-ups y pastillas de salud. Cuando el jugador se encuentra en una situación desesperada, no hay nada que aman más que conseguir esa comida al azar, o tropezar con un poder secreto. Crear un pickup básico es realmente muy fácil, pero antes de que podamos hacer eso, tendremos que importar un sprite para usarlo en nuestra recolección.

Este alegre sprite se usará para el objeto de recogida de los padres y la recolección de alimentos que crearemos más adelante.

Nombre de Sprite

Imágenes

Posición de origen

SPR_Food_Cherry

FoodCherry.png

X = 13, Y = 47

También importaremos un sonido nuevo para cuando el jugador reciba la comida.

  1. Haga clic con el botón derecho en la carpeta Sounds y elija Create Sound.
  2. Establezca el nombre en SND_FoodPickup.
  3. Importe el archivo de sonido FoodPickup.wav desde los activos del proyecto.
  4. Establezca la Sample Rate a 48000.
  5. Establezca la Bit Rate en 320.
  6. Presiona Ok para guardar tu sonido.

A continuación haremos nuestro objeto principal de recogida.

  1. Haga clic con el botón derecho en la carpeta Objects y elija Insert Object.
  2. Nombre el nuevo objeto OBJ_Pickup
  3. Establezca el sprite del objeto en SPR_Food_Cherry.
  4. Use Add Event > Create.
  5. En la pestaña Control, agregue una acción Ejecutar código.
  6. Agregue el siguiente código:

Este código establece la profundidad de la recolección en el mundo del juego de la misma manera que establecemos la profundidad del Jugador y del Enemigo previamente. También agrega una variable para almacenar el sonido que se producirá cuando recolectemos alimentos, del mismo modo que tenemos una variable para almacenar los sonidos de golpe y error de los ataques.

A continuación, agregaremos un evento de colisión para que el jugador pueda obtener la recolección en el juego.

  1. Con OBJ_Pickup, use Add Event > Collision > OBJ_Player.
  2. En la pestaña Control, agregue una acción Ejecutar código.    
  3. Agregue el siguiente código:

Esto comprueba si la recolección y el jugador están lo suficientemente cerca como para interactuar casi de la misma manera que verificamos colisiones con objetos de ataque. Si lo son, el pickup ejecuta el evento de usuario 0 y se destruye a sí mismo. El Evento de Usuario 0 es donde pondremos el código que aplica cualquier efecto que tenga el pickup. Al poner el código para el efecto de la recolección en un evento separado, podemos modificar fácilmente el efecto de las nuevas pastillas que agreguemos más tarde, sin tener que modificar el código de colisión.

Ahora solo tenemos que agregar un código de marcador de posición para el User Event 0, y la recolección debería funcionar perfectamente.

  1. Use Add Event > Other > User Event 0.
  2. En la pestaña Control, agregue una acción Ejecutar código.
  3. Agregue el siguiente código:

Este código es puramente un marcador de posición ya que la recolección de base en realidad no hace nada.

Si coloca la camioneta en su nivel e ingresa en el juego, debería verla desaparecer y escuchar el audio de la camioneta cuando colisiona con ella.

Si bien podríamos pasar a la primera recogida real de inmediato, demos sombra al pick-up de la base tal como lo hicimos con el Jugador y el Enemigo. De esta manera encaja bien en el estilo visual existente del juego.

  1. Use Add Event > Draw.   
  2. En la pestaña Control, agregue una acción Ejecutar código.
  3. Agregue el siguiente código:

Este código hace lo mismo que nuestro código de sombra para otros objetos. Primero, establece el alfa en .6 y establece el color en gris oscuro, luego dibuja una elipse para la sombra. Después de eso, dibuja el sprite real de la recolección.

Hacer la recolección de alimentos

Ahora que nuestro pick-up básico funciona, hagamos la versión de comida.

  1.     Haga clic con el botón derecho en la carpeta Objects y elija Insert Object.
  2. Nombre el nuevo objeto PKP_Food.
  3. Establezca el sprite del objeto en SPR_Food_Cherry.   
  4. Establezca Parent en OBJ_Pickup.
  5. Use Add Event > Create.
  6. En la pestaña Control, agregue una acción Ejecutar código.
  7. Agregue el siguiente código:

Este código hereda las propiedades de la clase base para establecer su profundidad, y crea una variable HealAmount que determina la fuerza de la capacidad de curación de la recolección.

Ahora ingresaremos al User Event 0, y agregaremos el código que realmente sana al jugador cuando recibe la comida.

  1. Use Add Event > Other > User Event 0.
  2. En la pestaña Control, agregue una acción Ejecutar código.
  3. Agregue el siguiente código:

Todo lo que hace es agregar el HealAmount al HP del jugador.

Antes de probar esto, asegúrese de eliminar la recolección original en el nivel y reemplazarla con el objeto alimenticio.

Ahora ve al juego para probar Food Pickup, y deberías ver algo interesante. Mientras que la recolección sana al jugador, en realidad empuja su salud más allá del borde de la barra de salud. Esto sucede porque no hicimos nada para evitar que CurrentHP del jugador excediera su MaxHP.

Regrese al User Event 0 en la recolección de alimentos y reemplace la línea de código existente con el siguiente código:

La diferencia entre esta versión y la versión original del código es que esta usa una declaración min para establecer la salud del jugador en el menor de los dos valores. Entonces, si CurrentHp + HealAmount es menor que su MaxHP, establecerá su CurrentHP a eso; de lo contrario, lo establecerá en su MaxHP e impedirá que supere ese valor.

Para probar esta nueva versión, debe esperar hasta que el reproductor haya recibido daño para usar el pickup. Intenta dejarte herir por los enemigos antes de obtener la comida y ver qué pasa. Debería ver que no importa qué tan poco daño haya tomado, o para qué configuró el HealAmount, su HP nunca va más allá del Máximo.

Hacer la recogida

Finalmente, convirtamos el objeto PKP_Food en una clase principal también, haciendo una recolección Cherry. De esta forma, puedes tener tantas recolecciones de alimentos diferentes como quieras, al continuar ampliando la clase base con nuevos objetos.

  1. Haga clic con el botón derecho en la carpeta Objects y elija Insert Object.
  2. Nombre el nuevo objeto PKP_FOOD_Cherry.
  3. Establezca el sprite del objeto en SPR_Food_Cherry.
  4. Establezca Parent en PKP_Food.
  5. Use Add Event > Create.
  6. En la pestaña Control, agregue una acción Ejecutar código.
  7. Agregue el siguiente código:

Ahora puede reemplazar la recolección genérica de alimentos con la cereza que acabamos de crear, y debería funcionar perfectamente en el juego.

Conclusión

Con eso completo, vamos a detenernos por el día. Asegúrese de regresar para el próximo artículo, donde finalizaremos el movimiento Enemy and Player e implementaremos algunas funciones nuevas.

Mientras tanto, no dude en hacerme cualquier pregunta o dejar comentarios en el feed a continuación.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.