====== Тамагочи «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-матрицей]]
* [[программирование:библиотеки|Как установить библиотеку]]