Minilop Retroware

MPAGD Avanzado

Vamos a añadir un ajuste de nivel de dificultad a nuestro juego

9 de octubre de 2021

Para alargar la vida útil de tu juego, a menudo es buena idea añadir distintos niveles de dificultad entre los que el jugador pueda elegir, o quizá quieras que las mecánicas de tu juego se vuelvan progresivamente más difíciles cuanto más avance el jugador.

Captura de pantalla del tutorial

Añadir un nivel de dificultad era algo que quería conseguir en mi último juego, The Swarm is Coming... , y aquí te cuento cómo lo hice...

1) Crea una variable

Vamos a necesitar una variable global que podamos usar para guardar el nivel de dificultad; en este caso usaremos D de Difficulty

2) Crea un MENU para seleccionar la dificultad

En el evento INTRO MENU usaremos el comando MENU de MPAGD para darle al jugador un selector de nivel de dificultad:

LET D = 5
AT 10 4
PRINT "Choose Your Difficulty"
MENU " VERY EASY, EASY, HARD, VERY HARD "
SUBTRACT OPT FROM D              ; D NOW CONTAINS THE DIFFICULTY 5= VERY EASY, 4= EASY, 3= HARD 2= VERY HARD

Así, en el código anterior guardamos inicialmente en D el valor de dificultad más fácil (5). Después el jugador elige su preferencia en un menú. Su elección fijará un valor en OPT (el primer elemento del menú es 0, luego 1, 2, 3); al restar OPT de D tendremos un valor de dificultad guardado en D, donde:

5 = muy fácil

4 = fácil

3 = difícil

2 = muy difícil

3) Añade un temporizador

Probablemente te suene el código básico de temporizador que usan muchos juegos de MPAGD en el MAIN LOOP; suele ser algo así:

ADD 1 to A

IF A = 2

LET A = 0

ENDIF

Esto guardará en A, en cada momento, un 1 o un 0. Podemos usarlo para ralentizar cosas en el juego; por ejemplo, en el evento de un sprite enemigo podríamos hacer algo así:

IF A = 0

{mover al enemigo}

ENDIF

En ese caso, el enemigo solo se movería un ciclo de cada dos (cuando A = 0), es decir, a la mitad de su velocidad máxima.

Podemos adaptar este concepto a nuestro ajuste de dificultad.

Al principio de MAIN LOOP 1 añadiremos:

ADD 1 TO A                           ; Increment the timer
IF A = D                             ; will reset A when it reaches the difficulty level (D)
   LET A = 0
ENDIF

Así, según el nivel de dificultad, A guardará un valor de 0 a (valor de dificultad menos 1).

Por tanto, en el nivel MUY FÁCIL de arriba, el valor de D es 5, así que A recorrerá los valores 0,1,2,3,4

Mientras que en el nivel MUY DIFÍCIL, el valor de D es 2, así que A solo recorrerá los valores 0 y 1

Enseguida vemos cómo esto podría afectar a la velocidad de nuestros enemigos...

4) Usar el ajuste de dificultad

a) Ejemplo: velocidad del juego

Recuerda nuestro ejemplo anterior:

IF A = 0

{mover al enemigo}

ENDIF

En nuestro nivel de dificultad MUY FÁCIL, el enemigo solo se moverá una vez cada 5 ciclos, mientras que en MUY DIFÍCIL se moverá en cada ciclo.

b) Ejemplo: daño

También podemos usar nuestro nivel de dificultad para determinar cosas como el daño. Por ejemplo, en tu juego el jugador puede tener un valor de salud, guardado en la variable H con un máximo de 100.

Cuando el jugador choca con un enemigo, quizá tú:

SUBTRACT 10 FROM H

Pero podríamos, en cambio, usar nuestro ajuste de dificultad para determinar la cantidad de daño. Podríamos escribir un gran IF anidado (IF D = 5 SUBTRACT 20 FROM H ELSE IF D = 4...), pero resulta un poco engorroso; en su lugar haremos un cálculo rápido y 'tomaremos prestada' la variable RND como variable temporal. En el evento del enemigo podríamos hacer algo así:

IF COLLISION PLAYER
    LET RND = 10              ; temporarily store 10 in RND
    SUBTRACT D FROM RND       ; subtract difficulty value from 10 to get damage
    SUBTRACT RND FROM H       ; apply damage to player health
ENDIF

Así, en nuestro nivel muy difícil restamos 2 a 10 para obtener 8 puntos de daño, que después se restan del contador de salud del jugador (H)

Pero si estamos en el nivel MUY FÁCIL, solo causa 10 - 5 = 5 puntos de daño.

Por supuesto, ¡puedes crear tus propias reglas de daño para infligir la cantidad de dolor que necesites!

c) Ejemplo: frecuencias

Otro caso de uso de tus niveles de dificultad podría ser la frecuencia con la que ocurre algo, quizá tengas un sprite de bonificación que aparece de vez en cuando: puedes usar el ajuste D para que aparezca con más frecuencia en los niveles fáciles que en los difíciles

d) Ejemplo: límites de tiempo

Añadir un límite de tiempo a tu juego, ya sea para completar el juego entero o una sola pantalla, es una oportunidad ideal para usar tus niveles de dificultad y hacer el juego más (o menos) desafiante según el nivel que elija el jugador. Es algo que implementé en The Swarm is Coming... cada misión debe completarse en un tiempo determinado, y cuanto más fácil es el nivel, más tiempo tienes. El código que lo hace es más o menos así:

LET S = 0                 ; seconds
LET M = D                 ; set the number of minutes to the value of the difficulty level
MULTIPLY M BY 10          ; multiply minutes by 10
ADD 10 TO M               ; and allow an extra 10 minutes

Esto te dará un número de minutos (M) y segundos (S) que el jugador tiene para completar el juego. Después necesitamos añadir un mecanismo de cuenta atrás en el MAIN LOOP que vaya decrementando el temporizador:

Para ello necesitamos un temporizador que nos dé un segundo; dado que MPAGD funciona a 25 fps, podríamos añadir una variable (T) en la inicialización (LET T = 0) y luego, en el MAIN LOOP:

ADD 1 TO T                     ; increment the counter
IF T = 25                      ; t counts in 1/25s so when it hits 25 = 1 second
   LET T = 0                   ; reset counter
   SUBTRACT 1 FROM S           ; decrement the seconds
   IF S = 0                    ; are seconds at 0?
      IF M = 0                 ; if minutes also at zero
         KILL                  ; out of time!
         EXIT
      ENDIF
      SUBTRACT 1 FROM M        ; decrement the minutes left
      LET S = 59               ; reset the seconds
   ENDIF
ENDIF

e) Ejemplo: puntuaciones & bonificaciones

También puedes usar tu nivel de dificultad para recompensar los logros del jugador cuando juega en los ajustes más difíciles. Quizá usando los comandos SCORE & BONUS de MPAGD

Por ejemplo, quizá cuando se mata a un enemigo:

LET RND = 10                ; temporarily store 10 in RND
DIVIDE RND BY D             ; divide by  Difficulty level
MULTIPLY RND BY 20          ; now multiply it by 20
SCORE RND                   ; and score that many points

De nuevo, primero dividimos a partir de un número dado: en nuestro ejemplo el nivel más difícil tiene el valor de D más bajo, lo que nos da un multiplicador por el que multiplicamos los puntos (20), y entonces sumamos esa cantidad a la puntuación.

Resumen

Los niveles de dificultad son muy fáciles de conseguir en MPAGD y, con un poco de cuidado al programar, no requieren mucha memoria, pero pueden mejorar notablemente la jugabilidad y la duración de tus juegos. En mi ejemplo, el valor de D nunca cambia: una vez que lo fija el jugador, se mantiene constante durante todo el juego. Si tu mecánica exige manipularlo, recuerda restablecerlo inmediatamente a su valor original después de hacerlo... ¡o pueden pasar cosas inesperadas! Yo prefiero usar una variable temporal como RND si necesito hacer cuentas con D.

Todo MPAGD Avanzado