Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Platformer
Gamedevelopment

Física básica de plataformas en 2D, Parte 4

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Basic 2D Platformer Physics .
Basic 2D Platformer Physics, Part 3
Basic 2D Platformer Physics, Part 5: Object vs. Object Collision Detection

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

Agarrando la cornisa

Ahora que podemos saltar, bajar desde las plataformas de una sola dirección y correr, también podemos implementar el agarre de rebordes. Mecánica que agarra retoños definitivamente no es imprescindible en todos los juegos, pero es un método muy popular de extender el rango de movimiento posible de un jugador sin hacer algo extremo como un doble salto.

Veamos cómo determinamos si un borde puede ser agarrado. Para determinar si el personaje puede tomar una repisa, constantemente comprobaremos el lado hacia el que se mueve el personaje. Si encontramos un mosaico vacío en la parte superior del AABB, y luego un mosaico sólido debajo de él, entonces la parte superior de ese mosaico sólido es el reborde al que nuestro personaje puede agarrarse.

Configurando variables

Vayamos a nuestra clase Character, donde implementaremos el agarre de salientes. No tiene sentido hacer esto en la clase MovingObject, ya que la mayoría de los objetos no tendrán la opción de agarrar un borde, por lo que sería un desperdicio realizar cualquier procesamiento en esa dirección.

Primero, necesitamos agregar un par de constantes. Comencemos creando las constantes de desplazamiento del sensor.

El cGrabLedgeStartY y cGrabLedgeEndY son compensaciones desde la parte superior de AABB; el primero es el primer punto sensor, y el segundo es el punto sensor final. Como puede ver, el personaje deberá encontrar una repisa dentro de los 2 píxeles.

También necesitamos una constante adicional para alinear el personaje con el azulejo que acaba de agarrar. Para nuestro personaje, esto se establecerá en -4.

Aparte de eso, querremos recordar las coordenadas del mosaico que agarramos. Vamos a guardarlos como variable del personaje miembro.

Implementación

Tendremos que ver si podemos agarrar la repisa del estado de salto, así que vayamos allí. Justo después de comprobar si el personaje ha aterrizado en el suelo, veamos si se cumplen las condiciones para agarrar una repisa. Las condiciones principales son las siguientes:

  • La velocidad vertical es menor o igual que cero (el personaje está cayendo).
  • El personaje no está en el techo; no sirve de nada agarrar una repisa si no puedes saltar de ella.
  • El personaje colisiona con la pared y se mueve hacia ella.

Si se cumplen esas tres condiciones, entonces tenemos que buscar el borde para agarrar. Comencemos por calcular la posición superior del sensor, que será la esquina superior izquierda o la esquina superior derecha de la AABB.

Ahora, como se pueden imaginar, aquí encontraremos un problema similar al que encontramos al implementar las comprobaciones de colisión: si el personaje está cayendo muy rápido, es muy probable que pierda el punto de acceso al que puede agarrarse la repisa . Es por eso que tendremos que comprobar si el mosaico que tenemos que tomar no está comenzando desde la esquina del marco actual, sino desde el anterior, como se ilustra aquí:


La imagen superior de un personaje es su posición en el cuadro anterior. En esta situación, debemos comenzar a buscar oportunidades para agarrar un borde desde la esquina superior derecha de la AABB del marco anterior y detenernos en la posición actual del marco.

Vamos a obtener las coordenadas de las fichas que debemos verificar, empezando por declarar las variables. Comprobaremos los mosaicos en una sola columna, por lo que todo lo que necesitamos es la coordenada X de la columna, así como sus coordenadas Y superiores e inferiores.

Vamos a obtener la coordenada X de la esquina de AABB.

Queremos comenzar a buscar una repisa desde la posición del marco anterior solo si ya nos estábamos moviendo hacia la pared empujada en ese momento, por lo que la posición X de nuestro personaje no cambió.

Como puede ver, en ese caso estamos calculando el topY utilizando la posición del fotograma anterior, y el de abajo usando el del fotograma actual. Si no estuviéramos al lado de una pared, simplemente veremos si podemos agarrar una repisa utilizando solo la posición del objeto en el marco actual.

De acuerdo, ahora que sabemos qué fichas controlar, podemos comenzar a iterar a través de ellas. Iremos de arriba hacia abajo, porque este orden tiene más sentido ya que permitimos el agarre de la repisa solo cuando el personaje se está cayendo.

Ahora revisemos si el mosaico que estamos iterando cumple las condiciones que permiten al personaje agarrar un borde. Las condiciones, como se explicó anteriormente, son las siguientes:

  • El azulejo está vacío.
  • El azulejo debajo de él es un azulejo sólido (este es el azulejo que queremos agarrar).

El siguiente paso es calcular la posición de la esquina del azulejo que queremos agarrar. Esto es bastante simple: solo necesitamos obtener la posición del mosaico y luego compensarlo con el tamaño del azulejo.

Ahora que sabemos esto, debemos verificar si la esquina está entre nuestros puntos de sensor. Por supuesto, queremos hacer eso solo si estamos revisando la tesela con respecto a la posición actual del recuadro, que es el mosaico con una coordenada Y igual a la parte bottomY. Si ese no es el caso, entonces podemos suponer con seguridad que pasamos la repisa entre el marco anterior y actual, por lo que queremos agarrar el borde de todos modos.

Ahora que estamos en casa, hemos encontrado la repisa que queremos atrapar. Primero, guardemos la posición del mosaico de la repisa agarrada.

También necesitamos alinear el personaje con la repisa. Lo que queremos hacer es alinear la parte superior del sensor de reborde del personaje con la parte superior de la losa, y luego compensar esa posición con cGrabLedgeTileOffsetY.

Aparte de esto, tenemos que hacer cosas como establecer la velocidad a cero y cambiar el estado a CharacterState.GrabLedge. Después de esto, podemos romper el ciclo porque no tiene sentido iterar a través del resto.

¡Eso va a ser! Las repisas ahora se pueden detectar y capturar, por lo que ahora solo tenemos que implementar el estado GrabLedge, que saltamos antes.

Ledge Grab Controls

Una vez que el personaje agarra una repisa, el jugador tiene dos opciones: puede saltar hacia arriba o hacia abajo. Saltar funciona normalmente. el jugador presiona la tecla de salto y la fuerza del salto es idéntica a la fuerza aplicada al saltar del suelo. La caída se realiza presionando el botón hacia abajo, o la tecla direccional que apunta hacia afuera de la repisa.

Implementación de controles

Lo primero que hay que hacer es detectar si el borde está a la izquierda o a la derecha del personaje. Podemos hacer esto porque guardamos las coordenadas de la repisa que el personaje está agarrando.

Podemos usar esa información para determinar si se supone que el personaje debe abandonar el saliente. Para desplegar, el jugador necesita:

  • presione el botón hacia abajo
  • presione el botón izquierdo cuando estamos agarrando una repisa a la derecha, o
  • presione el botón derecho cuando estamos agarrando una repisa a la izquierda

Hay una pequeña advertencia aquí. Considere una situación cuando estamos manteniendo presionado el botón hacia abajo y el botón derecho, cuando el personaje se está sosteniendo en una repisa a la derecha. Conseguirá la siguiente situación:

El problema aquí es que el personaje agarra el borde inmediatamente después de soltarlo.

Una solución simple a esto es bloquear el movimiento hacia la repisa para un par de cuadros después de que caímos de la repisa. Para eso necesitamos agregar dos nuevas variables; llamémosles mCannotGoLeftFrames y mCannotGoRightFrames.

Cuando el personaje cae de la cornisa, necesitamos establecer esas variables y cambiar el estado para saltar.

Ahora volvamos un poco al estado Jump, y asegurémonos de que respete nuestra prohibición de movernos hacia la izquierda o hacia la derecha después de dejar la cornisa. Restablezcamos las entradas justo antes de comprobar si debemos buscar una repisa para agarrar.

Como puede ver, de esta manera no cumpliremos las condiciones necesarias para agarrar un borde siempre que la dirección bloqueada sea la misma que la dirección de la repisa que el personaje puede tratar de agarrar. Cada vez que negamos una entrada en particular, disminuimos a partir de los cuadros de bloqueo restantes, por lo que eventualmente podremos mover de nuevo, en nuestro caso, después de 3 cuadros.

Ahora sigamos trabajando en el estado GrabLedge. Como manejamos la caída desde la cornisa, ahora debemos permitir saltar desde la posición de agarre.

Si el personaje no cayó del borde, debemos verificar si se ha presionado la tecla de salto; si es así, debemos establecer la velocidad vertical del salto y cambiar el estado:

¡Eso es practicamente todo! Ahora el agarre de la repisa debería funcionar correctamente en todo tipo de situaciones.

Permitir que el personaje salte poco después de abandonar una plataforma

A menudo, para hacer los saltos más fáciles en los juegos de plataformas, el personaje puede saltar si simplemente se bajó del borde de una plataforma y ya no está en el suelo. Este es un método popular para mitigar la ilusión de que el jugador ha presionado el botón de salto pero el personaje no saltó, lo que podría haber aparecido debido al retraso de entrada o al jugador presionando el botón de salto justo después de que el personaje se movió fuera de la plataforma.

Implementemos una mecánica así ahora. Antes que nada, necesitamos agregar una constante de cuántos cuadros después de que el personaje se baje de la plataforma, aún puede realizar un salto.

También necesitaremos un contador de fotogramas en la clase Character, por lo que sabemos cuántos fotogramas ya tiene el personaje en el aire.

Ahora configuremos mFramesFromJumpStart en 0 cada vez que dejamos el suelo. Hagámoslo justo después de llamar a UpdatePhysics.

E incrementemos cada fotograma en el estado de salto.

Si estamos en el estado de salto, no podemos permitir un salto en el aire si estamos en el techo o tenemos una velocidad vertical positiva. Velocidad vertical positiva significaría que el personaje no ha perdido un salto.

Si ese no es el caso y se presiona la tecla de salto, todo lo que tenemos que hacer es establecer la velocidad vertical en el valor de salto, como si saltamos normalmente, aunque el personaje ya esté en el estado de salto.

¡Y eso es! Podemos establecer cJumpFramesThreshold a un gran valor, como 10 fotogramas, para asegurarnos de que funciona.

El efecto aquí es bastante exagerado. No es muy notable si permitimos que el personaje salte solo de 1 a 4 fotogramas después de que ya no esté en el suelo, pero en general esto nos permite modificar cuán indulgente queremos que sean nuestros saltos.

Escalando los objetos

Vamos a hacer posible escalar los objetos. Ya tenemos la mScale en la clase MovingObject, por lo que todo lo que tenemos que hacer es asegurarnos de que afecte correctamente la compensación AABB y AABB.

Antes que nada, editemos nuestra clase AABB para que tenga un componente de escala.

Ahora editemos halfSize, de modo que cuando accedamos a él, obtengamos un tamaño escalado en lugar de uno sin escalar.

También querremos poder obtener o establecer solo un valor X o Y de la mitad del tamaño, por lo que también debemos crear getters y setters para ellos.

Además de escalar el propio AABB, también necesitaremos escalar el mAABBOffset, de modo que después de que escalemos el objeto, su sprite aún coincidirá con el AABB de la misma manera que lo hizo cuando el objeto no fue escalado. Regresemos a la clase MovingObject para editarlo.

Al igual que anteriormente, querremos tener acceso a los componentes X e Y por separado también.

Finalmente, también debemos asegurarnos de que cuando se modifique la escala en MovingObject, también se modifique en AABB. La escala del objeto puede ser negativa, pero la AABB en sí no debe tener una escala negativa porque confiamos en la mitad del tamaño para que siempre sea positiva. Es por eso que en lugar de simplemente pasar la báscula al AABB, vamos a aprobar una escala que tenga todos los componentes positivos.

Todo lo que queda por hacer ahora es asegurarnos de que cada vez que utilicemos las variables directamente, las usemos a través de los getters y setters ahora. Donde sea que usemos halfSize.x, querremos usar HalfSizeX, donde sea que usemos halfSize.y, querremos usar HalfSizeY, y así sucesivamente. Algunos usos de una función de búsqueda y reemplazo deberían tratarlo bien.

Verifica los resultados

La escala debería funcionar bien ahora, y debido a la forma en que construimos nuestras funciones de detección de colisiones, no importa si el personaje es gigante o pequeño, debería interactuar bien con el mapa.

Resumen

Esta parte concluye nuestro trabajo con el mapa de azulejos. En las siguientes partes, configuraremos las cosas para detectar colisiones entre objetos.

Tomó algo de tiempo y esfuerzo, pero el sistema en general debería ser muy robusto. Una cosa que puede faltar en este momento es el soporte para pendientes. Muchos juegos no dependen de ellos, pero muchos de ellos sí lo hacen, así que ese es el mayor objetivo de mejora de este sistema. Gracias por leer hasta ahora, ¡nos vemos en la próxima parte!

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