Разработка игр для консоли на Arduino в летнем лагере

    В прошлом году мы в летней компьютерной школе проводили кружок по Arduino. Там поучаствовали и преподаватели, в результате чего появилась 8-битная игровая консоль с экраном 64x64.

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


    4095 светодиодов и все-все-все

    API для создания игр


    Железка у нас была только одна, поэтому сначала надо было сделать эмулятор. Ведь над играми будет работать несколько команд. Кроме того, исходно весь код был в одном *.ino файле, поэтому надо было разбить его на части, чтобы добавление новых игр сводилось к программированию их поведения и отрисовки. Среду сборки мы оставили Arduino IDE, чтобы упростить настройку рабочих машин.

    Для вывода изображения на экран, процессор постоянно сканирует его и загружает в сдвиговые регистры цвета пикселей. Поэтому правильная процедура обновления экрана должна отдавать только одну линию пикселей. Но такой интерфейс был бы слишком неудобным, поэтому мы сделали набор функций типа game_draw_sprite, game_draw_text, а они уже внутри проверяют какую линию надо выводить (и надо ли вообще). Из-за этого возникает некоторый оверхед, так как все вызовы будут делаться для каждой строки, а не только для нужных.



    Эмулятор предоставляет те же функции, но он отрисовывает всё во фреймбуфере, к тому же работает намного быстрее, чем консоль. Поэтому не всегда можно оценить то, что получится на железе. Зато он решает большинство проблем с отладкой, потому что можно запустить Visual Studio (или gdb) и посмотреть как работает программа. Все скриншоты здесь именно с этого эмулятора.

    Разработчик должен реализовать две функции, чтобы получилась игра: отрисовка экрана и обновление внутреннего состояния. Обновление происходит с заранее заданной периодичностью. Функция отрисовки вызывается для каждой строки экрана (на самом деле для групп по 4 строки сразу из-за особенностей адресации).

    Функция обновления реагирует на нажатые на джойстике клавиши и меняет внутренние данные игры (например, координаты объектов, чтобы их переместить). Так как в каждый момент времени выполняется только одна игра, нет смысла в каждой из них объявлять статические переменные. Ведь всего у нас 2 килобайта памяти. Поэтому работа с памятью у нас слегка неудобная — все данные игры хранятся в структуре, к которой приходится обращаться через указатель.

    Чтобы проверить работоспособность API, а также меню для выбора игр, мы реализовали игру «Змейка». Делали слегка в спешке, поэтому несколько багов осталось. Дети их потом с радостью обнаруживали.



    Совместная разработка


    Для начала мы разместили наш проект с гитхаба на внутреннем сервере с gitlab. Ребята работали с форками этого репозитория, а затем присылали пулл реквесты, чтобы собрать всё в кучу. В целом всё прошло удачно, но глубоко в объяснение принципов работы git мы не погружались.

    Всего участников проектов было не слишком много. Сначала заявилось 4 команды, но до конца дошли только 2. Это были ребята из группы, только начинающей изучать алгоритмы, поэтому было интересно, получится ли у них что-нибудь.

    Сапёр


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



    Самая большая сложность в реализации сапёра — это открывание всех свободных клеток, смежных с той свободной, на которую нажал пользователь. Обход в ширину было объяснять долго. Да и память он под очередь занимает. Поэтому ребята просто несколько раз сканировали массив и открывали соседние и открытыми клетки. Это нечастая операция, поэтому всё сработало нормально.

    Ещё тут как раз возникли проблемы с отображением — если каждый раз пытаться отрисовать всё поле, то получается слишком медленно, и картинка начинает мерцать. Пришлось сделать небольшой костыль — функцию, которая позволяет скипать целые строки из спрайтов, если они не попадают на отображаемую строку. Дальше мы планируем проапгрейдить процессор до ATmega2560, чтобы хватило памяти на фреймбуфер. Тогда все проблемы с мерцанием для подобных игр изчезнут.

    Breakout


    Игра называется Breakout, потому что ребята сначала хотели делать именно его. Но потом решили, что всё сложно и у них получился Pong. В игре несколько режимов — игра двух игроков, игра с компьютером, демонстрация (игра компьютера против себя). Правда вот компьютер проигрывать не умеет — ему всегда удаётся отбить мяч. Более хитрый алгоритм ребята придумать не успели. Всего файл с игрой занимает 272 строки.



    Flappy submarine


    Параллельно с детьми я сделал «Flappy submarine», чтобы показывать им как работать со спрайтами и управлением.



    Всех этих игр хватило, чтобы забить 32 килобайта программной памяти. Но после апргейда до ATmega2560 памяти будет 256 килобайт, так что в следующем году мы планируем повторить этот кружок не выкидывая уже созданные игры.

    Исходники проекта на гитхабе
    • +17
    • 5,9k
    • 6
    Поделиться публикацией
    Комментарии 6
      –1
      А можно увидеть код симулятора?
        0
        Уже нашёл на githab
        +1
        А почему светодиодов 4095 а не 4096
          0
          Приз самому внимательному игроку!
          Один в углу отвалился (и в коде даже есть хак, который сбрасывает значение яркости для этого светодиода).
          0

          STM32 берите. STM32F4 нормальный такой с 1MB флеша, они дешевле чем ардуино.

            0
            Кстати, мы уже попробовали. Даже выводили видео.
            Но именно этот экземпляр хочется оставить 8-битным. Да и избыточен ARM для простеньких игрушек.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое