Minilop Retroware

MPAGD Avançado

Vamos adicionar uma definição de nível de dificuldade ao nosso jogo

9 de outubro de 2021

Para aumentar a longevidade do teu jogo, muitas vezes é boa ideia adicionar diferentes níveis de dificuldade entre os quais o jogador possa escolher, ou talvez queiras que as mecânicas do teu jogo se tornem progressivamente mais difíceis quanto mais o jogador avançar.

Captura de ecrã do tutorial

Adicionar um nível de dificuldade era algo que queria conseguir no meu último jogo, The Swarm is Coming... , e aqui está como o fiz...

1) Cria uma variável

Vamos precisar de uma variável global que possamos usar para guardar o nível de dificuldade; neste caso, usaremos D de Difficulty

2) Cria um MENU para selecionar a dificuldade

No evento INTRO MENU, vamos usar o comando MENU do MPAGD para dar ao jogador um seletor de nível de dificuldade:

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

Assim, no código acima, guardamos inicialmente em D o valor de dificuldade mais fácil (5). Depois o jogador escolhe a sua preferência num menu. A escolha define um valor em OPT (o primeiro item do menu é 0, depois 1, 2, 3); ao subtrair OPT a D, ficamos com um valor de dificuldade guardado em D, onde:

5 = muito fácil

4 = fácil

3 = difícil

2 = muito difícil

3) Adiciona um temporizador

Provavelmente já conheces o código básico de temporizador que muitos jogos MPAGD usam no MAIN LOOP; costuma ser algo assim:

ADD 1 to A

IF A = 2

LET A = 0

ENDIF

Isto vai guardar em A, a cada momento, um 1 ou um 0. Podemos usar isto para abrandar coisas no jogo; por exemplo, no evento de um sprite inimigo podíamos fazer algo assim:

IF A = 0

{mover o inimigo}

ENDIF

Nesse caso, o inimigo só se moveria de dois em dois ciclos (quando A = 0), ou seja, a metade da sua velocidade máxima.

Podemos adaptar este conceito à nossa definição de dificuldade.

No início de MAIN LOOP 1 vamos adicionar:

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

Assim, consoante o nível de dificuldade, A guardará um valor de 0 a (valor de dificuldade menos 1).

Assim, no nível MUITO FÁCIL acima, o valor de D é 5, pelo que A percorrerá os valores 0,1,2,3,4

Já no nível MUITO DIFÍCIL, o valor de D é 2, pelo que A só percorrerá os valores 0 e 1

De imediato, vemos como isto pode afetar a velocidade dos nossos inimigos...

4) Usar a definição de dificuldade

a) Exemplo: velocidade do jogo

Recorda o nosso exemplo anterior:

IF A = 0

{mover o inimigo}

ENDIF

No nosso nível de dificuldade MUITO FÁCIL, o inimigo só se move uma vez a cada 5 ciclos, enquanto no MUITO DIFÍCIL se move em todos os ciclos.

b) Exemplo: dano

Também podemos usar o nosso nível de dificuldade para determinar coisas como o dano. Por exemplo, no teu jogo o jogador pode ter um valor de saúde, guardado na variável H com um máximo de 100.

Quando o jogador colide com um inimigo, talvez tu:

SUBTRACT 10 FROM H

Mas podíamos, em vez disso, usar a nossa definição de dificuldade para determinar a quantidade de dano. Podíamos escrever um grande IF aninhado (IF D = 5 SUBTRACT 20 FROM H ELSE IF D = 4...), mas isso é um pouco trabalhoso; em vez disso, vamos fazer uma conta rápida e 'pedir emprestada' a variável RND como variável temporária. No evento do inimigo podíamos fazer algo assim:

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

Assim, no nosso nível muito difícil subtraímos 2 a 10 para obter 8 pontos de dano, que depois são subtraídos ao contador de saúde do jogador (H)

Mas se estivermos no nível MUITO FÁCIL, só causa 10 - 5 = 5 pontos de dano.

Claro que podes criar as tuas próprias regras de dano para infligir a dose de dor necessária!

c) Exemplo: frequências

Outra utilização para os teus níveis de dificuldade pode ser a frequência com que algo acontece, talvez tenhas um sprite de bónus que aparece de vez em quando: podes usar a definição D para que apareça com mais frequência nos níveis fáceis do que nos difíceis

d) Exemplo: limites de tempo

Adicionar um limite de tempo ao teu jogo, seja para completar o jogo inteiro ou um único ecrã, é uma oportunidade ideal para usar os teus níveis de dificuldade e tornar o jogo mais (ou menos) desafiante consoante o nível escolhido pelo jogador. Foi algo que implementei em The Swarm is Coming... cada missão tem de ser concluída num tempo definido, e quanto mais fácil o nível, mais tempo se tem. O código por trás disto é mais ou menos assim:

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

Isto dá-te um número de minutos (M) e segundos (S) que o jogador tem para completar o jogo. A seguir, precisamos de adicionar um mecanismo de contagem decrescente no MAIN LOOP que decremente o temporizador:

Para isso precisamos de um temporizador que nos dê um segundo; como o MPAGD funciona a 25 fps, podemos adicionar uma variável (T) na inicialização (LET T = 0) e depois, no 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) Exemplo: pontuações & bónus

Também podes usar o teu nível de dificuldade para recompensar as conquistas do jogador quando joga nas definições mais difíceis. Talvez com os comandos SCORE & BONUS do MPAGD

Por exemplo, talvez quando um inimigo é morto:

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

Mais uma vez, primeiro dividimos a partir de um número dado: no nosso exemplo o nível de dificuldade mais difícil tem o valor de D mais baixo, o que nos dá um multiplicador pelo qual multiplicamos os pontos (20), e depois pontuamos essa quantidade.

Resumo

Os níveis de dificuldade são muito fáceis de conseguir no MPAGD e, com algum cuidado a programar, não exigem muita memória, mas podem melhorar bastante a jogabilidade e a longevidade dos teus jogos. No meu exemplo, o valor de D nunca muda: depois de definido pelo jogador, mantém-se constante durante todo o jogo. Se a tua mecânica exigir que o manipules, lembra-te de o repor imediatamente no valor original depois de o manipulares... senão podem acontecer coisas inesperadas! Eu prefiro usar uma variável temporária como RND se precisar de fazer contas com D.

Todo o MPAGD Avançado