Хабрахабр

[Из песочницы] Простейшая игра на Ardruino с дисплеем 1602 — Часть #1

Вот что у нас должно получиться, ну он еще умеет прыгать, ходить и бить злые кактусы, которые на него нападают, но к этому придем поэтапно 🙂
Я заказал себе arduino, «так себе игрушка» подумал я, комплект маленький (для пробы) о чем в последствии пожалел. Хотелось раскрыть потенциал, но из-за отсутствия дополнительных модулей этого не выходило, пришлось экспериментировать, подрубал ardruino к системе безопасности и смотрел как датчики делают свою работу, потом решил сделать звуковую сигнализацию (используя пративную пищалку из комплекта), так сказать отучивал собак громко или неожиданно лаять 🙂 и тут мои руки дошли до дисплея 1602. «Хм… это же настоящий дисплей» подумал я, но тут же разочаровался узнав что он сжирает почти половину всех контактов на самой ardruino. Покопавшись я нашел странную плату в комплекте «i2C» и очень подозрительно было ТО! Что количество дырдочек совпало с количеством пимпочек на дисплее. «Хм, не с проста...» подумал я, и решил их спаять. Чуть позже я понял что сделал верную штуку и теперь мой дисплей съедает всего два канала. Начал изучать, что же это за дисплей и что он умеет. Изучив достаточное количество материала я узнал, что дисплей чисто текстовый, но о чудо! Он может обработать 8 новых символов, габаритами 5х8 пикселей. Ну что же, давайте начнем писать игру! Первое, это надо придумать что за игра будет, решил сделать подобие динозаврика гуугл хром, но с парочкой фишек так сказать, для начала я думаю сойдет 🙂 но ведь надо еще чем-то управлять, причем многокнопочным, долго думать не пришлось. Взял ИК пульт из комплекта.

image

Так что приступил я записывать коды пульта, что бы в дальнейшем с ними работать. «Вот и джойстик» подозрительно пробормотал я себе под нос, думая о задержке от пульта, четкости работы ИК датчика да и вообще об адекватности данной идеи, но делать было нечего, я мог бы обучить ardruino работать с клавиатурой для компа, но было действительно лень это делать. Код для микроконтролера тут простейший:

"--------------------------------------------------------------------------"
// качаем библиотеку IRremote
#include
IRrecv irrecv (A0) // включаем аналоговый порт для датчика
Void setup ()
{ Serial.begin(9600); // настраиваем скорость com порта Irrecv.enableIRIn(); // запускаем сам сенсор
}
Void loop ()

} "--------------------------------------------------------------------------"

После заливки сего в ardruino и подключив его как надо, мы можем начать записывать с лога порта цифорки, после нажатия на кнопки ИК устройства. Но тут как раз я хочу вам уточнить про то, как надо подключать датчик ИК.

Если мы смотрим на датчик, мы видим три ножки, левая (аналоговый сигнал), средняя (масса), правая (плюс 5V).

image

Сначала делал код скетча шаговый, через (delay(time)) сначала я не подозревал что это плохая идея 🙂
В чем главный косяк. Так как я еще мало представлял как это будет вообще работать, я начал эксперименты. Он считает код сверху вниз, проходя по всем веткам и функциям и после завершения, он начинает заново. Данный микроконтроллер не умеет делать мультизадачность. Кстати да, зачем нам много «delay» вообще нужно. И вот, когда у нас этих «delay» в коде становиться очень много, мы начинаем замечать явные задержки. Например к нам движется враг а я хочу его перепрыгнуть, я нажимаю «прыжок» а по плану, я должен зависнуть в воздухе к примеру на 0. Когда мы делаем игру, у нас начинает расти количество проверок и взаимодействий. 8f секунды. 8f секунд в воздухе, вот и вся игра и зависает на эти 0. Решение было найдено быстро. «Косяк» подумал я и начал думать над решением. Вот это то нам и надо. Сам микроконтроллер умеет достаточно быстро читать код от начала до конца, (если ему не мешать) и еще он умеет считать время с начала его включения. Ardruino за секунду, берет 1000 миллисекунд, достаточно удобно. Теперь мы всего лишь создаем переменные которые будут хранить время на то или иное действие и переменную которая сверяет разницу от того сколько сейчас время и во сколько надо активировать код. Вот фрагмент когда что бы стало понятнее:

"--------------------------------------------------------------------------"
// данный пример фрагмента кода, очищает экран, грубо говоря это наша частота обновления кадров
// переменные
long ClearTime = 150; // 150 = 0.15f секунды или ~6 кадров в секунду
long ClearTimeCheck = 0; // проверка, будет меняться в процессе работы кода
long currentMillis = 0; // переменная таймера void loop ()
{ currentMillis = millis(); // переменная таймера = время в миллисекундах
}
void clearscreen () //функция обновления экрана
{ // if (currentMillis - ClearTimeCheck >= ClearTime) // если (время работы - проверка больше или равно 0.15f то условие выполнено { ClearTimeCheck = currentMillis; // выравниваем проверку для обнуления нашего счетчика lcd.clear(); // выполняем само действие, а именно очистку экрана }
} "--------------------------------------------------------------------------"

Не трудно, правда?

Ведь нам надо еще сделать персонажа, подобие интерфейса и анимации. После переписывания всего кода на новый лад, игра стала работать быстрео и четко, симулировать мультизадачные действия 🙂 Я что-то далеко зашел. На дисплее много объектов делать я пока что не планирую, следовательно, можно сделать так что бы у меня было как раз восемь активных объектов на экране за одну обработку кода. Так как мы можем создавать всего восемь новых символов, нам надо как-то это все промутить по умному. Ну естественно главный герой, удар, злодей, сердечко и индикатор здоровья. Что же это будет? Да и у меня еще три уникальных объекта в запасе. Для начала этого с головой хватит.

Главный герой будет у нас выглядеть так:

Процесс вписывания нового символа, я произвожу двоичным кодом (мне так удобно)
выглядеть он будет так:

01110
01110
00100
01110
10101
00100
01110
01010

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

Теперь к нашему коду, добавиться еще один набор двоичных цифорок, а именно такой:

00000
01110
01110
00100
11111
00100
01110
01010

Это легче чем кажется: Как сделать анимацию на этом дисплее, логику я указал выше, а теперь перейдем к практике, в данный момент, расположим его на центр экрана, и заставим просто стоять на месте, и помните, наша задача использовать только одну ячейку памяти на два спрайта анимации.

"--------------------------------------------------------------------------"
#include <Wire.h> #include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F,16,2); // Устанавливаем дисплей long AnimatedTime = 300; // скорость анимации главного герое
long AnimatedTimeCheck = 0; // проверка скорости (как и в прошлом примере)
int AnimPlayer = 1; // проверка состояния анимации int GGpozX = 8; // положение горизонталь
int GGpozY = 1; // положение вертикаль 1 это 2я строка а 0 это первая строка
long currentMillis = 0; // переменная таймера //Создаем переменные наших объектов, их может быть сколько угодно, они же переменные :)
enum { SYMBOL_HEIGHT = 8 };
byte Player_1[SYMBOL_HEIGHT] = {B01110,B01110,B00100,B01110,B10101,B00100,B01110,B01010,}; // спрайт 1
byte Player_2[SYMBOL_HEIGHT] = {B00000,B01110,B01110,B00100,B11111,B00100,B01110,B01010,}; // спрайт 2 void setup() { lcd.init(); lcd.backlight();// Включаем подсветку дисплея loop(); PlAn();
} void loop() { if (AnimPlayer != 50) { // это проверка смерти персонажа, пока что не забивайте себе голову :) // --------------------------- Animated -> // -------------------- Player -> if (AnimPlayer == 1){lcd.createChar(0, Player_1);} //если состояние 1 то спрайт 1 //(lcd.createChar(номер ячейки памяти от 0 до 7, название переменной спрайта)) if (AnimPlayer == 2){lcd.createChar(0, Player_2);} //если состояние 2 то спрайт 2 } // --------------------------- <- Animated currentMillis = millis(); // переменная таймера = время в миллисекундах PlAn();
}
void PlAn ()
{ if (AnimPlayer == 1) // если состояние 1 то { lcd.setCursor(GGpozX, GGpozY); // ставим "курсор" на точку координат нашего героя lcd.write(0); // рисуем спрайт из ячейки памяти на то место где "курсор" } if (AnimPlayer == 2) // аналогично №1 { lcd.setCursor(GGpozX, GGpozY); lcd.write(0); } if (currentMillis - AnimatedTimeCheck >= AnimatedTime) // проверка времени как и до этого { AnimatedTimeCheck = currentMillis; // ну тут уже понятно if (AnimPlayer == 2){AnimPlayer = 1; return;} //если положение 2 то делаем 1 и стопорим этот фрагмент кода if (AnimPlayer == 1){AnimPlayer = 2;} //если 1 то 2 и стопорить нет смысла так что не забиваем память лишним кодом, ее у нас там и так очень мало }
} "--------------------------------------------------------------------------"

После запуска, мы видим чУловечка, который находиться в центре экрана, на 2й строке и качается, так сказать.

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

Остальное скоро 🙂 писать еще очень много чего, так что гляну как это вообще будет вам интересно и если да, то завтра же приступлю к написанию продолжения.

Всем спасибо за внимание, чао-какао!

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»