====== Тамагочи «Space Invaders» ====== ** [[slot-box:start#хочу_собрать|Проекты на Arduino и Slot Shield]] ** Дети девяностых и их родители могут не читать описание, а сразу переходить к сборке девайса. **Тамагочи** — это электронная игрушка, суть которой сводится к выращиванию виртуального питомца. Его нужно вовремя кормить, поить, развлекать — как настоящего котёнка или щеночка. Не уследишь, электронный зверёк заболеет или даже умрёт. В порыве ностальгии мы решили сделать своего тамагочи, а в качестве виртуального друга выбрали милых космических захватчиков из классической аркады Space Invaders. {{ :slot-box:tamagotchi:tamagotchi.jpg?nolink|}} Наш питомец будет жить на монохромном [[amp>product/troyka-led-matrix?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki| LED-дисплее]]. Мы начнём с простейшего двухклеточного организма, к концу игры он займёт собой почти все 64 пикселя матрицы. На втором экране будут появляться его просьбы: пришельца нужно кормить, поить, говорить приятные слова и вовремя укладывать спать. Каждому из этих действий соответствует кнопка на [[amp>product/troyka-quad-switch?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|четырёхкнопочной клавиатуре]]. Не забывайте о питомце, иначе он может погибнуть. * Платформы: [[amp>product/arduino-uno?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|Arduino Uno]], [[amp>product/arduino-leonardo?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|Arduino Leonardo]], [[amp>product/iskra-neo?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|Iskra Neo]] * Язык программирования: Arduino (C++) ===== Что потребуется ===== [[amp>collection/tamagotchi?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki | Полный сет]] компонентов проекта. В сет входят: * [[amp>product/iskra-neo?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|Iskra Neo]] * [[amp>product/arduino-troyka-slot-shield?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|Troyka Slot Shield]] * две [[amp>product/troyka-led-matrix?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|LED матрицы 8×8]] * [[amp>product/troyka-quad-switch?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|четырёхкнопочная клавиатура]] * [[amp>product/troyka-buzzer?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|пьезопищалка]] ===== Видеоинструкция ===== {{youtube>Hjap8aisoOo?large}} ===== Как собрать ===== Установите [[amp>product/arduino-troyka-slot-shield?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|Troyka Slot Shield]] на [[amp>product/iskra-neo?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|Iskra Neo]] {{:slot-box:tamagotchi:step-01.png?nolink|}} Вставьте две [[amp>product/troyka-led-matrix?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|LED матрицы 8×8]] в левый и средний слоты верхнего ряда. Чтобы у матриц были разные адреса, капните припой на любую контактную площадку любого из модулей. Как это сделать — смотрите на странице [[http://wiki.amperka.ru/%D0%BF%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82%D1%8B:troyka-led-matrix#%D0%BF%D0%BB%D0%BE%D1%89%D0%B0%D0%B4%D0%BA%D0%B8_%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%D0%B0_%D0%B0%D0%B4%D1%80%D0%B5%D1%81%D0%B0|описания матриц]]. {{:slot-box:tamagotchi:step-02.png?nolink|}} Поверните [[amp>product/troyka-quad-switch?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|четырёхкнопочную клавиатуру]] на 90 градусов против часовой стрелки и вставьте в оставшийся слот верхнего ряда. {{:slot-box:tamagotchi:step-03.png?nolink|}} Переверните [[amp>product/troyka-buzzer?utm_source=proj&utm_campaign=slot-proj-16&utm_medium=wiki|пьезопищалку]] вверх ногами и вставьте в нижний правый слот. {{:slot-box:tamagotchi:step-04.png?nolink|}} ===== Скетч ===== Прошейте контроллер скетчем через [[../установка-и-настройка-arduino-ide|Arduino IDE]]. // библиотека для работы I²C #include "Wire.h" // библиотека для работы со светодиодной матрицей #include "TroykaLedMatrix.h" // библиотека для работы с кнопками #include "TroykaButton.h" // создаём объекты matrix для работы с матрицами // для каждой матрицы передаём свой адрес // подробнее читайте на: // http://wiki.amperka.ru/продукты:troyka-led-matrix TroykaLedMatrix matrix1; TroykaLedMatrix matrix2(0b01100001); // создаём объекты для работы с кнопками и передаём им номера пинов TroykaButton button1(0); TroykaButton button2(1); TroykaButton button3(4); TroykaButton button4(5); // пин пищалки #define BUZZER_PIN 10 // интервал времени до сметри // если инопланетянин не получил потребность #define INTERVAL_DEATH 10000 // первое состояние инопланиянина const uint8_t stateAlienOne[][8] { { 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00}, }; // второе состояние инопланиянина const uint8_t stateAlienTwo[][8] { { 0x00, 0x00, 0x00, 0x18, 0x3C, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x00, 0x0C, 0x1E, 0x00, 0x00} }; // третье состояние инопланиянина const uint8_t stateAlienThree[][8] { { 0x00, 0x00, 0x00, 0x38, 0x54, 0x7C, 0x00, 0x00}, { 0x00, 0x00, 0x1C, 0x2A, 0x3E, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x38, 0x54, 0x7C, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x1C, 0x2A, 0x3E, 0x00, 0x00} }; // четвёртое состояние инопланиянина const uint8_t stateAlienFour[][8] { { 0x00, 0x00, 0x1C, 0x2A, 0x3E, 0x2A, 0x00, 0x00}, { 0x00, 0x00, 0x38, 0x54, 0x7C, 0x2A, 0x00, 0x00}, { 0x00, 0x00, 0x38, 0x54, 0x7C, 0x54, 0x00, 0x00}, { 0x00, 0x00, 0x1C, 0x2A, 0x3E, 0x54, 0x00, 0x00}, }; // пятое состояние инопланиянина const uint8_t stateAlienFive[][8] { { 0x00, 0x18, 0x3C, 0x5A, 0x7E, 0x3C, 0x00, 0x00}, { 0x00, 0x00, 0x18, 0x3C, 0x5A, 0x7E, 0x5A, 0x00}, { 0x00, 0x00, 0x18, 0x3C, 0x5A, 0x7E, 0x99, 0x00}, { 0x00, 0x18, 0x3C, 0x5A, 0x7E, 0x5A, 0x00, 0x00} }; // шестое состояние инопланиянина const uint8_t stateAlienSix[][8] { { 0x00, 0x38, 0x7C, 0xD6, 0xFE, 0xBA, 0x92, 0x10}, { 0x00, 0x1C, 0x3E, 0x6B, 0x7F, 0x59, 0x92, 0x20}, { 0x00, 0x1C, 0x3E, 0x6B, 0x7F, 0x5D, 0x49, 0x08}, { 0x00, 0x38, 0x7C, 0xD6, 0xFE, 0x9A, 0x49, 0x04} }; // отображение «поесть» const uint8_t food[][8] { { 0x00, 0x20, 0x44, 0x28, 0x04, 0x00, 0x7E, 0x3C}, { 0x40, 0x28, 0x44, 0x08, 0x00, 0x00, 0x7E, 0x3C}, }; // отображение «попить» const uint8_t drink[][8] { { 0x00, 0x24, 0x24, 0x3C, 0x34, 0x2C, 0x34, 0x3C}, { 0x00, 0x24, 0x24, 0x3C, 0x2C, 0x34, 0x2C, 0x3C}, }; // отображение «приласкать» const uint8_t love[][8] { { 0x00, 0x66, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00}, { 0x00, 0x24, 0x7E, 0x7E, 0x3C, 0x18, 0x00, 0x00}, }; // отображение «поспать» const uint8_t sleep[][8] { { 0x00, 0x00, 0x00, 0x80, 0x80, 0xFE, 0xFF, 0x81}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; // отображение «OK» const uint8_t ok[] { 0x00, 0x64, 0x94, 0x95, 0x96, 0x95, 0x65, 0x00 }; // отображение «смерти» const uint8_t death[] { 0x00, 0x40, 0xE0, 0x40, 0x40, 0x7C, 0xFE, 0xFF }; // перечесления enum {ONE, TWO, THREE, FOUR, FIVE, SIX}; enum {NOT, FOOD, DRINK, LOVE, SLEEP}; // размер инопланетянина int alienSize; // желание инопланетянина int alienDesire; // номер кадра анимации инопланитянина int frameAnimationAlien = 0; // номер кадра анимации желания инопланитянина int frameAnimationDesire = 0; // переменные запоминания времени unsigned long desireMillis; unsigned long animationMillis; unsigned long deathMillis; // переменные включения таймеров состояния bool timerEnableDesire = false; bool timerEnableDeath = false; // случайный диапазон времени // через который у инопланитянина появится новая потребность int intervalDesire; void setup() { // начало работы с матрицами matrix1.begin(); matrix2.begin(); // очищаем матрицы matrix1.clear(); matrix2.clear(); // начало работы с кнопками button1.begin(); button2.begin(); button3.begin(); button4.begin(); // начальный размер инопланитянина alienSize = ONE; // желаний нет alienDesire = NOT; // запоминаем текущее время animationMillis = millis(); // для генерации разных значений randomSeed(analogRead(A0)); } void loop() { // считываем данные с кнопок button1.read(); button2.read(); button3.read(); button4.read(); // отображение элементов на матрице if (millis() - animationMillis > 500) { animationMillis = millis(); updateBitmapAnimationAlien(); updateBitmapAnimationDesire(); } // если у инопланитянина нет желаний и таймер не запущен if (alienDesire == NOT && !timerEnableDesire) { // выбираем случайный период времени // через который инопланетянин выявит в чём то потребность intervalDesire = random(1000, 5000); // запоминаем текущее время desireMillis = millis(); // включаем таймер ожидание появление потребности timerEnableDesire = true; } // если таймер включён и период выявлеения желания прошёл if (timerEnableDesire && millis() - desireMillis > intervalDesire) { // выбираем случайное желание alienDesire = random(1, 5); // выключаем таймер ожидание появление потребности timerEnableDesire = false; // запоминаем текущее время deathMillis = millis(); // выключаем таймер ожидания смерти timerEnableDeath = true; } // если таймер смерти включён и прошёл свой интервал if(timerEnableDeath && millis() - deathMillis > INTERVAL_DEATH) { // отображаем могилку matrix1.drawBitmap(death); // стираем потребность в чём то // так как умершей в ней уже не нуждается matrix2.clear(); for (int y = 0; y <= 2; y++) { // издаём звуковой сигнал tone(BUZZER_PIN, 200, 300); delay(200); // издаём звуковой сигнал tone(BUZZER_PIN, 131, 300); delay(200); } // котроллер отправляем тоже в вечный сон while(1); } // проверяем состояние потребности инопланетянин switch(alienDesire) { case FOOD: // если нажата соответсвующая кнопка if (button1.justPressed()) { // выводим «ОК» на матрицу matrix2.drawBitmap(ok); // подаём звуковой сигнал tone(BUZZER_PIN, 2000, 300); delay(1000); // увеличиваем размер инопланетянина alienSize++; // убираем потребность alienDesire = NOT; } else if (button2.justPressed() || button3.justPressed() || button4.justPressed()) { // если нажата не соответсвующая кнопка // подаём соответсвующий звуковой сигнал tone(BUZZER_PIN, 500, 500); } break; case DRINK: // если нажата соответсвующая кнопка if (button2.justPressed()) { // выводим «ОК» на матрицу matrix2.drawBitmap(ok); // подаём звуковой сигнал tone(BUZZER_PIN, 2000, 300); delay(1000); // увеличиваем размер инопланетянина alienSize++; // убираем потребность alienDesire = NOT; } else if (button1.justPressed() || button3.justPressed() || button4.justPressed()) { // если нажата не соответсвующая кнопка // подаём соответсвующий звуковой сигнал tone(BUZZER_PIN, 500, 500); } break; case LOVE: // если нажата соответсвующая кнопка if (button3.justPressed()) { // выводим «ОК» на матрицу matrix2.drawBitmap(ok); // подаём звуковой сигнал tone(BUZZER_PIN, 2000, 300); delay(1000); // увеличиваем размер инопланетянина alienSize++; // убираем потребность alienDesire = NOT; } else if (button1.justPressed() || button2.justPressed() || button4.justPressed()) { // если нажата не соответсвующая кнопка // подаём соответсвующий звуковой сигнал tone(BUZZER_PIN, 500, 500); } break; case SLEEP: if (button4.justPressed()) { // выводим «ОК» на матрицу matrix2.drawBitmap(ok); // подаём звуковой сигнал tone(BUZZER_PIN, 2000, 300); delay(1000); // увеличиваем размер инопланетянина alienSize++; // убираем потребность alienDesire = NOT; } else if (button1.justPressed() || button2.justPressed() || button3.justPressed()) { // если нажата не соответсвующая кнопка // подаём соответсвующий звуковой сигнал tone(BUZZER_PIN, 500, 500); } break; } } // функция обновления анимации инопланитянина void updateBitmapAnimationAlien() { // ощичаем матрицу matrix1.clear(); if (frameAnimationAlien == 4) { frameAnimationAlien = 0; } // отображаем двигающегося инопланетянина по кадрово switch (alienSize) { case ONE: matrix1.drawBitmap(stateAlienOne[frameAnimationAlien++]); break; case TWO: matrix1.drawBitmap(stateAlienTwo[frameAnimationAlien++]); break; case THREE: matrix1.drawBitmap(stateAlienThree[frameAnimationAlien++]); break; case FOUR: matrix1.drawBitmap(stateAlienFour[frameAnimationAlien++]); break; case FIVE: matrix1.drawBitmap(stateAlienFive[frameAnimationAlien++]); break; case SIX: matrix1.drawBitmap(stateAlienSix[frameAnimationAlien++]); break; } } // функция обновления анимации желаний инопланитянина void updateBitmapAnimationDesire() { // ощичаем матрицу matrix2.clear(); if (frameAnimationDesire == 2) { frameAnimationDesire = 0; } // отображаем потребность по кадрово switch (alienDesire) { case NOT: matrix2.clear(); break; case FOOD: matrix2.drawBitmap(food[frameAnimationDesire++]); break; case DRINK: matrix2.drawBitmap(drink[frameAnimationDesire++]); break; case LOVE: matrix2.drawBitmap(love[frameAnimationDesire++]); break; case SLEEP: matrix2.drawBitmap(sleep[frameAnimationDesire++]); break; } } ====== Что дальше? ====== Хотите собрать другой девайс? Выберите своё будущее устройство из [[slot-box:start#хочу_собрать|списка проектов на Slot Shield]]. Если вы захотите изменить графику, рекомендуем воспользоваться удобным [[https://xantorohara.github.io/led-matrix-editor/|редактором для LED матриц]]. * анимация [[https://goo.gl/C4p3Ma|жизненного цикла питомца]] * [[https://goo.gl/t5rQou|просьбы и сообщения]] ====== Часто задаваемые вопросы ====== > Где скачать необходимые библиотеки и как их установить? * [[https://github.com/amperka/TroykaLedMatrix|библиотека для работы с LED-матрицей]] * [[программирование:библиотеки|Как установить библиотеку]]