Оглавление:

Игра Arduino OLED Snake: 3 шага
Игра Arduino OLED Snake: 3 шага

Видео: Игра Arduino OLED Snake: 3 шага

Видео: Игра Arduino OLED Snake: 3 шага
Видео: Игра змейка (snake) на Arduino 2024, Ноябрь
Anonim
Игра Arduino OLED Snake
Игра Arduino OLED Snake

Привет и добро пожаловать, в наши инструкции по созданию OLED-игры для arduino, этот проект появился в то время, когда мы пытались сделать нашу первую игру с Arduino, ооооо, мы подумали, с чего лучше начать, чем с классической игры Snake от nokia (ну в хоть змеиный клон:)).

Что тебе понадобится

Запасы

Arduino UNO или клон

OLED-дисплей

4 диода

Резистор 500-1к

4 кнопки контроллера

Пассивный пьезо-зуммер

по желанию

Доска для хлеба без пайки

Обратите внимание, что эти ссылки приведены только для примера.

Шаг 1: Схема

Схема
Схема

На изображении выше вы можете видеть нашу схему, мы используем вывод d3 на Arduino в качестве вывода запроса прерывания, так что Arduino будет отдавать приоритет чтению входов контроллера, которые являются d4 d5 d6 d7. Основа схемы заключается в том, что нажимается кнопка направления, которая достигает высокого уровня 5 В, это активирует вывод запроса прерывания (фиолетовый провод d3) и соответствующий вывод направления, функция прерывания вызывает функцию направления обновления, и этот код соответственно перемещает змейку. Вывод 9 используется в качестве звукового вывода, поскольку это ШИМ (~ широтно-импульсная модуляция), который подключен непосредственно к пьезоэлементу 5 В на выводе +, а - возвращается к 0 В / земля.

(К вашему сведению, на arduino uno и клонах только d2 и d3 могут действовать как контакты запроса прерывания).

Штифты направления:

d4 Вверх ОРАНЖЕВЫЙ

d5 вниз РОЗОВЫЙ

d6 Левый СИНИЙ

d7 Правый КОРИЧНЕВЫЙ

d9 звук СЕРЫЙ

Каждая кнопка имеет вход для подключения 5 В и выход, который сначала подключается к соответствующему цифровому входу на Arduino, этот же выход каждой кнопки затем подключается к собственному диоду, мы используем диоды, чтобы остановить обратную подачу напряжения на другие кнопки и их активация. На катодном (-) конце всех 4 диодов мы соединяем их вместе, чтобы создать выходной переход, который подключается к d3, а затем через резистор к 0 В / землю, чтобы подтянуть контакты Arduino к низкому уровню, чтобы не оставлять плавающие контакты, когда они не подключены. активирован.

(К вашему сведению, плавающий вывод может получить фантомное напряжение и вызвать необычное поведение)

2 аналоговых вывода используются для управления дисплеем, это выводы i2c оборудования Arduino.

A5 подключен к SCL ЖЕЛТЫЙ

A4 подключен к SDA GREEN

Выходы + 5 В и 0 В (земля) от Arduino используются в качестве источника питания для всей цепи, которая может питаться от USB или зарядного устройства для телефона.

Шаг 2: Код

// ------------------------ ANJAWARE SNAKE Games С помощью людей сети --------------- -------

#включают

#include // https://github.com/adafruit/Adafruit-GFX-Library #include // https://github.com/adafruit/Adafruit-GFX-Library // отображение набора (ширина, высота) Adafruit_SSD1306 display (128, 64); // определяем входные контакты это контакты на Arduino, они никогда не меняются, поэтому #define #define INTPIN 3 // только контакты 2 и 3 могут быть прерваны контактами на UNO #define UPPIN 4 // это контакты, подключенные к соответствующему переключателю #define DWNPIN 5 #define LFTPIN 6 #define RHTPIN 7 #define SND 9 // определение направлений #define DIRUP 1 // на эти значения «змея» смотрит, чтобы решить- #define DIRDOWN 2 // направление, в котором будет двигаться змея # define DIRLEFT 3 #define DIRRIGHT 4

// устанавливаем переменные кнопки

// volitile, потому что нам нужно, чтобы он обновлялся с помощью прерывания, поэтому может быть любым битом значения цикла

// никогда не превышает 4, поэтому для экономии ресурсов требуется только 8-битное int volatile uint8_t buttonpressed = 0; bool butup = 0; bool butdown = 0; // мы используем это, чтобы установить true, чтобы «определять», в каком направлении нажимается bool butleft = 0; bool butright = 0;

// змеиные вставки

byte snakePosX [30]; // массив для создания тела змеи байтом snakePosY [30];

int snakeX = 30; // положение головы змеи

int snakeY = 30; int snakeSize = 1; // счетчик размера змейки ограничен размером массива

// мировые целые числа

uint8_t worldMinX = 0; // они устанавливают пределы игровой области

uint8_t worldMaxX = 128; uint8_t worldMinY = 10; uint8_t worldMaxY = 63;

// сбор мусора (еды) и положение мусора

bool scranAte = 0; uint8_t scranPosX = 0; uint8_t scranPosY = 0;

// оценивает переменные

long playscore = 0; длинный рекорд = 30; // устанавливаем высокий балл на 3 сбор в качестве отправной точки

// --------------------------- это то, что выполняет прерывание при повышении напряжения ------------ -------------

void interruptpressed () {задержка (150); // небольшая задержка для добавления защиты от «дребезга» updatedirection (); } // ------------------ обновить значение направления после нажатия кнопки ----------------- void updatedirection () { // Serial.println ("направление обновления"); butup = digitalRead (UPPIN); // проверяем, какой вход стал высоким, и устанавливаем соответствующий bool true butdown = digitalRead (DWNPIN); butleft = digitalRead (LFTPIN); butright = digitalRead (RHTPIN); // это, если средства состояния смотрят, какой вход был повышен, и вводят соответствующее значение в переменную "buttonpressed" //, эта переменная определяет направление движения if (butup == true) {buttonpressed = DIRUP; // Serial.println ("нажата кнопка ВВЕРХ"); // Serial.println (кнопка нажата); butup = false; тон (СНД, 1500, 10); } если (butdown == true) {buttonpressed = DIRDOWN; // Serial.println ("ВНИЗ нажата"); // Serial.println (кнопка нажата); butdown = false; тон (СНД, 1500, 10); }

если (butleft == true)

{buttonpressed = DIRLEFT; // Serial.println ("Нажата ВЛЕВО"); // Serial.println (кнопка нажата); butleft = false; тон (СНД, 1500, 10); } если (butright == true) {buttonpressed = DIRRIGHT; // Serial.println ("Нажат ВПРАВО"); // Serial.println (кнопка нажата); butright = false; тон (СНД, 1500, 10); }}

// -------------------------- отрисовываем процедуры отображения ------------------ -----------------

void updateDisplay () // рисуем оценки и контуры

{// Serial.println ("Обновить отображение");

display.fillRect (0, 0, display.width () - 1, 8, ЧЕРНЫЙ);

display.setTextSize (0); display.setTextColor (БЕЛЫЙ); // выводим результаты display.setCursor (2, 1); display.print ("Оценка:"); display.print (String (playscore, DEC)); display.setCursor (66, 1); display.print ("Высокий:"); display.print (String (рейтинг, DEC)); // рисуем игровую область // pos 1x, 1y, 2x, 2y, color display.drawLine (0, 0, 127, 0, WHITE); // самая верхняя граница display.drawLine (63, 0, 63, 9, БЕЛЫЙ); // разделитель очков display.drawLine (0, 9, 127, 9, БЕЛЫЙ); // под рамкой текста display.drawLine (0, 63, 127, 63, БЕЛЫЙ); // нижняя граница display.drawLine (0, 0, 0, 63, БЕЛЫЙ); // левая граница display.drawLine (127, 0, 127, 63, БЕЛЫЙ); // правая граница

}

// ----------------------------------- обновить игровую зону ---------- --------------------

void updateGame () // это обновляет отображение игровой области

{display.clearDisplay ();

display.drawPixel (scranPosX, scranPosY, БЕЛЫЙ);

scranAte = scranFood ();

// проверяем подпрограммы змейки

если (outOfArea () || selfCollision ())

{ игра закончена(); }

// отображаем змейку

для (int i = 0; i0; i--) {snakePosX = snakePosX [i-1]; snakePosY = snakePosY [i-1]; } // добавляем дополнительный пиксель к змейке if (scranAte) {snakeSize + = 1; snakePosX [snakeSize-1] = snakeX; snakePosY [snakeSize-1] = snakeY; }

переключатель (кнопка нажата) // было snakeDirection

{case DIRUP: snakeY- = 1; перерыв; case DIRDOWN: snakeY + = 1; перерыв; case DIRLEFT: snakeX- = 1; перерыв; case DIRRIGHT: snakeX + = 1; перерыв; } snakePosX [0] = snakeX; snakePosY [0] = snakeY; updateDisplay (); display.display (); // --------------------- размещаем скран -------------------

пусто местоScran ()

{scranPosX = random (worldMinX + 1, worldMaxX-1); scranPosY = random (worldMinY + 1, worldMaxY-1); } // ------------------------ СКАНИРОВАТЬ ТОЧКУ ВВЕРХ ---------------- bool scranFood () {если (snakeX == scranPosX && snakeY == scranPosY) {playscore = playscore + 10; тон (СНД, 2000, 10); updateDisplay (); placeScran (); возврат 1; } else {return 0; }} // --------------------- вне зоны действия ---------------------- bool outOfArea () {return snakeX = worldMaxX || snakeY = worldMaxY; } //---------------------- игра закончена----------------------- --- void gameOver () {uint8_t rectX1, rectY1, rectX2, rectY2; rectX1 = 38; rectY1 = 28; rectX2 = 58; rectY2 = 12; display.clearDisplay (); display.setCursor (40, 30); display.setTextSize (1); тон (СНД, 2000, 50); display.print («ИГРА»); тон (СНД, 1000, 50); display.print («НАВЕРХ»); if (playscore> = highscore) // проверяем, выше ли результат, чем high score {highscore = playscore; // одиночный, если статус для обновления рекорда} for (int i = 0; i <= 16; i ++) // это для рисования прямоугольников вокруг игры над {display.drawRect (rectX1, rectY1, rectX2, rectY2, WHITE); Serial.println ("если цикл"); display.display (); rectX1- = 2; // сдвиг на 2 пикселя rectY1- = 2; rectX2 + = 4; // сдвиг на 2 пикселя от последней точки rectY2 + = 4; тон (СНД, i * 200, 3); } display.display (); // Очистка экрана после фейма над rectX1 = 0; // устанавливаем начальную позицию строки rectY1 = 0; rectX2 = 0; rectY2 = 63; для (int я = 0; я <= 127; я ++) {uint8_t cnt = 0; display.drawLine (rectX1, rectY1, rectX2, rectY2, ЧЕРНЫЙ); rectX1 ++; rectX2 ++; display.display (); } display.clearDisplay (); playscore = 0; // сбросить данные змейки и игрока snakeSize = 1; snakeX = display.width () / 2; snakeY = display.height () / 2; waitForPress (); // ждем, пока игрок начнет игру} // ------------------------- ждать цикла нажатий ---------- --------------- void waitForPress () {bool wait = 0; // цикл заканчивается, если это правда display.clearDisplay (); пока (ожидание == 0) {drawALineForMe (БЕЛЫЙ); // рисуем случайную белую линию drawALineForMe (BLACK); // рисуем случайную черную линию, чтобы экран не полностью заполнял белый цвет display.fillRect (19, 20, 90, 32, ЧЕРНЫЙ); // пустой фон для текста display.setTextColor (WHITE); display.setCursor (35, 25); display.setTextSize (2); // более крупный шрифт display.println ("SNAKE"); // x y w h r col display.drawRoundRect (33, 22, 62, 20, 4, БЕЛЫЙ); // граница змейки display.drawRect (19, 20, 90, 32, БЕЛЫЙ); // рамка рамки - 3 display.setCursor (28, 42); display.setTextSize (0); // возврат шрифта к нормальному display.println ("нажать любую клавишу"); display.display (); ожидание = digitalRead (INTPIN); // проверяем, изменится ли ожидание нажатия клавиши на 1, заканчивающееся при нажатой кнопке = 0; // нажать кнопку сброса}} // -------------------- НАРИСИМ случайный цвет линии ввода uint8_t -------------- ----- void drawALineForMe (uint8_t clr) {uint8_t line1X, line1Y, line2X, line2Y = 0; // задаем случайные координаты для линии, затем рисуем ее // переменная не менее не более line1X = random (worldMinX + 1, worldMaxX-1); line1Y = random (worldMinY + 1, worldMaxY-1); line2X = случайный (worldMinX + 1, worldMaxX-1); line2Y = random (worldMinY + 1, worldMaxY-1); display.drawLine (line1X, line1Y, line2X, line2Y, clr); } // ------------------------------------- обнаружение столкновений -------- -----------------------

for (byte i = 4; i <snakeSize; i ++) {if (snakeX == snakePosX && snakeY == snakePosy ) {return 1; тон (СНД, 2000, 20); тон (СНД, 1000, 20); } return 0; }

//-------------------------------- НАСТРАИВАТЬ--------------- -------------------------------

void setup () {задержка (100); // просто даем возможность "загрузиться" // Serial.begin (9600); // снимите этот флажок, если хотите видеть последовательные выходы display.begin (SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay (); // начать с чистого дисплея display.setTextColor (WHITE); // установить размер поворота цвета текста и т. д. display.setRotation (0); display.setTextWrap (ложь); display.dim (0); // устанавливаем яркость отображения pinMode (INTPIN, INPUT); // установить правильные порты на входы pinMode (UPPIN, INPUT); pinMode (DWNPIN, ВХОД); pinMode (LFTPIN, ВХОД); pinMode (RHTPIN, ВХОД); // это команда прерывания, которая "останавливает" arduino для чтения входов // команда-функция-контакт-функция для выполнения-условия на контакте attachInterrupt (digitalPinToInterrupt (INTPIN), interruptpressed, RISING); // Serial.println ("Настройка прошла"); waitForPress (); // отображаем стартовый экран змейки placeScran (); // размещаем первый кусочек еды} // --------------------- ГЛАВНЫЙ ЦИКЛ ----------------- ---------------------- void loop () {updateGame (); // эта функция несет основной код}

Шаг 3:

Рекомендуемые: