Minilop Retroware

MPAGD Iniciantes · Parte 29 de 29

Vamos criar um jogo com MPAGD, Parte 29: Adicionar um temporizador/contagem decrescente ao teu jogo

7 de novembro de 2021

Imagina que no teu jogo queres dar ao jogador um tempo fixo para completar cada ecrã. E depois, talvez, dar-lhe um bónus consoante o tempo que lhe sobrar quando cumprir com sucesso os objetivos dos níveis.

Como faríamos isso?

Primeiro, o MPAGD funciona a 25 frames por segundo, por isso deve ser bastante simples criar um mecanismo de temporização que conte segundos.

Segundo, vamos precisar de algumas variáveis para guardar (e mostrar) a quantidade atual de minutos/segundos restantes.

Terceiro, talvez queiramos mudar o tempo permitido ecrã a ecrã, ou talvez usar níveis de dificuldade para alterar o tempo permitido.

MÉTODO 1: Simples: o mesmo tempo em cada ecrã

Vamos começar com uma versão simples, em que cada ecrã tem uma quantidade fixa de segundos e minutos para ser completado; se o temporizador chegar a 0, o jogador morre.

Bem, o MPAGD funciona a 25 frames por segundo. Assim, se tivermos uma variável que é incrementada uma vez por frame, quando chegar a 25 teremos cronometrado um segundo.

Vamos usar a variável G como o nosso tempo geral, contando 1/25 de segundo.

Vamos também precisar de variáveis que guardem o número de minutos e segundos permitidos por ecrã:

M = Minutos

S = Segundos.

Vamos inicializar os valores destas três variáveis no nosso evento RESTART SCREEN; assim, serão repostos sempre que começarmos um ecrã, incluindo depois de uma morte. No teu evento restart screen vais adicionar:

LET G = 0         ; reset the 1/25s timer
LET M = 1         ; countdown minutes allowed
LET S = 0         ; countdown seconds allowed

A seguir, vamos acrescentar código que faz e mostra a contagem decrescente. Para garantir que é executado de forma consistente durante todo o jogo, vamos colocá-lo no evento MAINLOOP 1, para que seja executado no início de cada frame:

; COUNTDOWN TIMER
;================

ADD 1 TO G               ; increment the general timer
IF G = 25                ; when it reaches 25
   LET G = 0             ; reset it
   SUBTRACT 1 FROM S     ; decrement seconds remaining
ENDIF

IF S = 0                 ; if 0 seconds
   IF M = 0              ; and 0 minutes left
      KILL               ; kill the player
      EXIT               ; exit processing this event
   ENDIF
   SUBTRACT 1 FROM M     ; decrement the minutes
   LET S = 59            ; reset the seconds
ENDIF

COLOUR 71                ; white text on black (zx spectrum)                 
AT 22 26
DISPLAY DOUBLEDIGITS M   ; display minutes remaining
CHR 58                   ; ':'
DISPLAY DOUBLEDIGITS S   ; display seconds remaining

Assim, incrementamos G; quando chega a 25 repomos a 0 e decrementamos o número de segundos (S); quando S chega a 0 repomos a 59 e decrementamos o número de minutos, a não ser que já não haja minutos, caso em que o tempo acabou e matamos o jogador. É assim que deve ficar...

MÉTODO 2: Avançado: tempo diferente por ecrã

Se quisermos alterar o tempo disponível, só temos de mudar o código de inicialização de variáveis do nosso evento RESTART SCREEN para usar uma tabela DATA e fazer READ dos valores de Minutos (M) e Segundos (S)

Podemos usar isto para a inicialização de variáveis em RESTART SCREEN

LET G = 0           ; reset the 1/25s timer
RESTORE             ; restore the data table so we read from the start
READ M              ; read the 1st data and put it in M
READ S              ; read 2nd value put it in S
IF SCREEN >= 1      ; if it is screen 1 or above
   REPEAT SCREEN    ; then repeat SCREEN-many times
   READ M           ; read the next value for M
   READ S           ; and the next value for S
   ENDREPEAT        ; so we've found the right M & S values for the Screen
ENDIF

Depois vamos adicionar isto como a nossa tabela DATA; os valores alternam entre minutos e segundos, por isso o tempo permitido no SCREEN 0 é 0 minutos e 30 segundos (os 2 primeiros valores), para o SCREEN 1 é 1 minuto e 0 segundos (valores 3 e 4), para o Screen 2 é 1 minuto e 15 segundos (valores 5 e 6), e assim sucessivamente.

DATA 0 30 1 0 1 15 2 0 1 45 1 30 1 0 1 30 

Adicionar um bónus por completar o ecrã

Tudo o que fizemos até agora serve para matar o jogador se o tempo acabar, mas e se ele cumprir os objetivos do ecrã? Poderíamos acrescentar um bónus com base no tempo restante. Dependendo do que ativa a conclusão do ecrã (no Stinky Dog é apanhar todos os ossos), vais ter de adicionar a tua rotina de bónus no evento correto. Pode ser algo como isto:

   IF M > 0             ; if there are minutes left
      REPEAT M          ; repeat for each minute remaining
	 REPEAT 60      ; repeat 60 times (60 sec)
	    BONUS 10    ; increase bonus by 10
	 ENDREPEAT
      ENDREPEAT
   ENDIF
   IF S > 0             ; if there are seconds left
      REPEAT S          ; repeat for each second
	 BONUS 10       ; increase the bonus by 10
      ENDREPEAT
   ENDIF
   ADDBONUS             ; add the bonus to the score
   ZEROBONUS            ; reset bonus to zero

Aplicar isto ao nosso jogo Stinky Dog

No nosso jogo Stinky Dog, no entanto, há algumas alterações que quero fazer.

Primeiro, já temos uma tabela DATA em RESTART SCREEN 0, que guarda o número de ossos que têm de ser apanhados por ecrã. Sem problema, o MPAGD não quer saber que dados guarda na tabela DATA, por isso só temos de incluir os valores dos ossos e fazer READ três vezes por ciclo para obter o número correto de ossos, minutos e segundos.

LET G = 0           ; reset the 1/25s timer
RESTORE             ; restore the data table so we read from the start
READ M              ; read the 1st data and put it in M
READ S              ; read 2nd value put it in S
READ B              ; read third value and put it in B for Bones
IF SCREEN >= 1      ; if it is screen 1 or above
   REPEAT SCREEN    ; then repeat SCREEN-many times
   READ M           ; read the next value for M
   READ S           ; and the next value for S
   READ B           ; and the next values for B
   ENDREPEAT        ; we've found the right M, S & B values for the Screen
ENDIF

E a tabela DATA no fim de RESTART SCREEN:

DATA 0 30 6 1 0 7 1 15 3 2 0 4 1 45 6 1 
DATA 30 4 1 0 8 1 30 6 

A seguir, como já criámos uma rotina de morte reutilizável que chamamos ao definir o valor SETTINGA do Stinky Dog como 99, vou mover o código do temporizador de contagem decrescente de MAINLOOP 1 para o evento do jogador, remover o comando KILL e mudá-lo para LET SETTINGA = 99

NOTA: Coloca o código DEPOIS da tua rotina IF SETTINGA = 99 ....ENDIF

; COUNTDOWN TIMER
;================

ADD 1 TO G               ; increment the general timer
IF G = 25                ; when it reaches 25
   LET G = 0             ; reset it
   SUBTRACT 1 FROM S     ; decrement seconds remaining
ENDIF

IF S = 0                 ; if 0 seconds
   IF M = 0              ; and 0 minutes left
      LET SETTINGA = 99  ; kill the player
      EXIT               ; exit processing this event
   ENDIF
   SUBTRACT 1 FROM M     ; decrement the minutes
   LET S = 59            ; reset the seconds
ENDIF

COLOUR 71                ; white text on black (zx spectrum)                 
AT 22 26
DISPLAY DOUBLEDIGITS M   ; display minutes remaining
CHR 58                   ; ':'
DISPLAY DOUBLEDIGITS S   ; display seconds remaining


Aqui está em acção quando ficamos sem tempo

Para adicionar uma nova bonificação de tempo ao completar o ecrã, vamos ter de actualizar o nosso evento COLLECT BLOCK:

EVENT COLLECTBLOCK

SCORE 100                  	; score 100 points when we collect a Bone
AT 0 6 
COLOUR 71
SHOWSCORE			; display the score

SUBTRACT 1 FROM B	; decrement the number of bones left on screen
IF B = 0                ; have all Bones been collected?
   CLS                  ; clear the screen
   AT 4 6
   MESSAGE 1            ; "well done"
   CHR 32
   MESSAGE 0            ; "stinky dog"
   CHR 33
   DELAY 25             ; wait another second
   COLOUR 65            ; bright blue on balck
   AT 7 7
   MESSAGE  2           ; "score"
   CHR 32               ; <space>
   DELAY 25             ; wait another second
   SHOWSCORE
   DELAY 25             ; wait another second
   AT 9 2
   COLOUR 70            ;  bright yellow on black
   MESSAGE 3            ; "fart"
   CHR 32               ; <space>
   MESSAGE 4            ; "bonus"
   CHR 32               ; <space>
   DELAY 25             ; wait another second
   SHOWBONUS            ; display the bonus
   ADDBONUS             ; add the bonus to the score
   ZEROBONUS            ; reset the bonus to 0 now we've added it
   DELAY 25             ; wait another second
   COLOUR 67            ; bright magenta on black
   AT 11 2
   MESSAGE 7            ; "time"
   CHR 32               ; <space>
   MESSAGE 4            ; "bonus"
   CHR 32               ; <space>
   DELAY 25             ; wait another second
   IF M > 0             ; if there are minutes left
      REPEAT M          ; repeat for each minute remaining
	 REPEAT 60      ; repeat 60 times (60 sec)
	    BONUS 10       ; increase bonus by 10
	 ENDREPEAT
      ENDREPEAT
   ENDIF
   IF S > 0             ; if there are seconds left
      REPEAT S          ; repeat for each second
	 BONUS 10       ; increase the bonus by 10
      ENDREPEAT
   ENDIF
   SHOWBONUS
   ADDBONUS             ; add the bonus to the score
   ZEROBONUS            ; reset bonus to zero  
   AT 13 5                
   COLOUR 71
   MESSAGE 8            ; "total"
   CHR 32
   CHR 61
   CHR 32 
   SHOWSCORE            ; display the score
   DELAY 25             ; wait 1 second

   AT 17 8              ; at line 15 column 8
   COLOUR 215
   MESSAGE 5            ; "press key to start"
   WAITKEY              ; wait for a key press
   NEXTLEVEL            ; if so move to the next screen
ENDIF

Repara que adicionei duas mensagens novas aos TEXT MESSAGES:

DEFINEMESSAGES
"STINKY DOG"          ;0
"WELL DONE"           ;1
"SCORE"               ;2
"FART"                ;3
"BONUS"               ;4
"PRESS KEY TO START"  ;5
"LIVES"               ;6
"TIME"                ;7 
"TOTAL"               ;8

Aqui está tudo a funcionar às mil maravilhas!

Todo o MPAGD Iniciantes