Главная » Статьи » Мои проекты и мысли

Игра "Повторика"

Шла 2-ая неделя ожидания модуля Blootooth для SAR. В эти дни никаких озарений не было, но надо было себя чем-то занять. И вот как-то раз, гуляя с девушкой, мы начали спорить о возможностях памяти, мол у кого она лучше:) И сами того не замечая, придумали игру, которая раз и навсегда поставила бы точку в этом споре. Началось все с простого повтора комбинации.  Дальше  пошли мысли про разнообразие уровней, количество жизней, правил и прочем. В конечном счете получилась довольно таки не плохая и интересная игра. 

   

На панели располагаются 4 светодиода разного цвета, эти светодиоды и выводят задание, в зависимости от уровня игры. Например если первый уровень, то моргнет только один светодиод, если уровень 5, то светодиоды моргнут по очереди пять раз в случайном порядке. Всего уровней 10. Почему так мало? Как показывает практика, и этого количества вполне достаточно. Если же вам будет этого мало, то можно будет с легкостью это изменить. 
Сверху панели располагается индикация жизни. На протяжении всей игры индикация показывает текущее значение оставшихся жизней. В начале каждой новой игры игроку дается 3 жизни. Если количество жизней падает до 0, то игра заканчивается. И начинается все сначала.
Для повтора комбинации "Задания" используются кнопки, установленные справа на панели. Во время игры, при нажатии на кнопку, будет загораться определенный светодиод.
Чтобы не запутаться в этапах игры, а именно: 

  1. Вывод задания

  2. Ввод ответа

  3. Вывод результата

были сделаны подсказки в программном и в конструктивном исполнении. Так например второй этап (игрок повторяет комбинацию задания, нажимая на кнопки) начинается тогда, когда подсветка кнопок начинает светиться. Т.е. во время первого и третьего этапа, подсветка выключена. Как только дело доходит до ввода ответа, подсветка загорается, сигнализируя игроку о начале ввода. В первом и третьем этапе все иначе, но про это позже.

Процесс реализации:

     Для создания игры нам понадобятся:

  • Светодиоды 8 шт. (4 шт. - Задание, 3 шт. - Индикация жизни, 1 шт. - Подсветка кнопок) цвет выбирайте на свой вкус.
  • Кнопки 4 шт. 
  • Arduino. В конечном варианте я использовал Arduino Nano, вы можете использовать любой вид платформы
  • Резисторы 220 Ом, 7шт.
  • Провода

И так, начнем! В начале давайте соберем схему нашего устройства. Она выглядит так:

Первые 4 светодиода отвечают за вывод задания на панель, следующие 3 светодиода - это индикация жизни. И оставшийся один светодиод используется для подсветки кнопок.

В самом начале я не стал использовать сразу 4 светодиода и 4 кнопки. Писал скетч и проверял все возможные варианты на 3 светодиодах. Были проблемы с написанием кода. Все работало как надо, кроме ввода кнопок. Перепробовал все, но так и не смог добиться результата. Программа просто не хотела ждать пока игрок введет ответ. И перескакивала сразу на вывод результата. Решение оказалось очень простым. Оказывается при программировании Arduino, надо вводить задержку на ввод. Своего рода таймер, по истечению которого, программа продолжает работать дальше. Эта проблема была решена. И начался этап улучшения и приведения скетча к конечному варианту. 

     Видео работы схемы с тремя светодиодами: 

После того как закончил с отладкой кода для 3 светодиодов, скетч был переписан под 4 СД. Была разведена и спаяна плата. 

Порядок сборки:

1. Разводка производилась в программе SprintLayoutИ заняла примерно 3 часа. Большее время было потрачено на размещение компонентов на плате. Так как места хотелось занять немного, а элементов и дорожек получалось большое количество. Вот конечный вариант разводки в программе (все размеры указанны в мм): 

2. Теперь надо как-то эту схему разместить на плате. Существует много способов. Я буду использовать ЛУТ (лазерно-утюжная технология). Для начала печатаем схему с помощью лазерного принтера на глянцевой бумаге. Я использовал обложку от журнала. Получаем вот такую распечатку: 

3. Вырезаем из стеклотекстолита форму нужной нам величины и размеров

4. Зачищаем медную поверхность шкуркой и обезжириваем. Получаем что-то подобное:

5. Следующим шагом будет перенос рисунка с распечатки, на плату. Для этого крепим бумагу, рисунком вниз, на плату. Подгинаем углы и закрепляем их с другой стороны стеклотекстолита. Далее Нагреваем утюг, и проглаживаем им всю поверхность бумаги. На этом шаге важно прогладить все как много лучше. Чтобы рисунок четко перенесся с бумаги на плату. 

6. Теперь надо очистить плату от бумаги. Для этого кладем стеклотекстолит в емкость с водой, чтобы размягчилась бумага. И потом под напором воды смываем остатки. Получаем вот такую печатную плату:

7. Вот и дошли до травления печатной платы. Чтобы оставить только те дорожки что нам нужны, и убрать лишнюю медь, воспользуемся раствором хлорного железа. Разбавляем пару ложек порошка в горячей воде (не используйте для этого металлическую посуду!) и погружаем в него нашу плату. Ждем минут 30 (все зависит от концентрации и температуры получившейся смеси) и проверяем нашу плату. Если вся лишняя медь ушла, вытаскиваем макетку и смываем с нее рисунок. Если же остались какие-либо куски меди, то кладем плату еще на некоторое время в емкость с раствором.

 

8. Теперь нужно подготовить отверстия для компонентов платы. Для этого воспользуемся дрелью и сверлом на 2мм. Я использовал дрель собственного производства. Этот процесс легкий, поэтому объяснять тут ничего не буду.

Примеряем компоненты:

Все подошло идеально. Теперь очередь за лужением. 

9. На этом шаге нам потребуется паяльник, флюс, и припой. Проходим сначала всю плату флюсом. И далее паяльником и припоем проходим все дорожки. Чтобы при лужении припой хорошо ложился на плату, не жалейте флюса. 

У меня получилось не очень красиво, так как флюс я использовал очень ужасный. Но ничего, качество от этого не пострадало.

10. Самый интересный шаг, это сборка конечного устройства. При сборке, сначала паяйте те элементы, которые не боятся перегрева (не очень прихотливые). Это элементы, такие как кнопки, резисторы, конденсаторы и т.п. Потом уже транзисторы, светодиоды и микросхемы. 

Вот что получилось в итоге:

11. Ну и наконец крайний шаг, это заливка скетча в наше устройство. Конечный код игры:

#define DEBUG 1 //для отладки! Если строку закомментить, отладка выкл.
#define knopka1 9 //кнопка 1
#define knopka2 10 //кнопка 2
#define knopka3 12 //кнопка 3
#define knopka4 11 //кнопка 4
 #define ledLive1 6 //светодиод жизни 1
 #define ledLive2 7 //светодиод жизни 2
 #define ledLive3 8 //светодиод жизни 3
#define ledAssignment1 2 //светодиод задания 1
#define ledAssignment2 3 //светодиод задания 2
#define ledAssignment3 5 //светодиод задания 3
#define ledAssignment4 4 //светодиод задания 4
 #define ledBlockButtons 13 //светодиод подсказки для ввода ответа
byte levelCurrent=1; //начальный уровень
byte levelMax=10; //максимальное количество уровней
byte liveCurrent=3; //количество жизней
 enum SECTION{sectionOut,sectionIn,sectionResult};
 SECTION section=sectionOut;
unsigned long oldMillis;
 byte assignment[10];
 byte reply[10];
 byte countKnopka;
//=========================================================
void setup()
{
#ifdef DEBUG
 Serial.begin (9600);
#endif
 pinMode (knopka1,INPUT);
 digitalWrite(knopka1,HIGH);
 pinMode (knopka2 ,INPUT);
 digitalWrite(knopka2,HIGH);
 pinMode (knopka3,INPUT);
 digitalWrite(knopka3,HIGH);
 pinMode (knopka4,INPUT);
 digitalWrite(knopka4,HIGH);
 pinMode (ledAssignment1,OUTPUT);
 pinMode (ledAssignment2,OUTPUT);
 pinMode (ledAssignment3,OUTPUT);
 pinMode (ledAssignment4,OUTPUT); 
 OutByteAssignment(0);
 pinMode (ledLive1,OUTPUT);
 pinMode (ledLive2,OUTPUT);
 pinMode (ledLive3,OUTPUT);
 OutByteLive(0);
 pinMode (ledBlockButtons,OUTPUT);
 digitalWrite(ledBlockButtons,LOW);
 randomSeed (analogRead(0));
 oldMillis=millis();
}
//=========================================================
void loop()
{
// sectionOut
 if(section==sectionOut)
 {
 ClearAssignment(); // очистить массив
 fillAssignment(); // заполнить массив
 showLive(); // показать жизни
 showAssignment(); // показать задание
 digitalWrite(ledBlockButtons,HIGH); // вкл подсветки блока кнопок
#ifdef DEBUG
 controlAssignment(); // функция просмотра задания
#endif
 ClearReply(); // очистить массив
 countKnopka=0; // обнулить колво нажатий
 section=sectionIn; // смена флага секции
 oldMillis=millis(); // вкл секундомер
 }
// sectionIn
 if(section==sectionIn)
 {
 if((millis()-oldMillis)<(5000+levelCurrent*500))
 {
 if(countKnopka<levelMax)
 {
 if(!digitalRead(knopka1))
 {
 digitalWrite(ledAssignment1,HIGH); // визуализация нажатой кноки
 delay(50);
 while(!digitalRead(knopka1))
 { }
 reply[countKnopka]=1;
#ifdef DEBUG
 Serial.print("knopka1 = ");
 Serial.print(countKnopka);
#endif
 countKnopka++;
 digitalWrite(ledAssignment1,LOW); // визуализация нажатой кноки
#ifdef DEBUG
 Serial.print(" >>> ");
 Serial.println(countKnopka);
#endif
 }
 if(!digitalRead(knopka2))
 {
 digitalWrite(ledAssignment2,HIGH); // визуализация нажатой кноки
 delay(50);
 while(!digitalRead(knopka2))
 { }
 reply[countKnopka]=2;
#ifdef DEBUG
 Serial.print("knopka2 = ");
 Serial.print(countKnopka);
#endif
 countKnopka++;
 digitalWrite(ledAssignment2,LOW); // визуализация нажатой кноки
#ifdef DEBUG
 Serial.print(" >>> ");
 Serial.println(countKnopka);
#endif
 }
 if(!digitalRead(knopka3))
 {
 digitalWrite(ledAssignment3,HIGH); // визуализация нажатой кноки
 delay(50);
 while(!digitalRead(knopka3))
 { }
 reply[countKnopka]=3;
#ifdef DEBUG
 Serial.print("knopka3 = ");
 Serial.print(countKnopka);
#endif
 countKnopka++;
 digitalWrite(ledAssignment3,LOW); // визуализация нажатой кноки
#ifdef DEBUG
 Serial.print(" >>> ");
 Serial.println(countKnopka);
#endif
 }
 if(!digitalRead(knopka4))
 {
 digitalWrite(ledAssignment4,HIGH); // визуализация нажатой кноки
 delay(50);
 while(!digitalRead(knopka4))
 { }
 reply[countKnopka]=4;
#ifdef DEBUG
 Serial.print("knopka4 = ");
 Serial.print(countKnopka);
#endif
 countKnopka++;
 digitalWrite(ledAssignment4,LOW); // визуализация нажатой кноки
#ifdef DEBUG
 Serial.print(" >>> ");
 Serial.println(countKnopka);
#endif
 }
 }
 }
 else
 {
 digitalWrite(ledBlockButtons,LOW); // выкл подсветки блока кнопок
#ifdef DEBUG
 Serial.println("...allotted time has expired !");
#endif
 section=sectionResult; // смена флага секции
 }
 }
// sectionResult
 if(section==sectionResult)
 {
#ifdef DEBUG
 controlReply(); // функция просмотра ответов
#endif
 CompareArray(); // сравнить массивы, принять решение
 // showResult(); // ещё не написана :)
 section=sectionOut; // смена флага секции
 }
}
//=========================================================
 
//====== функции =========================================
//=========================================================
// фунцция получения случайного байта - 001 , 010 , 100
//=========================================================
byte RandomByte()
{
 byte tempByte=random(1,5);
return tempByte;
}
//=========================================================
// фунцция очистки массива "задание"
//=========================================================
void ClearAssignment()
{
 for(byte i=0;i<10;i++) { assignment[i]=0; };
}
//=========================================================
// фунцция очистки массива "ответ"
//=========================================================
void ClearReply()
{
 for(byte i=0;i<10;i++) { reply[i]=0; };
}
//=========================================================
// фунцция вывода байта на СД "задание"
//=========================================================
void OutByteAssignment(byte _n)
{
 switch (_n)
 {
 case 0 : // для выключения всех трёх СД_задание
 {
 digitalWrite(ledAssignment1,LOW);
 digitalWrite(ledAssignment2,LOW);
 digitalWrite(ledAssignment3,LOW);
 digitalWrite(ledAssignment4,LOW);
 break;
 }
 case 1 :
 {
 digitalWrite(ledAssignment1,HIGH);
 digitalWrite(ledAssignment2,LOW);
 digitalWrite(ledAssignment3,LOW);
 digitalWrite(ledAssignment4,LOW);
 break;
 }
 case 2 :
 {
 digitalWrite(ledAssignment1,LOW);
 digitalWrite(ledAssignment2,HIGH);
 digitalWrite(ledAssignment3,LOW);
 digitalWrite(ledAssignment4,LOW);
 break;
 }
 case 3 :
 {
 digitalWrite(ledAssignment1,LOW);
 digitalWrite(ledAssignment2,LOW);
 digitalWrite(ledAssignment3,HIGH);
 digitalWrite(ledAssignment4,LOW);
 break;
 } 
 case 4 :
 {
 digitalWrite(ledAssignment1,LOW);
 digitalWrite(ledAssignment2,LOW);
 digitalWrite(ledAssignment3,LOW);
 digitalWrite(ledAssignment4,HIGH);
 break;
 }
 case 7 : // для включения всех четырех СД_задание
 {
 digitalWrite(ledAssignment1,HIGH);
 digitalWrite(ledAssignment2,HIGH);
 digitalWrite(ledAssignment3,HIGH);
 digitalWrite(ledAssignment4,HIGH);
 break;
 }
 }
}
//=========================================================
// фунцция вывода байта на СД "жизнь"
//=========================================================
void OutByteLive(byte _n)
{
 switch (_n)
 {
 case 0 : // для выключения всех трёх СД_жизнь
 {
 digitalWrite(ledLive1,LOW);
 digitalWrite(ledLive2,LOW);
 digitalWrite(ledLive3,LOW);
 break;
 }
 case 1 :
 {
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,LOW);
 digitalWrite(ledLive3,LOW);
 break;
 }
 case 2 :
 {
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,LOW);
 break;
 }
 case 3 :
 {
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,HIGH);
 break;
 }
 case 7 : // для включения всех трёх СД_жизнь
 {
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,HIGH);
 break;
 }
 }
}
//=========================================================
// функция сравнения массивов "задание" и "ответ"
//=========================================================
void CompareArray()
{
 boolean error=0;
 for(byte i=0;i<10;i++) 
 { 
 if(assignment[i]!=reply[i])
 {
 error=1;
 break;
 }
 }
 if (error)
 {
 liveCurrent--;
 }
 else 
 {
 levelCurrent++;
 }
 if (liveCurrent == 0)
 {
 Loss();
 levelCurrent = 1;
 liveCurrent = 3;
 }
 if (levelCurrent == 10)
 {
 Win();
 liveCurrent = 3;
 levelCurrent = 1;
 }
}
//=========================================================
// функция заполнения массива "задание"
//=========================================================
void fillAssignment()
{
 for(byte i=0;i<levelCurrent;i++)
 {
 if(i==0) assignment[0]=RandomByte();
 if(i>0)
 {
 byte tempByte=RandomByte();
 while(assignment[i-1]==tempByte)
 {
 tempByte=RandomByte();
 }
 assignment[i]=tempByte;
 }
 }
}
//=========================================================
// функция показать жизни
//=========================================================
void showLive()
{
 
 OutByteLive(liveCurrent);
}
//=========================================================
// функция показать задание
//=========================================================
void showAssignment()
{
 OutByteAssignment(7);
 delay(1000);
 OutByteAssignment(0);
 delay(1000);
 byte n=0; // начало миганий задания
 while((assignment[n]!=0)&&(n<10))
 {
 OutByteAssignment(assignment[n]); // вкл случСД
 delay(500); // пауза
 OutByteAssignment(0); // выкл случСД
 delay(500); // пауза
 n++;
 } // окончание миганий задания
 delay(500);
 OutByteAssignment(7);
 delay(500);
 OutByteAssignment(0);
 delay(500);
}
//=========================================================
// функция просмотра задания
//=========================================================
void controlAssignment()
{
 Serial.print(" level = ");
 Serial.println(levelCurrent);
 Serial.print("live = ");
 Serial.println(liveCurrent);
 for(byte i=0;i<10;i++)
 {
 Serial.print(assignment[i]);
 Serial.print(" ");
 }
 Serial.println("");
 Serial.println(" GO ! Push button !!!");
}
//=========================================================
// функция просмотра ответов
//=========================================================
void controlReply()
{
 for(byte i=0;i<10;i++)
 {
 Serial.print(reply[i]);
 Serial.print(" ");
 }
 Serial.println("");
 Serial.println("************************");
}
//=========================================================
// фунцция оповещения игрока о победе
//=========================================================
void Win()
{
 for (int i=0 ; i < 3 ; i++)
 {
 digitalWrite(ledLive1,LOW);
 digitalWrite(ledLive2,LOW);
 digitalWrite(ledLive3,LOW);
 delay(100);
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,LOW);
 digitalWrite(ledLive3,LOW);
 delay (100);
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,LOW);
 delay(100);
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,HIGH);
 delay (100);
 }
 for (int i=0 ; i < 3 ; i++)
 {
 digitalWrite(ledLive1,LOW);
 digitalWrite(ledLive2,LOW);
 digitalWrite(ledLive3,LOW);
 delay (200);
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,HIGH);
 delay (200);
 }
}
//=========================================================
// фунцция оповещения игрока о проигрыше
//=========================================================
void Loss()
{
 digitalWrite(ledLive1,LOW);
 digitalWrite(ledLive2,LOW);
 digitalWrite(ledLive3,LOW);
 delay(100);
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,LOW);
 digitalWrite(ledLive3,LOW);
 delay (100);
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,LOW);
 delay(100);
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,HIGH);
 delay (100);
 digitalWrite(ledLive1,LOW);
 digitalWrite(ledLive2,LOW);
 digitalWrite(ledLive3,LOW);
 delay(100);
 digitalWrite(ledLive1,LOW);
 digitalWrite(ledLive2,LOW);
 digitalWrite(ledLive3,HIGH);
 delay(100); 
 digitalWrite(ledLive1,LOW);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,HIGH);
 delay(100); 
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,HIGH);
 delay (100); 
 for (int i=0 ; i < 3 ; i++)
 {
 digitalWrite(ledLive1,LOW);
 digitalWrite(ledLive2,LOW);
 digitalWrite(ledLive3,LOW);
 delay (200);
 digitalWrite(ledLive1,HIGH);
 digitalWrite(ledLive2,HIGH);
 digitalWrite(ledLive3,HIGH);
 delay (200);
 }
}
//=========================================================

 

И видео конечного варианта самой приставки:

Мысли и идеи по данной теме:

Самое первое, что приходит на ум, это добавить звуковое сопровождение и цифровую индикацию. Звуковое сопровождение помогало бы игроку лучше понять игру. Новая игра, конец игры, проигрыш, выигрыш, все бы это сопровождалось каким-нибудь фоновым звуком, и игрок исходя из этого сразу понимал что происходит. В качестве источника звука, подошел бы даже обыкновенный пьезоизлучатель.

На счет цифровой индикации, то тут еще легче. Уровень, количество оставшихся жизней, все это бы воспроизводилось на обычном 7-ми сигментном индикаторе. И тогда игроку было бы понятно на каком этапе игры он находится. 

Также хотелось бы добавить хороший корпус, лицевую панель игры я приводил в самом начале статьи. Ее бы изготовил из оркстекла, а вот сам корпус из пластмассы. Тем более, если бы установил звуковой излучатель и индикатор, то корпус был бы кстати. 

Вот и все, что хотелось бы рассказать по поводу создания простой и в тоже время интересной игры. Надеюсь тема была вам полезна. Все интересующие вас вопросы и пожелания, вы можете оставить в комментариях или же на форуме

 

Если Вам понравилась наша статья, поделитесь ею с друзьями.

 

 

Категория: Мои проекты и мысли | Добавил: GM (05.10.2015)
Просмотров: 1151 | Комментарии: 7 | Теги: статьи, игра, Проекты, Arduino | Рейтинг: 5.0/2
Всего комментариев: 7
avatar
1 mrjarviscraft • 21:56, 14.10.2015
Гениально!
avatar
2
2 GM • 22:01, 14.10.2015
Благодарю biggrin
avatar
3 zirkov_micha • 20:08, 15.10.2015
Классно придумано и реализовано) Хотелось бы сделать что-нибудь подобное)))
avatar
1
4 GM • 22:09, 15.10.2015
Спасибо. Вы если что обращайтесь, обязательно поможем и подскажем smile
avatar
У меня такая игра в детстве была.  Индикатора там не было, а пищалка присутствовала.
Хочу своим детям что-нить подобное собрать.
avatar
0
6 GM • 15:13, 24.03.2017
Ну в принципе ничего сложного в создании такой игры нет. Я больше времени на код потрати . На счет индикатора и пищалки. То я создал прототип. Как бы некий шаблон, который можно улучшать. Добавить 7-ми сигментный индикатор, либо дисплей, пищалку. Это все уже зависит от воображения smile
avatar
Нашел "полную" версию. И с 7-ми сегментным индикатором , пищалкой с музыкой).
http://cxem.net/arduino/arduino167.php
Буду собирать.
Спасибо за идею!
avatar