43. Añadiendo gravedad.

Si nuestro personaje está "expuesto a las leyes de la gravedad", debería "caer" cuando no haya suelo bajo sus pies...

Aparentemente, la acción de "caer" se parecerá mucho a la de "mover hacia abajo" de nuestro personaje, pero hay un par de diferencias, una de las cuales es poco importante y otra que lo es bastante más:

  • Por una parte, es habitual que el personaje "cambie de fotograma" cuando está andando, pero no cuando cae.
  • Por otra parte (y esto sí es importante), el personaje baja cuando pulsamos cierta tecla (o movemos el joystick/gamepad), pero si está cayendo, debe seguir haciéndolo aunque nosotros no hagamos nada.

Por lo tanto, podríamos crear un método "caer" que hiciera que el personaje bajase pero sin cambiar de fotograma:

public void Caer()
{
    y += incrY;
}

Pero este método podemos necesitar llamarlo de forma repetitiva. De igual modo que el "enemigo" tenía un método "mover" que se encargaba de su movimiento automático, vamos a crear un "mover" que gestione los movimientos que tenga que hacer nuestro personaje incluso cuando nosotros no toquemos ninguna tecla: caer y, dentro de poco, saltar.

public void Mover()
{
    if (miPartida.EsPosibleMover(x,y+incrY,
            x+ancho,y+alto+incrY))
        Caer();
}

Este "mover" deberá ser llamado desde la "partida", en concreto desde la función encargada de "mover elementos":

public void MoverElementos()
{
    // -- Mover enemigos, entorno --
    for (int i = 0; i < miNivel.GetNumEnemigos(); i++)
        miNivel.GetEnemigo(i).Mover();
 
    miPersonaje.Mover();
}

Y ya sólo quedan pequeños retoques como ese "EsPosibleMover" de partida, para que el personaje (o incluso los enemigos) puedan saber si es posible desplazarse a cierta posición o hay algún obstáculo que lo impide).


¿Y de dónde sale ese "miPartida" que aparece en "Personaje"? Como la partida contiene al personaje, puede acceder a sus datos y sus métodos (que sean públicos), pero no al contrario: el personaje no sabe "a qué partida pertenece". Podemos solucionarlo creando un atributo "miPartida", al que se dará valor con una función "IndicarPartida" del personaje o bien desde su constructor:

public class Personaje : ElemGrafico
{
    private Partida miPartida;
 
    public Personaje(Partida p)
        : base(4, 32, 23)  // Elemento gráfico con 4 direcciones
    {
        miPartida = p;
        ...
 

Y la partida le daría valor inicial cuando crea al personaje:

public Partida()
{
    miMarcador = new Marcador();
    miPersonaje = new Personaje(this);
    miNivel = new Nivel();
}

Como hemos hecho varias ampliaciones de una cierta complejidad, puedes descargar una versión actualizada del juego completo, incluyendo fuentes, proyecto, imágenes y ejecutable, para ver la apariencia de los fuentes completos y poder probar el resultado con facilidad.

Ejercicio propuesto: Mejora esta estructura, para que el personaje no se pueda mover hacia los lados mientras cae.