Pequeno jogo de reflexos para 2 jogadores
Desta vez apresento-vos um pequeno jogo em que a ideia é ver qual dos dois jogadores tem melhores reflexos. Consiste num LED que acende de forma aleatória e, quando acesso, permite aos jogadores carregarem no botão e ver quem foi o mais rápido. Uma luz acende-se a indicar qual o jogador mais rápido e os pontos vão sendo acumulados (não são usados neste esquema, mas podem ser apresentados se juntarem um display de 7 segmentos com 2 ou 4 dígitos, por exemplo).
Material
- 3 LEDs de cores à escolha (de preferência com cores distintas) + 3 resistências de 330 Ω (laranja, laranja, castanho)
- Buzzer + 1 resistência de 330 Ω (laranja, laranja, castanho)
- 2 botões (push-button) + 2 resistências de 1 kΩ (castanho, preto, vermelho)
Esquema de ligação
Montagem
- Coloquem os LEDs como apresentado na figura. Para cada LED, liguem uma resistência de 330 Ω, unindo a perna negativa do LED e o negativo da breadboard
- Coloquem agora os botões. Liguem numa das pernas um fio (branco na figura) ao positivo (5V). Na perna diagonalmente oposta, liguem a resistência de 1 KΩ (em cada botão).
- Liguem o buzzer, e coloquem uma resistência de 330 Ω, unindo o negativo do buzzer e o ground (0V/negativo da breadboard).
- Liguem o fio do primeiro LED (jogador A) à porta 2 do Arduino
- Liguem o fio do LED central (blinker) à porta 3 do Arduino
- Liguem o fio do terceiro LED (jogador B) à porta 4 do Arduino
- Liguem um fio da porta 6 ao positivo do buzzer
- Liguem um fio na linha de cada resistência dos botões às portas 8 e 9 (jogador A e B, respetivamente).
- Por fim, liguem um fio do GND (ground/0V) do Arduino à trilha da breadboard que escolheram como negativo)
- Façam o mesmo para o fio de alimentação de 5V (se ligaram os fios dos botões no lado oposto da breadboard, não se esqueçam de colocar um fio adicional para a outra trilha do positivo (isto foi feito no esquema em cima)).
Terminadas as ligações, façam upload deste código:
// Configuração de constantes
byte const PLRA_LED = 2; // led do jogador A
byte const PLRA_BTN = 8; // botão do jogador A
byte const PLRB_LED = 4; // led do jogador B
byte const PLRB_BTN = 9; // botão do jogador B
byte const BLINKER = 3; // porta do LED indicador
byte const BELL = 6; // porta do buzzer
int const MIN_TIME = 3000; // tempo mínimo de intervalo entre indicador
int const MAX_TIME = 7000; // tempo máximo de intervalo entre indicador
int const BELL_CHEAT = 600;
// variáveis a usar no jogo
long last_start = 0;
long score_a = 0;
long score_b = 0;
int multiple = 1;
byte touch = 0;
byte block_A = 0;
byte block_B = 0;
// preparação das portas
void setup()
{
pinMode(PLRA_LED, OUTPUT);
pinMode(PLRB_LED, OUTPUT);
pinMode(PLRA_BTN, INPUT);
pinMode(PLRB_BTN, INPUT);
pinMode(BLINKER, OUTPUT);
pinMode(BELL, OUTPUT);
intro();
last_start = nextBlink();
}
// loop do jogo
void loop()
{
long ms = millis();
// jogo gerado; aceitar input dos jogadores
if( ms >= last_start && touch == 0 )
{
digitalWrite(BLINKER, HIGH);
byte read_A = digitalRead(PLRA_BTN);
byte read_B = digitalRead(PLRB_BTN);
// leitura do jogador A
if( read_A == 1 && block_A == 0 )
{
score_a += (1 * multiple);
digitalWrite(PLRA_LED, HIGH);
touch = 1;
}
// leitura do jogador B
if( read_B == 1 && block_B == 0)
{
score_b += (1 * multiple);
digitalWrite(PLRB_LED, HIGH);
touch = 1;
}
}
else if( ms <= last_start )
{
// sistema contra batota
byte read_A = digitalRead(PLRA_BTN);
byte read_B = digitalRead(PLRB_BTN);
if( read_A == 1 )
{
digitalWrite(PLRA_LED, HIGH);
tone(BELL, BELL_CHEAT);
delay(700);
noTone(BELL);
digitalWrite(PLRA_LED, LOW);
block_A = 1;
}
else
{
block_A = 0;
}
if( read_B == 1 )
{
digitalWrite(PLRB_LED, HIGH);
tone(BELL, BELL_CHEAT);
delay(700);
noTone(BELL);
digitalWrite(PLRB_LED, LOW);
block_B = 1;
}
else
{
block_B = 0;
}
}
// gerar novo jogo
if( ms >= last_start && touch == 1 )
{
sound();
delay(1000);
digitalWrite(PLRA_LED, LOW);
digitalWrite(PLRB_LED, LOW);
digitalWrite(BLINKER, LOW);
last_start = nextBlink();
touch = 0;
}
}
// função para controlar a próxima execução do blinker
long nextBlink()
{
return millis() + random(MIN_TIME, MAX_TIME);
}
// tocar um som com um buzzer
void sound()
{
tone(BELL, 1600);
delay(100);
noTone(BELL);
tone(BELL, 2000);
delay(100);
noTone(BELL);
tone(BELL, 2400);
delay(500);
noTone(BELL);
}
// tocar o mesmo som, de forma inversa
void r_sound()
{
tone(BELL, 2400);
delay(100);
noTone(BELL);
tone(BELL, 2000);
delay(100);
noTone(BELL);
tone(BELL, 1600);
delay(500);
noTone(BELL);
}
// Animação do jogo (luzes + som)
// as luzes piscam da esquerda para a direita e voltam (ver array seq)
// um som é gerado no inicio da animação e no final é gerado novo som
void intro()
{
sound();
int seq[] = { PLRA_LED, BLINKER, PLRB_LED, BLINKER, PLRA_LED };
int sz = 5;
for( byte i = 0; i < sz; i++ )
{
digitalWrite(seq[i], HIGH);
delay(200);
digitalWrite(seq[i], LOW);
}
r_sound();
}
Notas
Este jogo implementa um pequeno sistema anti-cheat, que consiste em verificar
se um jogador está a carregar no botão antes do LED central estar aceso.
Quando isso acontece, é emitido um som de erro no buzzer, e a luz do jogador
acende. No código duas variáveis block_A
e block_B
controlam
se os jogadores pressionaram ou não antes do tempo. Se um deles pressionar,
essa jogada não é válida enquanto o botão não for largado. Isto permite
bloquear um jogador que mantenha o botão pressionado até ao momento de
acender o botão central, evitando essa batota.
Outra mudança interessante é o facto de se usar o tipo de dados long
em
vez de int
para guardar o tempo. Como um int
pode
armazenar 2 bytes de dados (valores decimais entre -32768 a 32767), ao
chegar aproximadamente aos 33 segundos de jogo (32768 milissegundos, para
ser exato), acontecia o chamado overflow, e o valor retornaria
à escala de negativos. A partir daí, o LED central nunca mais acenderia,
porque o valor retornado pela função millis()
seria sempre superior
a 32767, valor que nunca poderia ser assumido por uma variável do tipo
inteiro. Por isso, é usado o tipo de dados long
para registar
o próximo instante em que o LED central é aceso. Com èste tipo de dados
podemos registar valores até 2147483647 (2 mil milhões ou 2 bilhões), que
dá algo como 23 dias de jogo. Por mais que gostem deste jogo, creio que
23 dias é mais que suficiente para não levantar problemas :)