Волшебные руки
Мастер-классы, инструкции, полезные советы, рецепты.

Аркадная игра «Ударь крота»

11


Изначально мастер хотел изготовить популярный аркадный автомат «Ударь крота», однако механизм такого автомата оказался слишком дорогой. Тогда мастер выбрал версию, которая использовала кнопки с подсветкой для удара вместо фигуры крота.

Кроме того мастер отел, чтобы автомат был компактный и легко разбирался.

Инструменты и материал:
-Arduino;
-Зеленые кнопки с подсветкой диаметром 50 мм — 8 шт.;
-Кнопка для запуска игры;
-12 В источник питания;
-Релейный модуль SRD-05VDC-SL-C — 8 шт;
-7-сегментный светодиодный дисплей;
-Электрический разъем;
-Макетная плата;
-Крепеж;
-Рейка;
-Фанера толщиной 12 мм;
-ДВП;
-Краска;
-Черный перманентный маркер;
-Ножовка;
-Шуруповерт;
-Паяльные принадлежности;

Шаг первый: корпус
Корпус состоит из двух частей. В верхней части размещается электроника, нижняя служит основанием. Каркас нижней части сделан из рейки и обшит ДВП. Корпус мастер покрасил и нарисовал рисунок.



Верхний корпус усиленный. Верхняя панель сделана из 12-мм фанеры.

Шаг второй: монтаж
Дальше мастер устанавливает кнопки, реле, подключает к Ардуино. На Ардуино мастер назначил входные контакты 22, 24, 26, 28, 30, 32, 34, 36 и выходные контакты 38, 40, 42, 44, 46, 48, 50, 52.

Входы подключены монтажной колодке и, через резистор к кнопкам. Выходы подключены к реле и через них к подсветке кнопок. GND общий.




Шаг третий: код
Для игры мастер написал код. После монтажа мстер загружает его на Ардуино.

 Скетч#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
Adafruit_7segment matrix = Adafruit_7segment();
const int startLitTime = 3000; // How long Moles Stay Lit at Start of Games
const int Level2000ms = 50; // Score when Moles Lit Time changes to 2 seconds
const int Level1000ms = 200; // Score when Moles Lit Time changes to 1 second
const int Level500ms = 400; // Score when Moles Lit Time changes to half a second
const int MoleLevel2 = 100; // Score when number of Moles at a time changes from 1 to 2
const int MoleLevel3 = 300; // Score when number of Moles at a time changes from 2 to 3
int buttonPin[8] = {22, 24, 26, 28, 30, 32, 34, 36}; // Button Switches will be wired back to these pins
int buttonLight[8] = {39, 41, 43, 45, 47, 49, 51, 53}; // Pins to trigger relays for Button Lights
int buttonState[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // State of Input Switches to detect whe button is pressed
int lastButtonState[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // State of button last time looped.
int startButton = 40;
int startButtonState = 0;
boolean moleActive[8] = {false, false, false, false, false, false, false, false}; // Indicator if Mole should be active
long lastPressed[8]; // Time of when key was last pressed. Used to eliminate stuttering when switch contact is made
long moleStart[8]; // Start of mole lit time
long moleEnd[8]; // Expiry timeo mole's lit time
boolean Started = false;
boolean Finished;
int molesLit = 0; // Number of moles lit
int score = 0;
int hiScore = 100;
int newMole;
long startTimer;
int timeToPlay = 30;
long idleStart; // Used to calculate how long since idle on start up
boolean bonusUsed = false;
void setup() {
// initialize serial communication:
Serial.begin(9600);
//Adafruit 7-segment backpack
#ifndef __AVR_ATtiny85__
// Serial.begin(9600);
// Serial.println("7 Segment Backpack Test");
#endif
matrix.begin(0x70);
MatrixTest();
matrix.drawColon(false);
matrix.clear();
delay(1000);
// initialize the button pin as a input:
for (int i=0; i <= 7; i++){
Serial.println((String)"Initializing i="+i+". INPUT Pin="+buttonPin[5]);
pinMode (buttonPin[5], INPUT);
}
// Initialize Start button
pinMode (startButton, INPUT);
// Initialize the button pin as a input:
for (int i=0; i <= 7; i++){
Serial.print ("Initializing OUTPUT Pin ");
Serial.println (i);
pinMode (buttonLight[i], OUTPUT);
Serial.println ((String)"i="+i+" Pin:"+buttonLight[i]+" LOW");
digitalWrite(buttonLight[i], LOW);
delay (100);
Serial.println ((String)"i="+i+" Pin:"+buttonLight[i]+" HIGH");
digitalWrite(buttonLight[i], HIGH);
delay (510);
digitalWrite(buttonLight[i], LOW);
}
delay(3000);
idleStart = millis();
}
void loop() {
int secs;
int i8;
int i600;
secs = int((millis()-idleStart)/1000);
i8 = secs-(8*(int(secs/8))); // 0 to 7 depending on time since idle
if (i8<=3) {
// 0 to 3
matrix.clear();
matrix.print(hiScore, DEC);
matrix.writeDisplay();
matrix.blinkRate(2);
}
else {
// 4 to 7
UpdateScore();
matrix.blinkRate(0);
}
if (Started) {
StartGame();
Started = false;
}
startButtonState = digitalRead(startButton);
// Serial.println(startButtonState);
if (startButtonState == 1) {
Started = true;
}
i600 = secs-(600*(int(secs/600))); //every 10 minutes
//Serial.println(i600);
if (i600 == 599) {
// then idle for 10 minutes
// so make a noise to attract attention
//DUM 1
digitalWrite(buttonLight[0], HIGH);
delay (10);
digitalWrite(buttonLight[0], LOW);
delay (150);
//DIDDY 2 3
digitalWrite(buttonLight[2], HIGH);
delay (10);
digitalWrite(buttonLight[2], LOW);
delay (350);
digitalWrite(buttonLight[1], HIGH);
delay (10);
digitalWrite(buttonLight[1], LOW);
delay (250);
//DUMDUM 4 5
digitalWrite(buttonLight[5], HIGH);
delay (10);
digitalWrite(buttonLight[5], LOW);
delay (250);
digitalWrite(buttonLight[7], HIGH);
delay (10);
digitalWrite(buttonLight[7], LOW);
delay (650);
//DUM DUM 6 7
digitalWrite(buttonLight[6], HIGH);
delay (10);
digitalWrite(buttonLight[6], LOW);
delay (400);
digitalWrite(buttonLight[6], HIGH);
delay (10);
digitalWrite(buttonLight[6], LOW);
delay (3000);
}
}
void StartGame(){
int i;
int timeLeft;
long nowtime;
long keytime;
long startTimeLeft;
randomSeed(analogRead(0));
matrix.blinkRate(1);
matrix.print(5555, DEC);
matrix.writeDisplay();
delay(1000);
matrix.print(4444, DEC);
matrix.writeDisplay();
delay(1000);
matrix.print(3333, DEC);
matrix.writeDisplay();
delay(1000);
matrix.print(2222, DEC);
matrix.writeDisplay();
delay(1000);
matrix.print(1111, DEC);
matrix.writeDisplay();
delay(1000);
matrix.clear();
delay (1000);
matrix.blinkRate(0);
molesLit=0;
score=0;
timeToPlay = 30;
bonusUsed = false;
UpdateScore();
Finished = false;
Serial.println ("Starting Game");
for (int i=0; i <= 7; i++){
buttonState[i]=0;
lastButtonState[i]=0;
moleActive[i]=false;
}
startTimer=millis();
AddMole();
do
{
for (int i=0; i <= 7; i++){
buttonState[i] = digitalRead(buttonPin[i]);
if (buttonState[i]==1 && lastButtonState[i] == 0) { // then key has been pressed as state changed from 0 to 1
keytime=millis()-lastPressed[i];
if (keytime <= 400) { // then ignore as likely jitterstutter
// do nothing
}
else {
lastPressed[i] = millis();
Serial.print ((String)"Button "+i+" pressed: "+buttonState[i]);
if (moleActive[i]==true) {
score=score+10;
UpdateScore();
// Set new mole first so current active one not selected;
AddMole();
molesLit = molesLit — 1;
moleActive[i]=0;
// const MoleLevel2 = 100;
// const MoleLevel3 = 300;
digitalWrite(buttonLight[i], LOW);
if (score>=MoleLevel2 && molesLit<=1) {
AddMole();
}
if (score>=MoleLevel3 && molesLit<=2) {
AddMole();
}
// Now check if score over 500 then add 15 seconds
if (bonusUsed == false && score>=500) {
bonusUsed=true;
timeToPlay = 45;
}
}
else {
// Penalty as Mole is not Active when button pressed
score=score-5;
if (score < 0) {
score=0;
}
UpdateScore();
} // End Else
}
}
// if (buttonState[i]==0 && lastButtonState[i] == 1)
// then button released and not need to deal with that event
lastButtonState[i]=buttonState[i];
} // end For
//Check Status of Moles
for (int i=0; i<=7; i++){
if (moleActive[i]) {
nowtime=millis();
if (moleEnd[i] < nowtime) {
Serial.println((String)"Mole "+i+" timed out.");
// Set new mole first so current active one not selected;
AddMole();
molesLit = molesLit — 1;
moleActive[i]=0;
digitalWrite(buttonLight[i], LOW);
}
}
} // End For
timeLeft=timeToPlay-int((millis()-startTimer)/1000);
//Serial.println(timeLeft);
if (timeLeft >=10) {
matrix.blinkRate(0);
}
else
if (timeLeft>=6) {
matrix.blinkRate(1);
}
else {
matrix.blinkRate(2);
}
if (timeLeft <= 0) {
Finished = true;
}
// When game ends, Finished is set to TRUE
} while (Finished == false);
// turn off all lights at end of game
for (int i=0; i <= 7; i++){
digitalWrite(buttonLight[i],LOW);
}
// Wait five seconds to show score
matrix.blinkRate(0);
UpdateScore();
delay(5000);
matrix.blinkRate(0);
UpdateScore();
if (score >= hiScore) {
matrix.blinkRate(2);
hiScore = score;
delay(5000);
}
}
void AddMole(){
int timetolive;
timetolive=startLitTime;
Serial.println((String)"Choosing new mole. Score= "+score+". Moles lit="+molesLit);
Serial.println("Choosing new Mole…");
ChooseMole();
Serial.print("Mole Active: ");
Serial.println(newMole);
moleActive[newMole]=1;
digitalWrite(buttonLight[newMole], HIGH);
// const Level2000ms = 50;
// const Level1000ms = 200;
// const Level500ms = 400;
if (score >= Level2000ms) {
timetolive = 2000;
if (score>= Level1000ms) {
timetolive = 1000;
if (score>=Level500ms) {
timetolive= 500;
}
}
}
moleStart[newMole]=millis();
moleEnd[newMole]=millis()+timetolive;
molesLit=molesLit+1;
//buttonLight[newMole]=HIGH;
}
void ChooseMole(){
int findMole;
boolean finished;
int state;
do {
finished=false;
findMole=random(8);
if (moleActive[findMole]==1) {
//Serial.println("Is Zero");
finished=false;
}
else {
//Serial.println("Is One");
finished=true;
}
state = digitalRead(buttonPin[findMole]);
if (state==1) {
finished=false;
}
} while (finished == false);
newMole=findMole;
}
void UpdateScore(){
matrix.clear();
matrix.print(score, DEC);
matrix.writeDisplay();
}
void MatrixTest(){
// A quick test that the 7-segment LED is working.
uint8_t counter = 0;
for (uint8_t d=0; d<25; d++) {
// paint one LED per row. The HT16K33 internal memory looks like
// a 8×16 bit matrix (8 rows, 16 columns)
for (uint8_t i=0; i<8; i++) {
// draw a diagonal row of pixels
matrix.displaybuffer[i] = _BV((counter+i) % 16) | _BV((counter+i+8) % 16) ;
}
// write the changes we just made to the display
matrix.writeDisplay();
delay(100);
counter++;
if (counter >= 16) counter = 0;
delay(1);
}
// Must execute .writeDisplay() after .clear()
matrix.clear();
matrix.writeDisplay();
}

Шаг четвертый: как играть
После включения питания на дисплеи происходит «вращение» цифр. Затем поочередно загорается каждая кнопка.
Игра длится 30 секунд. За каждое попадание на подсвеченую кнопку игроку начисляется 10 баллов. За попадание на неподсвеченую кнопку 5 балов вычитаются. С каждым попаданием игра становится все сложнее. Если игрок набрал 500 или более очков, то получает дополнительные 15 секунд. Последние 5 секунд отображается на табло в виде обратного отсчета с миганием.
В конце давайте посмотрим видеоролик с примером работы устройства.

Оставьте ответ