В прошлом году мы в летней компьютерной школе проводили кружок по Arduino. Там поучаствовали и преподаватели, в результате чего появилась 8-битная игровая консоль с экраном 64x64.
Теперь же мы решили сделать кружок, в котором ребята смогут создать собственные игры для этой консоли. Благодаря её минимализму, кода должно получиться немного. Всего на кружок было запланировано около 14 часов в течение смены, так что низкий порог входа в программирование таких игр был важной особенностью.
![](https://habrastorage.org/r/w780q1/files/ac0/20d/905/ac020d905fcf46f8a7458d999c31a514.jpg)
4095 светодиодов и все-все-все
Железка у нас была только одна, поэтому сначала надо было сделать эмулятор. Ведь над играми будет работать несколько команд. Кроме того, исходно весь код был в одном *.ino файле, поэтому надо было разбить его на части, чтобы добавление новых игр сводилось к программированию их поведения и отрисовки. Среду сборки мы оставили Arduino IDE, чтобы упростить настройку рабочих машин.
Для вывода изображения на экран, процессор постоянно сканирует его и загружает в сдвиговые регистры цвета пикселей. Поэтому правильная процедура обновления экрана должна отдавать только одну линию пикселей. Но такой интерфейс был бы слишком неудобным, поэтому мы сделали набор функций типа game_draw_sprite, game_draw_text, а они уже внутри проверяют какую линию надо выводить (и надо ли вообще). Из-за этого возникает некоторый оверхед, так как все вызовы будут делаться для каждой строки, а не только для нужных.
![](https://habrastorage.org/r/w1560/web/49f/001/854/49f00185480348068ffa0ad0ce72f9ee.png)
Эмулятор предоставляет те же функции, но он отрисовывает всё во фреймбуфере, к тому же работает намного быстрее, чем консоль. Поэтому не всегда можно оценить то, что получится на железе. Зато он решает большинство проблем с отладкой, потому что можно запустить Visual Studio (или gdb) и посмотреть как работает программа. Все скриншоты здесь именно с этого эмулятора.
Разработчик должен реализовать две функции, чтобы получилась игра: отрисовка экрана и обновление внутреннего состояния. Обновление происходит с заранее заданной периодичностью. Функция отрисовки вызывается для каждой строки экрана (на самом деле для групп по 4 строки сразу из-за особенностей адресации).
Функция обновления реагирует на нажатые на джойстике клавиши и меняет внутренние данные игры (например, координаты объектов, чтобы их переместить). Так как в каждый момент времени выполняется только одна игра, нет смысла в каждой из них объявлять статические переменные. Ведь всего у нас 2 килобайта памяти. Поэтому работа с памятью у нас слегка неудобная — все данные игры хранятся в структуре, к которой приходится обращаться через указатель.
Чтобы проверить работоспособность API, а также меню для выбора игр, мы реализовали игру «Змейка». Делали слегка в спешке, поэтому несколько багов осталось. Дети их потом с радостью обнаруживали.
![](https://habrastorage.org/r/w1560/web/1d4/f4d/9d6/1d4f4d9d658345de91f7f90cc3513910.png)
Для начала мы разместили наш проект с гитхаба на внутреннем сервере с gitlab. Ребята работали с форками этого репозитория, а затем присылали пулл реквесты, чтобы собрать всё в кучу. В целом всё прошло удачно, но глубоко в объяснение принципов работы git мы не погружались.
Всего участников проектов было не слишком много. Сначала заявилось 4 команды, но до конца дошли только 2. Это были ребята из группы, только начинающей изучать алгоритмы, поэтому было интересно, получится ли у них что-нибудь.
Первая команда решила делать классический сапёр. Мины размещались очень хитро — по одной в столбце — поэтому им понадобились только спрайты с цифрами от 1 до 3. Программа состоит из 475 строк, но там есть мусор и оставленные комментарии из шаблона. Придираться к коду в пулл реквестах уже не оставалось времени.
![](https://habrastorage.org/r/w1560/web/592/de4/a36/592de4a36a4449fda1dd309ad1bfdc18.png)
Самая большая сложность в реализации сапёра — это открывание всех свободных клеток, смежных с той свободной, на которую нажал пользователь. Обход в ширину было объяснять долго. Да и память он под очередь занимает. Поэтому ребята просто несколько раз сканировали массив и открывали соседние и открытыми клетки. Это нечастая операция, поэтому всё сработало нормально.
Ещё тут как раз возникли проблемы с отображением — если каждый раз пытаться отрисовать всё поле, то получается слишком медленно, и картинка начинает мерцать. Пришлось сделать небольшой костыль — функцию, которая позволяет скипать целые строки из спрайтов, если они не попадают на отображаемую строку. Дальше мы планируем проапгрейдить процессор до ATmega2560, чтобы хватило памяти на фреймбуфер. Тогда все проблемы с мерцанием для подобных игр изчезнут.
Игра называется Breakout, потому что ребята сначала хотели делать именно его. Но потом решили, что всё сложно и у них получился Pong. В игре несколько режимов — игра двух игроков, игра с компьютером, демонстрация (игра компьютера против себя). Правда вот компьютер проигрывать не умеет — ему всегда удаётся отбить мяч. Более хитрый алгоритм ребята придумать не успели. Всего файл с игрой занимает 272 строки.
![](https://habrastorage.org/r/w1560/web/8d8/055/709/8d80557090e64d0d8dfaeb73f5bb997d.png)
Параллельно с детьми я сделал «Flappy submarine», чтобы показывать им как работать со спрайтами и управлением.
![](https://habrastorage.org/r/w1560/web/7b4/556/83a/7b455683a1c644058ed7ff533ed34f74.png)
Всех этих игр хватило, чтобы забить 32 килобайта программной памяти. Но после апргейда до ATmega2560 памяти будет 256 килобайт, так что в следующем году мы планируем повторить этот кружок не выкидывая уже созданные игры.
→ Исходники проекта на гитхабе
Теперь же мы решили сделать кружок, в котором ребята смогут создать собственные игры для этой консоли. Благодаря её минимализму, кода должно получиться немного. Всего на кружок было запланировано около 14 часов в течение смены, так что низкий порог входа в программирование таких игр был важной особенностью.
![](https://habrastorage.org/files/ac0/20d/905/ac020d905fcf46f8a7458d999c31a514.jpg)
4095 светодиодов и все-все-все
API для создания игр
Железка у нас была только одна, поэтому сначала надо было сделать эмулятор. Ведь над играми будет работать несколько команд. Кроме того, исходно весь код был в одном *.ino файле, поэтому надо было разбить его на части, чтобы добавление новых игр сводилось к программированию их поведения и отрисовки. Среду сборки мы оставили Arduino IDE, чтобы упростить настройку рабочих машин.
Для вывода изображения на экран, процессор постоянно сканирует его и загружает в сдвиговые регистры цвета пикселей. Поэтому правильная процедура обновления экрана должна отдавать только одну линию пикселей. Но такой интерфейс был бы слишком неудобным, поэтому мы сделали набор функций типа game_draw_sprite, game_draw_text, а они уже внутри проверяют какую линию надо выводить (и надо ли вообще). Из-за этого возникает некоторый оверхед, так как все вызовы будут делаться для каждой строки, а не только для нужных.
![](https://habrastorage.org/web/49f/001/854/49f00185480348068ffa0ad0ce72f9ee.png)
Эмулятор предоставляет те же функции, но он отрисовывает всё во фреймбуфере, к тому же работает намного быстрее, чем консоль. Поэтому не всегда можно оценить то, что получится на железе. Зато он решает большинство проблем с отладкой, потому что можно запустить Visual Studio (или gdb) и посмотреть как работает программа. Все скриншоты здесь именно с этого эмулятора.
Разработчик должен реализовать две функции, чтобы получилась игра: отрисовка экрана и обновление внутреннего состояния. Обновление происходит с заранее заданной периодичностью. Функция отрисовки вызывается для каждой строки экрана (на самом деле для групп по 4 строки сразу из-за особенностей адресации).
Функция обновления реагирует на нажатые на джойстике клавиши и меняет внутренние данные игры (например, координаты объектов, чтобы их переместить). Так как в каждый момент времени выполняется только одна игра, нет смысла в каждой из них объявлять статические переменные. Ведь всего у нас 2 килобайта памяти. Поэтому работа с памятью у нас слегка неудобная — все данные игры хранятся в структуре, к которой приходится обращаться через указатель.
Чтобы проверить работоспособность API, а также меню для выбора игр, мы реализовали игру «Змейка». Делали слегка в спешке, поэтому несколько багов осталось. Дети их потом с радостью обнаруживали.
![](https://habrastorage.org/web/1d4/f4d/9d6/1d4f4d9d658345de91f7f90cc3513910.png)
Совместная разработка
Для начала мы разместили наш проект с гитхаба на внутреннем сервере с gitlab. Ребята работали с форками этого репозитория, а затем присылали пулл реквесты, чтобы собрать всё в кучу. В целом всё прошло удачно, но глубоко в объяснение принципов работы git мы не погружались.
Всего участников проектов было не слишком много. Сначала заявилось 4 команды, но до конца дошли только 2. Это были ребята из группы, только начинающей изучать алгоритмы, поэтому было интересно, получится ли у них что-нибудь.
Сапёр
Первая команда решила делать классический сапёр. Мины размещались очень хитро — по одной в столбце — поэтому им понадобились только спрайты с цифрами от 1 до 3. Программа состоит из 475 строк, но там есть мусор и оставленные комментарии из шаблона. Придираться к коду в пулл реквестах уже не оставалось времени.
![](https://habrastorage.org/web/592/de4/a36/592de4a36a4449fda1dd309ad1bfdc18.png)
Самая большая сложность в реализации сапёра — это открывание всех свободных клеток, смежных с той свободной, на которую нажал пользователь. Обход в ширину было объяснять долго. Да и память он под очередь занимает. Поэтому ребята просто несколько раз сканировали массив и открывали соседние и открытыми клетки. Это нечастая операция, поэтому всё сработало нормально.
Ещё тут как раз возникли проблемы с отображением — если каждый раз пытаться отрисовать всё поле, то получается слишком медленно, и картинка начинает мерцать. Пришлось сделать небольшой костыль — функцию, которая позволяет скипать целые строки из спрайтов, если они не попадают на отображаемую строку. Дальше мы планируем проапгрейдить процессор до ATmega2560, чтобы хватило памяти на фреймбуфер. Тогда все проблемы с мерцанием для подобных игр изчезнут.
Breakout
Игра называется Breakout, потому что ребята сначала хотели делать именно его. Но потом решили, что всё сложно и у них получился Pong. В игре несколько режимов — игра двух игроков, игра с компьютером, демонстрация (игра компьютера против себя). Правда вот компьютер проигрывать не умеет — ему всегда удаётся отбить мяч. Более хитрый алгоритм ребята придумать не успели. Всего файл с игрой занимает 272 строки.
![](https://habrastorage.org/web/8d8/055/709/8d80557090e64d0d8dfaeb73f5bb997d.png)
Flappy submarine
Параллельно с детьми я сделал «Flappy submarine», чтобы показывать им как работать со спрайтами и управлением.
![](https://habrastorage.org/web/7b4/556/83a/7b455683a1c644058ed7ff533ed34f74.png)
Всех этих игр хватило, чтобы забить 32 килобайта программной памяти. Но после апргейда до ATmega2560 памяти будет 256 килобайт, так что в следующем году мы планируем повторить этот кружок не выкидывая уже созданные игры.
→ Исходники проекта на гитхабе