Вот что у нас должно получиться, ну он еще умеет прыгать, ходить и бить злые кактусы, которые на него нападают, но к этому придем поэтапно 🙂
ЧАСТЬ #1 основы
Я заказал себе arduino, «так себе игрушка» подумал я, комплект маленький (для пробы) о чем в последствии пожалел. Хотелось раскрыть потенциал, но из-за отсутствия дополнительных модулей этого не выходило, пришлось экспериментировать, подрубал ardruino к системе безопасности и смотрел как датчики делают свою работу, потом решил сделать звуковую сигнализацию (используя пративную пищалку из комплекта), так сказать отучивал собак громко или неожиданно лаять 🙂 и тут мои руки дошли до дисплея 1602. «Хм… это же настоящий дисплей» подумал я, но тут же разочаровался узнав что он сжирает почти половину всех контактов на самой ardruino. Покопавшись я нашел странную плату в комплекте «i2C» и очень подозрительно было ТО! Что количество дырдочек совпало с количеством пимпочек на дисплее. «Хм, не с проста…» подумал я, и решил их спаять. Чуть позже я понял что сделал верную штуку и теперь мой дисплей съедает всего два канала. Начал изучать, что же это за дисплей и что он умеет. Изучив достаточное количество материала я узнал, что дисплей чисто текстовый, но о чудо! Он может обработать 8 новых символов, габаритами 5х8 пикселей. Ну что же, давайте начнем писать игру! Первое, это надо придумать что за игра будет, решил сделать подобие динозаврика гуугл хром, но с парочкой фишек так сказать, для начала я думаю сойдет 🙂 но ведь надо еще чем-то управлять, причем многокнопочным, долго думать не пришлось. Взял ИК пульт из комплекта.
«Вот и джойстик» подозрительно пробормотал я себе под нос, думая о задержке от пульта, четкости работы ИК датчика да и вообще об адекватности данной идеи, но делать было нечего, я мог бы обучить ardruino работать с клавиатурой для компа, но было действительно лень это делать. Так что приступил я записывать коды пульта, что бы в дальнейшем с ними работать. Код для микроконтролера тут простейший:
"--------------------------------------------------------------------------" // качаем библиотеку IRremote #include IRrecv irrecv (A0) // включаем аналоговый порт для датчика Void setup () { Serial.begin(9600); // настраиваем скорость com порта Irrecv.enableIRIn(); // запускаем сам сенсор } Void loop () { If (irrecv.decode( &result )) // если датчик видит любой ИК сигнал, то условие выполнено { Serial.printIn (result.value, HEX); //считываем код с пульта и выводим его в логи порта } } "--------------------------------------------------------------------------"
После заливки сего в ardruino и подключив его как надо, мы можем начать записывать с лога порта цифорки, после нажатия на кнопки ИК устройства. Но тут как раз я хочу вам уточнить про то, как надо подключать датчик ИК.
Если мы смотрим на датчик, мы видим три ножки, левая (аналоговый сигнал), средняя (масса), правая (плюс 5V).
Так как я еще мало представлял как это будет вообще работать, я начал эксперименты. Сначала делал код скетча шаговый, через (delay(time)) сначала я не подозревал что это плохая идея 🙂
В чем главный косяк. Данный микроконтроллер не умеет делать мультизадачность. Он считает код сверху вниз, проходя по всем веткам и функциям и после завершения, он начинает заново. И вот, когда у нас этих «delay» в коде становиться очень много, мы начинаем замечать явные задержки. Кстати да, зачем нам много «delay» вообще нужно. Когда мы делаем игру, у нас начинает расти количество проверок и взаимодействий. Например к нам движется враг а я хочу его перепрыгнуть, я нажимаю «прыжок» а по плану, я должен зависнуть в воздухе к примеру на 0.8f секунд в воздухе, вот и вся игра и зависает на эти 0.8f секунды. «Косяк» подумал я и начал думать над решением. Решение было найдено быстро. Сам микроконтроллер умеет достаточно быстро читать код от начала до конца, (если ему не мешать) и еще он умеет считать время с начала его включения. Вот это то нам и надо. Теперь мы всего лишь создаем переменные которые будут хранить время на то или иное действие и переменную которая сверяет разницу от того сколько сейчас время и во сколько надо активировать код. 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й строке и качается, так сказать.
Вывод: сегодня я рассказал как узнать данные через ИК порт, как обойти задержку работы кода микроконтроллера и как сделать начальную анимацию.
Остальное скоро 🙂 писать еще очень много чего, так что гляну как это вообще будет вам интересно и если да, то завтра же приступлю к написанию продолжения.
Всем спасибо за внимание, чао-какао!
ссылка на оригинал статьи https://habr.com/post/424859/
Добавить комментарий