Движение игрока

Программирование игры на Ардуино: движения игрока

При написании программы мы используем набор техник, которые были рассмотрены в разных уроках на нашем сайте. Во-первых, нам потребуется знать основы программирования: переменные разного типа, циклы, условия, массивы, функции. Во-вторых — основы работы с электроникой в целом и с Ардуино в частности: сборка устройства по принципиальной схеме, работа с макетной платой, считывание сигнала с кнопки, передача сигнала на динамик. В-третьих следует ознакомиться с техникой выполнения параллельных задач таймеру и с поработать с графическими индикаторами, особенно с OLED дисплеями.

Первое, что мы реализуем в программе нашего игрового автомата — движение игрока по игровому полю. Нам нужно, чтобы контроллер определял, какую из двух кнопок мы нажимаем и реагировал на это перемещением игрока влево или вправо.

Для начала будем перемещать по полю один пиксел, символизирующий игрока. Чтобы выводить пиксел на дисплей, нужно будет воспользоваться какой-нибудь библиотекой для работы с OLED дисплеями. В этом уроке будем работать с библиотекой Adafruit_SSD1306.

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

Начнем написание программы с подключения библиотеки и определения полезных статичных значений через директиву #define.

#include <Adafruit_SSD1306.h>

#define WIDTH 128 // ширина дисплея в пикселах
#define HEIGHT 64 // высота дисплея в пикселах
#define CTRL_TO 50 // период вызова главного обработчика событий

Объявим глобальные переменные

byte pinLeft = A1; // пин контроллера, к которому подключена кнопка Влево
byte pinRight = A0; // пин для кнопки Вправо

int x,y; // переменные для хранения координат игрока
unsigned long t, ctrl_next; // переменные для обеспечения работы таймера

Последние приготовления — создание объекта для работы с OLED дисплеем.

Adafruit_SSD1306 display(4);

Число 4 в данном случае означает пин, к которому подключен вывод RST дисплея. Поскольку мы этот вывод никуда не подключали, можем указать там любой номер.

Следом идёт функция setup.

void setup() {
    // инициализируем пины для работы с кнопками
    pinMode( pinLeft, INPUT );
    pinMode( pinRight, INPUT );

    x = WIDTH/2; // начальная позиция игрока - середина экрана
    y = HEIGHT-1; // начальная позиция по оси y - самый низ дисплея

    // инициализация дисплея
    display.begin(SSD1306_SWITCHCAPVCC, 0x3D);
    // очистка памяти дисплея, на всякий случай
    display.clearDisplay();
}

С этим всё понятно, а теперь суперцикл (он же loop) нашей программы.

void loop() {
    unsigned long t = millis();
    // проверяем, не настало ли время запуска обработчика
    if( t > ctrl_next ){
        ctrl_next = t + CTRL_TO;
        // собственно, всё что ниже - код обработчика

        // обработка нажатий кнопок
        if( digitalRead(pinLeft) && x>0 )
            x = x - 1;
        if( digitalRead(pinRight) && x<WIDTH-1 )
            x = x + 1;
        // отрисовка пиксела на фрейме
        display.drawPixel(x, y, 1);
        // вывод фрейма на дисплей
        display.display();
    }
}

Что такое фрейм? Фрейм — это некая область памяти, в которой формируется изображение перед тем, как попасть на дисплей. В нашем случае, это участок оперативной памяти (по факту, обычный массив). Всё это реализовано внутри библиотеки Adafruit и мы не будем это подробно разбирать.

Загружаем программу на дисплей и пробуем нажимать кнопки. Наш игрок-пиксел будет двигаться, как и задумывалось, но после него будет оставаться след! Оно и понятно, ведь каждый раз, когда мы вызываем функцию setPixel — мы добавляем новую точку в память дисплея, а старую не стираем.

У этой проблемы есть два решения.

1 — перед каждой отрисовкой игрового фрейма, очищать всю память дисплея. Плюс такого решения — не нужно думать про предыдущую позицию игрока и врагов. Минус — контроллеру придется каждый раз заново отрисовывать все игровое поле (это пока оно у нас из одного пиксела, а потом будет много чего еще).

2 — каждый раз затирать только предыдущие позиции игрока и врагов.

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

...
        // очистка фрейма
        display.clearDisplay();
        // отрисовка пиксела на фрейме
        display.drawPixel(x, y, 1);
        // вывод фрейма на дисплей
        display.display();
...

Загружаем программу, пробуем. Ну вот, теперь пиксел двигается как надо, без следов. Жалко, что только один пиксел. Скучновато как-то. Следующий шаг — нарисуем вместо одинокой точки целый пиксельный космолет!

2+

Изменено:

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

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.