Curso de Basic
Teclado e Joystick
Você está em: MarMSX >> Cursos >> BASIC
Este capítulo tem como objetivo mostrar como controlar ações na tela e eventos do usuário provenientes do teclado ou joystick.
Principais comandos de controle em Basic:
Instrução |
Periférico |
INPUT$(n) |
Teclado |
INKEY$ |
Teclado |
STICK(n) |
Teclado e Joystick |
STRIG(n) |
Teclado e Joystick |
Ações e Eventos
Ações
Uma ação é algo que o programa controla, e que deverá estar constantemente em execução para que seu estado possa ser constantemente atualizado.
Para que uma ação ou mais ações sejam constantemente executadas, elas deverão estar dentro de um loop. Dessa forma, enquanto o programa estiver preso nesse loop, todas as ações em seu interior serão executadas, salvo haja alguma condição de não execução (IFs).
Ilustrando:
10 SCREEN 0
20 FOR F=1 TO 10
30 PRINT"Pera" : ' Acao 1
40 PRINT"Maca" : ' Acao 2
50 NEXT F
60 END
O loop é temporário, ou seja, dura 10 iterações. Assim, a ação 1 e ação 2 serão executadas 10 vezes.
Vamos modificar o programa anterior:
10 SCREEN 0
20 A=0
30 PRINT"Pera" : ' Acao 1
40 IF A MOD 2 = 0 THEN PRINT"Maca" : ' Acao 2
50 A=A+1
60 GOTO 30
Nesse novo exemplo, o loop é permanente. Além disso, foi acrescentada um condição de execução para ação 2, onde ela executa somente quando o valor da variável "A" for um número par.
Nos próximos exemplos, a ação é fazer uma animação de um pássaro voando na tela.
Veja o primeiro exemplo com o pássaro.
10 SCREEN 2,1
20 FOR T=1 TO 8
30 READ A$
40 S$=S$+CHR$(VAL("&H"+A$))
50 NEXT T
60 SPRITE$(1)=S$
70 PUT SPRITE 0,(128,96),15,1
80 GOTO 80
90 DATA 10,70,92,D6,7C,10,00,00
Um pássaro é criado e mostrado na tela. Entretanto, o programa não possui qualquer atualização da posição do pássaro e ele fica parado na tela.
Ao acrescentarmos as seguintes linhas ao programa e modificar a linha 70, o pássaro ganhará movimento. Isto por que criamos um loop temporário com o laço FOR, que atualizará a cada iteração a posição do pássaro.
65 FOR X=0 TO 255
70 PUT SPRITE 0,(X,96),15,1
75 NEXT X
Observe que a ação dura enquanto o loop durar (linhas 65-75).
Uma ação está sempre relacionada a um objeto, como por exemplo, o pássaro do programa anterior. Esse objeto possui determinadas características que podem ser modificadas ao longo do tempo.
No exemplo anterior, a coordenada X do pássaro é constantemente modificada enquanto dura o loop. Podemos também modificar a forma do desenho do pássaro durante o seu vôo. Veja o exemplo a seguir.
10 SCREEN 2,1
20 SE=-1 : X=128 : Y=96 : P=0
30 FOR F=1 TO 4
40 FOR T=1 TO 8
50 READ A$
60 S$=S$+CHR$(VAL("&H"+A$))
70 NEXT T
80 SPRITE$(F)=S$ : S$=""
90 NEXT F
100 IF X MOD 4 = 0 THEN P=(P+1) MOD 2
110 IF SE=-1 THEN S=P+1 ELSE S=P+3
120 PUT SPRITE 0,(X,96),15,S
130 X=X+SE
140 IF X<0 OR X>240 THEN SE=-SE
150 GOTO 100
160 DATA 10,70,92,D6,7C,10,00,00
170 DATA 10,70,10,10,7C,92,00,00
180 DATA 10,1C,92,D6,7C,10,00,00
190 DATA 10,1C,10,10,7C,92,00,00
Obs: o loop temporário foi substituído por um loop permanente. Assim, o pássaro irá voar eternamente pela tela.
Podemos acrescentar mais pássaros na tela. Entretanto, quanto mais ações controlarmos ao mesmo tempo, mais lento se torna o processamento delas.
10 SCREEN 2,1
20 NP=3 : DIM P(NP) : DIM CO(NP,2) : DIM SE(NP)
30 FOR F=1 TO NP
40 CO(F,1)=INT(RND(-TIME)*240)
50 CO(F,2)=INT(RND(-TIME)*180)
60 SE(F)=INT(RND(-TIME)*255):IF SE(F)>128 THEN SE(F)=1 ELSE SE(F)=-1
70 NEXT F
80 FOR F=1 TO 4
90 FOR T=1 TO 8
100 READ A$
110 S$=S$+CHR$(VAL("&H"+A$))
120 NEXT T
130 SPRITE$(F)=S$ : S$=""
140 NEXT F
150 FOR F=1 TO NP
160 X=CO(F,1):Y=CO(F,2)
170 IF X MOD 4 = 0 THEN P(F)=(P(F)+1) MOD 2
180 IF SE(F)=-1 THEN S=P(F)+1 ELSE S=P(F)+3
190 PUT SPRITE F,(X,Y),15,S
200 X=X+SE(F) : CO(F,1)=X
210 IF X<0 OR X>240 THEN SE(F)=-SE(F)
220 NEXT F
230 GOTO 150
240 DATA 10,70,92,D6,7C,10,00,00
250 DATA 10,70,10,10,7C,92,00,00
260 DATA 10,1C,92,D6,7C,10,00,00
270 DATA 10,1C,10,10,7C,92,00,00
Obs: o retângulo azul marca as 5 ações, enquanto que o retângulo vermelho marca o loop.
Experimente alterar o número de pássaros na variável NP da linha 20.
Note que cada pássaro deverá ter seus próprios dados, ou então estaremos cruzando informações. Para isso, foram criados vetores e matrizes para armazenar os dados de cada pássaro.
Assim como em um processamento paralelo, cada objeto deverá ter uma fatia de tempo pequena para processar as mudanças, dando a impressão de estarem todos se movendo ao mesmo tempo. Se não fosse dessa maneira, uma ação longa de um único objeto iria fazer com que os demais objetos ficassem parados esperando essa ação terminar.
De maneira análoga, não podemos acrescentar uma música de fundo a uma animação e dar um comando para tocar várias notas de uma vez, como é feito normalmente em uma reprodução de música. Portanto, devemos dividir a música em notas e tocar cada nota (ou um conjunto pequeno de notas) a cada iteração do loop de controle.
Agora, vamos acrescentar uma música [1] de fundo ao vôo do pássaro.
10 SCREEN 2,1 : PLAY "S0M10000V15T180"
20 SE=-1:X=128:Y=96:P=0:DIM M$(55):M=0
30 FOR F=1 TO 4
40 FOR T=1 TO 8
50 READ A$
60 S$=S$+CHR$(VAL("&H"+A$))
70 NEXT T
80 SPRITE$(F)=S$ : S$=""
90 NEXT F
95 FOR F=1 TO 55 : READ M$(F) : NEXT
100 IF X MOD 4 = 0 THEN P=(P+1) MOD 2
110 IF SE=-1 THEN S=P+1 ELSE S=P+3
120 PUT SPRITE 0,(X,96),15,S125 IF PEEK(&HHFB40) < 4 THEN PLAY M$(M) : M=M+1
126 IF M>55 THEN M=1130 X=X+SE
140 IF X<0 OR X>240 THEN SE=-SE150 GOTO 100
160 DATA 10,70,92,D6,7C,10,00,00
170 DATA 10,70,10,10,7C,92,00,00
180 DATA 10,1C,92,D6,7C,10,00,00
190 DATA 10,1C,10,10,7C,92,00,00
200 DATA "L4O3G","F","L8E","D","E","C","O2B","A","B","G"
210 DATA "O3L4C","E","A","G","B","O4L3CO3"
220 DATA "L8A","G","F","E","L3E","L4D","G","B","O4C"
230 DATA "L6C","E","L4D","L6D","F","L4E","C","F","E","D","E"
240 DATA "L6F","E","D","C","L4C","O3B"
250 DATA "O4L4C","C#","L3D","L6D","F","L4E"
260 DATA "L6E","G","L4F","D","O3G","B","O4C"
Obs 1: o retângulo vermelho marca o loop de controle das ações, o fundo amarelo marca os comandos relativos à ação do pássaro e o fundo verde marca os comandos relativos à ação da música.
Obs 2: na linha 125 é verificado se há menos de 4 comandos PLAY na fila (queue) do interpretador Basic para poder introduzir um novo comando. Se esta fila estourar, o interpretador Basic ficará aguardando a liberação dela e a animação ficará lenta.
Eventos
Um evento é uma informação externa que chega ao sistema, que por sua vez deverá dar uma resposta adequada a esse evento. Por exemplo, uma tecla pressionada pelo usuário faz com que o sistema dispare uma determinada ação.
No capítulo anterior, vimos que as interrupções tratam os eventos externos. Nesse caso, o interpretador Basic aguarda certo evento ocorrer e dispara automaticamente uma sub-rotina para tratar esse evento.
Nesse capítulo, vamos ver como responder aos eventos de teclado e joystick, que devem ser capturados e tratados pelo próprio programa, exceto o botão de tiro que pode ser tratado pela interrupção ON STRIG GOSUB.
Os eventos podem ocorrer simultaneamente com as ações, justamente como é feito nos jogos de ação. Para isso, o mesmo loop deverá controlar as ações e os eventos.
Um evento é capturado por uma função específica, que por sua vez armazena em uma variável o valor enviado por esse evento. Dessa forma, o programa estará apto a perceber a mudança do valor dessa variável e disparar uma determinada ação.
Sintaxe:
DADO = EVENTO(X)
Ao ocorrer o EVENTO(X), a variável DADO recebe a informação enviada por um evento. No caso de um teclado por exemplo, EVENTO(X) percebe o clique do teclado, enquanto que a variável DADO recebe o código da tecla pressionada.
Comandos em Basic para tratamento de eventos do usuário
INPUT$(n)
Lê n caracteres do teclado.
Essa função espera até que os n caracteres sejam pressionados.
No modo texto, o cursor é apresentado.
Exemplo
10 PRINT"Digite sua senha (6 digitos): ";
20 A$=INPUT$(6)
30 PRINT
40 IF A$="karate" THEN PRINT"Acesso permitido"
ELSE PRINT"Acesso negado"
Um bom exemplo do uso do INPUT$ é esperar que o usuário pressione alguma tecla enquanto lê uma mensagem na tela.
10 SCREEN 0
20 PRINT"Temperatura do dia: 25 graus."
30 PRINT"Previsao do tempo: claro."
40 PRINT:PRINT"Tecle algo ..."
50 A$=INPUT$(1)
60 CLS
70 PRINT"Fim da consulta."
A função INPUT$ é utilizada para capturar teclas e tratar eventos do usuário. Exemplo:
10 A$=INPUT$(1):A=ASC(A$)
20 IF A=28 THEN PRINT "Para a direita"
30 IF A=29 THEN PRINT "Para a esquerda"
40 IF A=30 THEN PRINT "Para cima"
50 IF A=31 THEN PRINT "Para baixo"
60 IF A=13 THEN END
70 GOTO 10
INKEY$
Obtém o caractere que está sendo pressionado, exceto as teclas CONTROL, STOP e SHIFT.
Essa função NÃO espera até que um caractere seja pressionado como na função INPUT$. Ela capta o estado da tecla no instante que a instrução é lida. Caso nenhuma tecla esteja sendo pressionada, um caractere nulo é retornado.
Para ler um caractere diferente de nulo, o comando INKEY$ deverá estar em um loop aguardando até que um caractere válido seja pressionado.
Um bom exemplo do uso do INKEY$ é esperar que o usuário pressione alguma tecla enquanto lê uma mensagem na tela.
10 SCREEN 0
20 PRINT"Temperatura do dia: 25 graus."
30 PRINT"Previsao do tempo: claro."
40 PRINT:PRINT"Tecle algo ..."
50 IF INKEY$="" THEN 50
60 CLS
70 PRINT"Fim da consulta."
A execução do programa fica em loop na linha 50 aguardando que o usuário tecle algo.
Para realizar o mesmo que o INPUT$, com a vantagem de não aparecer o cursor em uma tela no modo texto, fazemos:
10 A$=INKEY$ : IF A$="" THEN 10
20 PRINT "Codigo do caractere pressionado:";ASC(A$)
Quando esperamos determinados eventos do usuário, como por exemplo as teclas direcionais, podemos colocar o tratamento do evento dentro do loop junto com a função INKEY$.
A vantagem disso é que o loop poderá realizar outras ações enquanto aguarda o usuário teclar algo.
10 A$=INKEY$
20 IF A$=CHR$(28) THEN PRINT "Para a direita"
30 IF A$=CHR$(29) THEN PRINT "Para a esquerda"
40 IF A$=CHR$(30) THEN PRINT "Para cima"
50 IF A$=CHR$(31) THEN PRINT "Para baixo"
60 IF A$=CHR$(13) THEN END
70 GOTO 10
Devemos fazer dessa forma, uma vez que a função ASC(A$) irá retornar um erro quando A$ for nulo (nenhuma tecla pressionada).
Observe que a tecla ENTER abandona o loop.
Agora, vamos adicionar uma ação. Será criado um texto que fica deslizando na tela, que mudará de cor quando o usuário pressionar a barra de espaços.
10 SCREEN 0:WIDTH 40:COLOR 15,1
20 M$=SPACE$(10)+"Exemplo de animacao"+SPACE$(10)
30 P=1:C=15:L=LEN(M$)
40 LOCATE 0,20:PRINT"Pressione a barra de espacos"
50 A$=INKEY$
60 IF A$=CHR$(32) THEN C=C+1:IF C>15 THEN C=2
70 COLOR C
80 LOCATE 0,0 : PRINT MID$(M$,P,10)
90 P=P+1:IF P>L-10 THEN P=1
100 FOR T=1 TO 50:NEXT T
110 GOTO 50
A animação é contínua, mesmo sem o usuário realizar qualquer ação. E quando o usuário interage com o programa, a cor do texto muda e a animação segue firme.
Observe que isso não é possível com o uso da função INPUT$, uma vez que o programa pára esperando até que o usuário tecle algo.
STICK(n)
As funções INPUT$ e INKEY$ se limitam ao teclado do MSX. Além disso, elas são capazes de capturar apenas uma tecla por vez.
A função STICK tem como objetivo mostrar qual a tecla direcional está sendo pressionada do teclado ou joystick, até mesmo as diagonais.
Assim como a função INKEY$, a função STICK NÃO pára a execução do programa a espera de uma tecla direcional ser pressionada. Dessa forma, também devemos ler o estado do teclado/joystick dentro de um loop.
O valor de n em STICK(n) é:
- Teclado
- Joystick A
- Joystick B
Os valores retornados são:
8 1 2
↖ ↑ ↗
7 ← 0 → 3
↙ ↓ ↘
6 5 4
O programa a seguir reproduz o antigo brinquedo "Traço Mágico".
10 COLOR1,14:SCREEN 2
20 X=128:Y=95
30 PSET(X,Y),1
40 C=STICK(0)
50 IF C=1 THEN Y=Y-1
60 IF C=2 THEN Y=Y-1:X=X+1
70 IF C=3 THEN X=X+1
80 IF C=4 THEN Y=Y+1:X=X+1
90 IF C=5 THEN Y=Y+1
100 IF C=6 THEN Y=Y+1:X=X-1
110 IF C=7 THEN X=X-1
120 IF C=8 THEN Y=Y-1:X=X-1
130 GOTO 30
Obs: altere o valor de STICK na linha 40 para 1 ou 2, e use o joystick A ou B, respectivamente.
Assim como visto no INKEY$, podemos controlar outras ações enquanto aguardamos o usuário interagir.
STRIG(n)
Essa função tem como objetivo ler o estado da barra de espaços ou de um dos botões de um dos joysticks.
Ela se diferencia da instrução ON STRIG GOSUB por ter que ser executada sempre que se deseja saber o estado dos botões. Conforme visto no capítulo anterior, a instrução ON STRIG GOSUB é declarada uma vez e sempre que o botão é disparado, o programa desvia automaticamente para uma sub-rotina.
Exemplo:
10 SCREEN 0
20 PRINT"Pressione a barra de espacos ..."
30 IF STRIG(0)=0 THEN 30
40 PRINT"Poooow !!"
Um pequeno jogo de demonstração
O jogo a seguir desenha um canhão, uma nave inimiga e uma bala, que é disparada pela barra de espaços.
10 SCREEN 2,1
20 SE=-1:XI=128:YI=5
30 XC=128:YC=178:YB=170:B=0
40 ON SPRITE GOSUB 500 : SPRITE ON
50 FOR F=1 TO 3
60 FOR T=1 TO 8
70 READ A$
80 S$=S$+CHR$(VAL("&H"+A$))
90 NEXT T
100 SPRITE$(F)=S$ : S$=""
110 NEXT F
120 C=STICK(0)
130 IF C=3 THEN XC=XC+2 : IF XC>240 THEN XC=240
140 IF C=7 THEN XC=XC-2 : IF XC<0 THEN XC=0
150 IF STRIG(0)<>0 AND B=0 THEN B=1 : XB=XC
160 PUT SPRITE 0,(XI,YI),5,3
170 IF B=1 THEN PUT SPRITE 2,(XB,YB),15,2 ELSE PUT SPRITE 2,(XB,YB),0
180 PUT SPRITE 1,(XC,YC),14,1
190 XI=XI+SE*2
200 IF XI<0 OR XI>240 THEN SE=-SE
210 IF B=1 THEN YB=YB-2 : IF YB<-8 THEN B=0 : YB=170
220 GOTO 120
230 DATA 10,38,54,FE,92,FE,00,00
240 DATA 10,10,10,10,10,10,10,10
250 DATA 7C,82,82,82,7C,28,44,00
500 ' Colisao de sprite
510 IF YB>128 THEN RETURN
520 PUT SPRITE 2,(XB,YB),0
530 FOR I=1 TO 5
540 FOR C=2 TO 15 STEP .2
550 PUT SPRITE 0,(XI,YI),C,3
560 NEXT C
570 PUT SPRITE 0,(XI,YI),0
580 GOTO 580
Observações:
- A interrupção para colisões de sprites foi ativada. Como há colisão no disparo da bala com o canhão, o tratamento é desabilitado nessa situação (linha 510).
- A variável B verifica se o tiro foi disparado para poder desenhar a bala e atualizar a posição dela na tela.
- O comando STICK controla a posição X do canhão na tela.
Referências:
[1]- Livro: Linguagem Basic MSX, editora Aleph, 5a. Edição, 1987.