Приставка на ардуино. Разработка игр для консоли на базе arduino с помощью Unity

Слово от переводчика: когда-то, когда страна и деревья были большими, а воображение просто безграничным, была у меня мечта – возможность выводить изображение с моего программируемого микрокалькулятора Электроника МК-61 (ну, там графики всякие, кривые, картинки ) на экран телевизора. Времена были дикие позднесовковые, и не то что игровая приставка и очень персональный МИКРОкомпьютер («Правец 8Д» или «Специалист» или «Сикнклер»), но и видеомагнитофоны были в диковинку. В общем, народ требовал зрелищ и те, кто помнит цикл учебно – развлекательных публикаций для программируемых калькуляторов под общим названием «Путь к Земле» (журнал «Техника – Молодежи») меня поймут.

Если кратко, то в виде научно-фантастического романа с неплохим сюжетом описывалось путешествие двух идиотов случайных знакомых – профессионального космонавта и мажора кибернетика с Луны на Землю. Отдельной остроты всему сюжету придавало то, что путешествовали они на т.н. «Лунолете», то есть малом космическом судне с химическим двигателем, предназначенным для передвижения в условиях прямой видимости над лунами и прочими небесными телами похожими на биллиардный шар с простым рельефом. В каждом выпуске цикла присутствовало упрощенное правда, но вполне обоснованное математическое описание каждого маневра как в условиях сильной (относительно) гравитации близкого небесного тела, так и при влиянии на небесный снаряд героев гравитаций Земли и Луны, а также программа для расчета очередного этапа полета. В общем, глядеть на циферки на экране калькулятора не то чтоб доставало, но хотелось красивых кривых на экран (как в ЦУПе).

С другой стороны, не будем забывать что даже примитивные микроконтроллеры семейства Arduino на порядок превосходят по производительности не только микропроцессоры тогдашних флагманов – МК-52 и МК-61, но и вычислительные возможности некоторых 8-битовых игровых приставок поздних времен (Atary 2600 и прочих Рембо так точно).

В общем, вступление вышло слегка затянутым, так что перейдем к теме сегодняшнего занятия – выводе видеоизображения с Arduino на экран телевизора.

К сожалению, конструктивные особенности Arduino позволяют выводить только монохромное (черно – белые) изображения, хотя и это может быть полезным в некоторых проектах, а ЧСВ поднимет у нубов так точно…

Шаг первый. Детали и ПО

Вам понадобятся:

Детали и агрегаты:

  1. Микроконтроллер Arduino
  2. Телевизор (без него точно никуда)
  3. Макетная плата или шилд для Arduino
  4. 2 резистора номиналом 470 Ом и 1 Ком
  5. 2 двухпиновых монтажных переходника папа-папа
  6. Экранированный телевизионный кабель с тюльпаном на конце

Программное обеспечение:

  1. Среда разработки/прошивки Arduino. Официальная ссылка

Шаг второй. Сборка

От имени автора прошу прощение за пахабное низкое качество изображения готового ТВ – переходника. Поясняется это тем, что при написании инструкций, сначала надо их писать, а потом уже приступать к сборке с тщательным фотофиксированием всех этапов. В нашем же случае, получилось все с точностью до наоборот, так что из мутного изображения готового переходника понять что-либо просто невозможно.

Гораздо лучше, что куда и как паять, поясняет принципиальная схема, к тому же состоящая всего из нескольких деталей.


Распиновка:

Sync — цифровой вывод 9 микроконтроллера

Video — цифровой вывод 8 микроконтроллера

GND — вывод GND микроконтроллера

Шаг третий. Программирование

Самая веселая часть – программирование.

В принципе, уже вышла новая версия ТВ – библиотеки, однако она еще более глючна нестабильна чем R5.91, которую использует автор, так что лучше качайте библиотеку по приведенной выше ссылке.

Текс программы для ленивых тех, кому лень перенабирать код с копии экрана:

#include TVout TV; unsigned char x, y; void setup () { TV.start_render(_NTSC); } void loop () { TV.clear_screen (); TV.print_str (10, 10, «TVout FTW!!!»); TV.delay (60); }

Предполагается, что базовые принципы работы и программирования Arduino – подобных микроконтроллеров вам известны, так что автор решил не растекаться мыслью по древу, порекомендовав ознакомится с командами библиотеки ниже:

  • begin(mode) Начало вывода информации на экран. Разешение стандартное — 128х96
  • begin(mode,x,y) Начало вывода информации на экран. Разешение определяется пользователем аргументами x,y
  • end() Очистка видеобуфера
  • force_vscale(sfactor) Force the number of times to display each line.
  • force_outstart(time) Force the time to start outputting on an active line.
  • force_linestart(line) Force line to start outputting on.
  • set_vbi_hook(func) Set the function to be called once per vertical blanking period.
  • set_hbi_hook(func) Set the function to be called once per horizontal blanking period.
  • hres() Команда возвращает значение горизонтального разрешения,
  • vres() Команда возвращает значение вертикального разрешения,
  • char_line() Команда возвращает значение количества символов, которые поместятся в строку.
  • set_pixel(x,y,color) Установка цвета пикселя по заданным координатам
  • get_pixel(x,y) Установка пикселя с заданными координатами в качестве точки отсчета.
  • fill(color) Заливка экрана заданным цветом.
  • clear_screen() Очистка экрана.
  • invert() Инвертирование изображение на экране.
  • shift(distance,direction) Прокрутка экрана на заданную дистанцию в любом из 6 направлений.
  • draw_line(x0,y0,x1,y1,color) Создание прямой с координат (x0,y0) до координат (x1,y1).
  • draw_row(row,x0,x1,color) Заполнение строки с координатами от x0 to x1 заданным цветом.
  • draw_column(column,y0,y1,color) Заполнение столбца с координатами от у0 до у1 заданным цветом.
  • draw_rect(x,y,w,h,color,fillcolor) Отображение прямоугольника с началом в координатах (x,y) с размерами(h,w), и заполнение заданным цветом.
  • draw_rect(x,y,w,h,color) Отображение прямоугольника с началом в координатах (x,y) с размерами(h,w).
  • draw_circle(x,y,radius,color,fillcolor) Отображение окружности с центором в координатах (x,y) с радиусом (RADIUS) и его заполнение заданным цветом
  • draw_circle(x,y,radius,color) Отображение окружности с центором в координатах (x,y) с радиусом (RADIUS).
  • bitmap(x,y,bmp,i,width,height) Отображение заданного изображения в координатах..
  • print_char(x,y,c) Печать символа в координатах (x,y).
  • set_cursor(x,y) Установка позиции для вывода слеующего символа.
  • select_font(font) Установка шрифт для вывода текста.
  • print() Вывод текста.
  • println() Вывод пстой строки.
  • printPGM() Вывод строки с текстом из памяти программы.
  • tone(frequency) Тональный сигнал с заданной частостой.
  • tone(frequency,duration) Тональный сигнал заданной частоты и длительности.
  • noTone() Прикращение вывода тонового сигнала.

Шаг четверый. Завершение

Как использовать плату Arduino для вывода какой-либо информации на ТВ? Оказывается есть такая библиотека TVOut (статья на сайте ), которая позволяет выводить информацию на ТВ по НЧ-кабелю (тюльпан). Правда изображение будет черно-белым, однако этого будет достаточно для большинства проектов.

Вот игровая консоль, где используется данная библиотека

Сайт проекта http://nootropicdesign.com/hackvision/index.html
Цена платы $43.95.

Создадим свою ТВ-приставку, состоящую из Arduino-платы и двухкоординатного джойстика и напишем простенькую игру.
Вот вид нашей приставки

Ознакомимся с возможностями библиотеки и напишем игру.
Страница библиотеки - http://code.google.com/p/arduino-tvout
Здесь можно скачать библиотеку и посмотреть описание функций.
Библиотека использует следующие выводы Arduino

Плата SYNC VIDEO AUDIO Decimilia, Uno, Nano 9 7 11 Mega 11 A7(D29) 10

Рассмотрим основные функции библиотеки.

Функции установки режима

Функция begin() инициализирует вывод видеосигнала (разрешение экрана по умолчанию 128x96)
Синтаксис:

  • TVOut.begin(mode);
  • TVOut.begin(mode, x, y);

Параметры:

  • mode – стандарт видеосигнала:
  • _PAL – режим PAL;
  • _NTSC – режим NTSC.

Возвращаемое значение:

  • 0 – в случае удачного соединения, 4 – в случае неудачи (недостаточно памяти для буфера вывода).

Функции задержки

Функция delay() осуществляет задержку выведенного изображения.
Синтаксис:

  • TVOut.delay(ms);

Параметры:

  • ms – задержка в мс с точностью: 20 мс для PAL и 16 мс для NTSC.

Функция delay_frame() осуществляет задержку выведенного изображения.
Синтаксис:

  • TVOut.delay_frame(frames);

Параметры:

  • frames – количество кадров для задержки..

Функция полезна для сведения к минимуму или устранения на мерцание экрана, вызванные обновлением экрана.

Функции получения параметров

Функция hres() возвращает горизонтальное разрешение экрана.
Синтаксис:

  • TVOut.hres();

Параметры:

Возвращаемое значение:

  • unsigned char – горизонтальное разрешение экрана.

Функция vres() возвращает вертикальное разрешение экрана.
Синтаксис:

  • TVOut.vres();

Параметры:

Возвращаемое значение:

  • unsigned char – вертикальное разрешение экрана.

Функция char_line() возвращает максимально возможное количество символов в одной строке при выводе текстовой информации.
Синтаксис:

  • TVOut. char_line();

Параметры:

Возвращаемое значение:

  • unsigned char – количество символов.

Основные графические функции

Функция set_pixel() устанавливает цвет пикселя экрана в точке с заданными координатами.
Синтаксис:

  • TVOut.set_pixel(x,y,color);

Параметры:

  • x,y – координаты пикселя;
  • color – цвет пикселя:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.

Функция get_pixel() получает цвет пикселя экрана из точки с заданными координатами.
Синтаксис:

  • TVOut.get_pixel(x,y);

Параметры:

  • x,y – координаты пикселя.

Возвращаемое значение:

  • color – цвет пикселя:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.

Функция fill() заполняет экран заданным цветом.
Синтаксис:

  • TVOut.fill(color);

Параметры:

  • color – цвет заполнения:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.

Функция clear_screen() очищает экран, заполняя заданным цветом.
Синтаксис:

  • TVOut.clear_screen(color);

Параметры:

  • color – цвет заполнения:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.

Функция invert() инвертирует содержимое экрана.
Синтаксис:

  • TVOut.invert();

Параметры:

Функция shift_direction() сдвигает содержимое экрана.
Синтаксис:

  • TVOut.shift_direction(distance, direction);

Параметры:

  • distance – расстояние для сдвига содержимого экрана.
  • direction – направление сдвига:
  • UP=0 – вверх;
  • DOWN=1 – вниз;
  • LEFT=2 – влево;
  • RIGHT=3 – вправо.

Функция draw_line() соединяет на экране линией две точки.
Синтаксис:

  • TVOut.draw_line(x0,y0,x1,y1,color);

Параметры:

  • x0,y0 – координаты первой точки;
  • x1,y1 – координаты второй точки;
  • color – цвет заполнения:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.

Функция draw_row() заполняет строку указанным цветом между двумя точками строки.
Синтаксис:

  • TVOut.draw_row(row,x0,x1,color);

Параметры:

  • row – вертикальная координата строки;
  • x1,x2 – горизонтальный координаты точек строки;
  • color – цвет заполнения:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.

Функция draw_column() заполняет строку указанным цветом между двумя точками столбца.
Синтаксис:

  • TVOut.draw_column(column,y0,y1,color);

Параметры:

  • column – горизонтальная координата столбца;
  • y1,y2 – вертикальные координаты точек столбца;
  • color – цвет заполнения:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.

Функция draw_rect() рисует на экране прямоугольник.
Синтаксис:

  • TVOut.draw_rect(x,y,w,h,color);
  • TVOut.draw_rect(x,y,w,h,color,fillcolor);

Параметры:

  • x,y – координаты левой верхней точки;
  • w,h – ширина и высота рисуемого прямоугольника;
  • color – цвет границ прямоугольника:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.
  • fillcolor – цвет заполнения прямоугольника:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.

Функция draw_circle() рисует на экране круг.
Синтаксис:

  • TVOut.draw_ circle(x,y,r,color);
  • TVOut.draw_ circle(x,y,r,color,fillcolor);

Параметры:

  • x,y – координаты центра круга;
  • r – радиус круга;
  • color – цвет границ круга:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.
  • fillcolor – цвет заполнения круга:
  • 0 – черный;
  • 1 – белый;
  • 2 – инвертировать цвет.

Функция bitmap() выводит на экран растровое изображение.
Синтаксис:

  • TVOut.bitmap(x,y,bmp,w,h);

Параметры:

  • x,y – координаты левого верхнего угла точки вывода;
  • bmp – указатель на массив памяти, где хранится картинка;
  • w,h – ширина, высота выводимого изображения;

Ниже рассмотрим процесс создания кода выводимых растровых изображений.

Функции вывода текстовой информации

Для применения функций вывода текстовой информации требуетя подключение файлов с включенными в библиотеку или пользовательскими шрифтами. Для подключения пользовательского набора шрифтов необходимо в скетче подключить заголовочный файл:
#include
В состав библиотеки включены следующие наборы шрифтов:

  • font4x6;
  • font6x8;
  • font8x8;
  • font8x8ext.

Функция select_font() выбирает шрифт для вывода текстовой информации.
Синтаксис:

  • TVOut.select_font(font);

Параметры:

  • font – шрифт, подключенный в скетче.

Функция print_char() выводит символ на экран.
Синтаксис:

  • TVOut.print_char(x,y,char);

Параметры:

  • x,y – позиция на экране для вывода символа;
  • char – символ из текущего шрифта.

Функция set_cursor() устанавливает позицию курсора для вывода текстовой информации на экран.
Синтаксис:

  • TVOut.set_cursor(x,y);

Параметры:

  • x,y – координаты для курсора.

Функция print() выводит на экран строку, символ или число.
Синтаксис:

  • TVOut.print(x,y,string);
  • TVOut.print(x,y,char,base);
  • TVOut.print(x,y,int,base).

Параметры:

  • x,y – координаты курсора.
  • base – формат вывода:
  • BYTE = 0;
  • DEC = 10 (default);
  • HEX = 16.

Функция println() выводит на экран строку, символ или число и в конце символ перевода строки:
Синтаксис:

  • TVOut.println(x,y,string);
  • TVOut.println(x,y,char,base);
  • TVOut.println(x,y,int,base).

Параметры:

  • x,y – координаты курсора.
  • base – формат вывода:
  • BYTE = 0;
  • DEC = 10 (default);
  • HEX = 16.

Функции вывода аудио

Функции вывода звука позволяют отправлять на телевизор через аудиовыход сигнал определенной частоты.
Функция tone() выдает аудиосигнал определенной частоты.
Синтаксис:

  • TVOut.tone(frequency,duration);
  • TVOut.tone(frequency).

Параметры:

  • frequency – частота аудиосигнала;
  • duration – длительность сигнала.

Функция noTone() прекращает выдачу аудиосигнала.
Синтаксис:

  • TVOut.noTone().

Создание собственных шрифтов

Рассмотрим процесс создания пользовательских шрифтов для библиотеки TVOut.

Библиотека позволяет создавать собственные шрифты.
Существуют два вида шрифтов – фиксированной и переменной ширины. Для шрифтов фиксированной ширины первые три байта массива содержат данные о ширине символа (4), высоте символа (6) и первый печатный символ (32) . Затем идут данные для каждого последующего символа.

#include "myfont1.h" PROGMEM const unsigned char myfont1 = { 4,6,32, // 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, // 0b01000000, 0b01000000, 0b01000000, 0b00000000, 0b01000000, 0b00000000, … … };

Для шрифтов переменной ширины в описании каждого символа первый байт определяет ширину данного символа.

#include "myfont1.h" PROGMEM const unsigned char myfont1 = { 4,6,32, // 2, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, // 3, 0b01000000, 0b01000000, 0b01000000, 0b00000000, 0b01000000, 0b00000000, … … };

Создадим пользовательский шрифт myfont1. Для пользовательского шрифта myfont1 в папке TVOutfonts создаем 2 файла: myfont1.h и myfont1.cpp.
Вот содержимое файла myfont1.h

#ifndef MYFONT1_h #define MYFONT1_h #include extern const unsigned char myfont1; #endif

Теперь для использования библиотекой TVOut нашего пользовательского шрифта myfont1 в скетче необходимо подключить файл

#include "myfont1.h"

Создание графических примитивов.

Библиотека TVOut позволяет загружать на экран растровые изображения. Рассмотрим создание кода для загрузки растрового изображения функцией bitmap() библиотеки TVOut.

Сначала необходимо создать 1 битное (двухцветное изображение), например в графическом редакторе Paint.

Затем нам понадобится программа Image2Code, которая сконвертирует из нашего изображения код. Программу можно скачать по адресу . Это версия для операционной системы Windows. Скачиваем, запускаем.

Нажимаем на кнопку Convert и получаем массив.

# include # ifndef MYBITMAP1_H # define MYBITMAP1_H extern const unsigned char MyBitmap1; #endif

Далее создаем файл MyBitmap1.cpp , в него (в поле данных массива unsigned char MyBitmap1) копируем данные конвертации, убирая символы "{" и "}", и вставляя в начале ширину и высоту изображения в пикселах.

#include " MyBitmap1.h" PROGMEM const unsigned char MyBitmap1 = { 16,16, 0x73,0x8E, 0x8C,0x71, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x97,0x63, 0x94,0x55, 0xB4,0x55, 0xD4,0x67, 0x94,0x45, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0xAA,0xAA, 0x55,0x55, 0x00,0x00 };

Сохраняем файлы MyBitmap1.h и myBitmap.cpp в директории нашего скетча. Для вывода нашего изображения на экран телевизора вызываем функцию TVOut.bitmap():

#include " MyBitmap1.h" TVOut.bitmap(x,y,MyBitmap);

C библиотекой ознакомились, собрана игровая консоль - теперь можно приступать к написанию игры.

Игровая приставка на Arduino? Правда-правда. С экранчиком, кнопками и играми. И это не фантазия. Существовало (и существует) несколько проектов, которые попытались это сделать и один из них (Gamebuino) мы и попытаемся самостоятельно реализовать.

Так как проект открытый, то вся схематика доступна на ВИКИ , как и ссылки на ПО, игры и другая полезная информация.

Для создания своей игровой консоли нам понадобятся недорогие компоненты - экран Nokia 5110, шесть кнопок и пьезодинамик. Все это легко находится на Aliexpress или других сайтах китайских магазинов (в основном проблема найти экран) и даже в России. Экран можно назвать «массовым» и он хорошо описан, поэтому подключить его также не проблематично, как и достать.

Так как Gamebuino использует разводку удобную для размещения элеметов на плате, то при попытке подключить элементы через стандартные пины Arduino мы получаем вот такую картину:

На самом деле часть элементов уже была «упущена», так как в Gamebuino используются контакты микропроцессора, которые не разведены на Arduino или просто не нужны. В «утиль» пошли датчик заряда аккумулятора, кнопка C и SD-карта (они взаимосвязаны) и датчик внешнего освещения. Также решено было убрать резисторы - мы не собираемся использовать дисплей в режиме 24/7 и можно обойтись без этой защиты.

Можно было бы сделать «красиво», но тогда пришлось бы исправлять код всех библиотек и «мэпить» контакты по новой схеме. Я решил ограничиться лишь косметикой - то есть использовать «стандартные» контакты и лишь по необходимости внести небольшие правки в код.

Итак - после сборки схемы необходимо установить IDE Arduino и скачать вот этот архив . Его необходимо разархивировать в директорию Документы/Arduino и после этого запустить Arduino IDE. В этот архив уже внесены изменения в код библиотек и он будет «из коробки» работать с данной схемой.

У вас должны появиться примеры Gamebuino, которые позволят проверить дисплей, кнопки и т.п. и даже запусить Pong. Для платы «используйте» стандартную Arduino UNO или Leonardo (на других пока не тестировал).

Когда у вас все заработало, двигается, пищит и т.п. возникает закономерный вопрос - а где же игры? Gamebuino использует свой загрузчик для работы с SD-картами, поэтому загрузить HEX через их loader не получится безе перепрошивки самой Arduino. Но даже если вы это сделаете, то из-за отстуствия сигнала с аккумулятора вы ничего не сможете запустить, так как прошивка будет ругаться и выключаться. Прекомпилированные HEX с играми тоже не запустить из-за проблем с датчиком заряда аккумулятора.

Но не все потеряно - можно скачать исходные коды, открыть их в Arduino IDE и «залить» в нашу консоль.

Я проверил часть игр и они… РАБОТАЮТ! Вы можете скачать архив с исходными кодами игр отсюда , открыть их в Arduino IDE и скомпилировать и загрузить самостоятельно. Остальные игры вы можете сами опробовать, скачав исходные коды с Вики Gamebuino .

Итак - миссия выполнена! Мы получили миниатюрную игровую консоль. Теперь наша задача сделать ее мобильной:) И это попытаемся сделать в следующих статьях.



Приступим к созданию игры с условным названием “Арифметический сборщик”. Игрок управляется джойстиком с возможностью перемещения по полю размером 128×90. При нулевых отклонениях джойстика игрок находится в центре. Максимальное отклонение джойстика соответствует максимальному перемещению игрока. С определенным интервалом времени генерируются объекты-цифры, которые движутся сверху вниз. По достижении нижнего положения экрана объект-цифра исчезает, уменьшая количество баллов игрока на величину данной цифры. Если игрок перехватывает объект-цифру на экране, то это приводит к исчезновению объекта-цифры и увеличению счетчика баллов игрока. С определенной периодичностью значок игрока меняет свое значение с цифры 0 до 9. При перехвате объекта-цифры счетчик баллов игрока увеличивается на сумму, равную сумме объекта-цифры и цифры игрока, если цифра игрока равняется цифре объекта-цифры, то счетчик баллов игрока увеличивается на произведение цифр. По достижении определенных порогов баллов, происходит переход на более высокий уровень игры, что приводит к увеличению скорости движения и скорости генерации объектов-цифр. Кроме того с 4 уровня игры столкновение игрока с объектом-цифрой приводит не только к увеличению счетчика игрока, но и уменьшению (если цифра игрока меньше цифры объекта-цифры). Если количество баллов игрока становится меньше 0, игра начинается сначала.

Вот видео того, что получилось

Скачать архив со скетчем и файлами библиотеки TVOut можно по ссылке

И сам процесс создания игры

Создание переменных игры

Для управления игрой создадим объекты для хранения текущего положения игры. Символ, отображающий игрока, и символы, отображающие объекты-цифры, выводятся как текстовая информация, поэтому поделим все поле игры на строки и все перемещения объектов будем производить как вывод символа в знакоместо на поле. Переменные MAX_X=31 и MAX_Y=14 определяют размер поля игры по количеству знакомест по горизонтали и вертикали. Переменная MAX_OBJ=30 определяет максимальное количество одновременно находящихся на поле игры объектов-цифр. Массив int FIGURA хранит информации об объектах-цифрах, находящихся на поле игры следующим образом:

  • FIGURA[i] – числовое значение объекта-цифры (0 – пустой объект);
  • FIGURA[i] – текущая координата x объекта-цифры;
  • FIGURA[i] – текущая координата y объекта-цифры.

Для хранения прочих переменных, описывающих текущее состояние игры, создадим структуру GAME. Список полей структуры:

  • xk – координата x(/4) игрока;
  • yk – координата y(/6) игрока;
  • tekCursor – текущее значение курсора;
  • blinkCursor – текущее состояние блинка курсора;
  • vblink – скорость blink в vk ;
  • vk – скорость движения игрока – проверка входов A0,A1;
  • vo_10 – скорость изменения цифры игрока;
  • vo_11 – скорость появления объектов-цифр;
  • vo_12 – скорость движения объектов-цифр;
  • count_objects – кол-во объектов-цифр на поле;
  • level – уровень игры;
  • balls – кол-во баллов.

int MAX_X=31; int MAX_Y=14; // структура данных игры struct GAME // структура для данных игры { int xk; // координата x(/4) игрока int yk; // координата y(/6) игрока int tekCursor; // текущее значение курсора int blinkCursor; // текущее состояние блинка курсора int vblink; // скорость blink в vk long vk; // скорость движения игрока – проверка входов A0,A1 long vo_10; // скорость изменения цифры игрока long vo_11; // скорость появления цифр long vo_12; // скорость движения цифр int count_objects; //кол-во объектов на поле int level; // уровень игры int balls; // кол-во баллов }; int MAX_OBJ=30; int FIGURA={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}, {0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}, {0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}, {0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}, {0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}, {0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0} };

Управления положением игрока с помощью джойстика.

Положение игрока на экране определяется отклонением джойстика. Выводы джойстика подключены к аналоговым портам A0, A1 платы Arduino. Опрос портов происходит через время, определенное параметром GAME.vk, эти данные обрабатываются функцией map(), которая пропорционально переносит значение из текущего диапазона 0-124 в новый диапазон (значения ширины и высоты экрана). Затем это значение переводится в координаты знакоместа. В это знакоместо необходимо переместить изображение символа игрока, предварительно поместив в предыдущее положение игрока символ пробела. Для отображения игрока используется мигающий символ – цифра и пробел.

//****************** установка нового положения игрока void set_iam() { TV.set_cursor(min(123,GAME1.xk*4),min(84,GAME1.yk*6)); TV.print(” “); GAME1.xk=map(analogRead(A0), 0, 1024, 0, 128); GAME1.yk=map(analogRead(A1), 0, 1024, 0, 96); GAME1.xk=GAME1.xk/4; GAME1.yk=GAME1.yk/6; GAME1.vblink–; if(GAME1.vblink<0) { GAME1.blinkCursor=1-GAME1.blinkCursor; GAME1.vblink=5+GAME1.blinkCursor*5; } TV.set_cursor(min(123,GAME1.xk*4),min(84,GAME1.yk*6)); if(GAME1.blinkCursor==1) TV.print(GAME1.tekCursor); else TV.print(” “); }

Символ, отображающий игрока меняется через время, определенное параметром GAME.vo_10, вызовом функции set_new_cursor(), изменение происходит случайным образом с помощью функции random().

//****************** установка нового вида символа игрока void set_new_cursor() { GAME1.tekCursor=random(0,10); }

Генерация и перемещение объектов-цифр.

Объекты-цифры генерируются через время, определенное параметром GAME.vo11. Вызывается функция set_new_object(). Информация обо всех объектах-цифрах хранится в массиве FIGURA. Программа ищет первый пустой индекс в массиве (FIGURA=0), и помещает в него новый объект-цифру. Цифровое значение и горизонтальная координата нового объекта генерируются функцией random, а вертикальная координата устанавливается равной нулю. Символ, отображающий новый объект-цифру, выводится на экран.

//****************** появление нового объекта-цифры void set_new_object() { int null_index=0; if(GAME1.count_objects)<> { for(int i=0;i;i++)<> { if(FIGURA[i]==0) {null_index=i;break;} } FIGURA=random(1,9); FIGURA=random(0,MAX_X); FIGURA=0; // вывод на доску TV.set_cursor(FIGURA*4,0); TV.print(FIGURA); GAME1.count_objects++; } }

Функция движении объектов-цифр (go_object()) вызывается через время, определенное параметром GAME.vo12. Визуализация движения объектов-цифр происходит путем стирания символа из предыдущего положения объекта(запись символа пробела), и записью символа объекта в новое положение (вертикальная координата знакоместа объекта-цифры увеличивается на единицу). По достижении низа экрана происходит удаление объекта-цифры (запись 0 в элемент массива FIGURA[i]), а также уменьшение количества баллов игрока.

//****************** движение объекта-цифры void go_object() { for(int i=0;i;i++)<> { if(FIGURA[i]>0) { TV.set_cursor(FIGURA[i]*4,FIGURA[i]*6); TV.print(” “); if(FIGURA[i])<> { FIGURA[i]++; TV.set_cursor(FIGURA[i]*4,FIGURA[i]*6); TV.print(FIGURA[i]); } else { TV.tone(294,200); change_balls(FIGURA[i]*(-1)); FIGURA[i]=0; GAME1.count_objects–; } } } }

Проверка столкновения игрока и объектов-цифр.

При перемещении символа игрока по экрану необходимо проверять столкновения игрока с объектами-цифрами. Для этого используем функцию collision(). После установки символа, соответствующего игроку, проверяем элементы массива FIGURA на соответствие координат объектов-цифр координатам символа игрока. При совпадении координат выполняем следующие действия:

  • уничтожается объект-цифра из массива FIGURA (запись 0 в FIGURA[[i]);
  • уменьшается на 1 счетчик количества объектов-цифр (GAME.count_objects)
  • производится изменение счетчика количества баллов игрока (вызов функции change_balls()).

//****************** проверка столкновения void collision() { for(int i=0;i;i++)<> { if(FIGURA[i]>0) { if(FIGURA[i]==GAME1.xk && FIGURA[i]==GAME1.yk) { TV.tone(740,200); if(FIGURA[i]==GAME1.tekCursor) change_balls(GAME1.tekCursor*GAME1.tekCursor); else if(FIGURA[i]>GAME1.tekCursor && GAME1.level>3) change_balls(FIGURA[i]*(-1)); else change_balls(FIGURA[i]+GAME1.tekCursor); FIGURA[i]=0; GAME1.count_objects–; } } } }

В функцию изменение счетчика количества баллов игрока (change_balls()) в качестве аргумента передается число, на которое следует увеличить (уменьшить) счетчик баллов игрока. Это число равно сумме объекта-цифры и цифры игрока. Если цифра игрока равняется цифре объекта-цифры, то передается значение, равное произведению цифр. Для повышения сложности игры с 4 уровня столкновение игрока с объектом-цифрой приводит не только к увеличению счетчика игрока, но и уменьшению (если цифра игрока меньше цифры объекта-цифры).

Счетчик баллов игрока.

Функция change_balls() производит изменение счетчика баллов игрока на величину входящего аргумента. Если счетчик количества баллов становится меньше 0, игра заканчивается, экран очищается и происходит перехов на начало игры. Кроме того, при достижении счетчиком определенных значений, происходит переход игры на новый, более высокий, уровень. Установку значений для нового уровня выполняет функция new_level().

//****************** изменение набранных балов void change_balls(int ball) { GAME1.balls=GAME1.balls+ball; if(GAME1.balls<0) start_game(); if(GAME1.balls>(GAME1.level+1)*100) new_level(GAME1.level); set_data_tablo(); }

Переход на новый уровень.

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

//****************** изменение уровня игры void new_level(int tek_level) { GAME1.level++; GAME1.vo_10=5000; GAME1.vo_11=2000-(GAME1.level-1)*100; GAME1.vo_12=1000-(GAME1.level-1)*100; }

Отображение данных игры на табло.

Табло с данными игрока находится внизу экрана. Здесь отображается текущие значения количество набранных баллов и уровень игры. При изменении счетчика баллов игрока происходит вызов функции set_data_tablo(), которое изменяет значение количества набранных баллов и уровень игры в переменных структуры GAME и на табло.

//****************** вывод данных на табло void set_data_tablo() { TV.print(20,91,” balls= “); TV.print(70,91,” level= “); TV.print(48,91,GAME1.balls); TV.print(98,91,GAME1.level); }

Звуковое сопровождение игры.

Для звукового оформления игры будем использовать функцию tone(frequency, duration) библиотеки TVOut. При достижении объектом-цифрой низа игрового поля – TV.tone(294,200) (функция go_object()), при столкновении игрока и объекта-цифры – TV.tone(740,200) (функция collision()). Если вы захотите в проект вставить фрагмент из последовательного воспроизведения нескольких нот, после вывода каждой ноты командой tone(frequency, duration) нобходимо вставлять командой delay() задержку длительностью не меньшей, чем параметр duration,6 – последовательное воспроизведении е двух но с паузой.

TV.tone(294,200); TV.delay(400); TV.tone(740,200);

Основной цикл игры.

Основной цикл программы состоит из вызова рассмотренных нами функций set_iam(), collision(), go_object(), set_new_object(), set_new_cursor() через промежуток времени установленный в переменных GAME.vk, GAME.vo_10, GAME.vo_11, GAME.vo_12.

int game1() { while(GAME1.balls>0 && GAME1.level<6) { long time2=millis(); if(time2-time11>GAME1.vk) {set_iam(); collision(); time11=time2; } if(time2-time12>GAME1.vo_12) {go_object();time12=time2;} if(time2-time13>GAME1.vo_11) {set_new_object();time13=time2;} if(time2-time14>GAME1.vo_10) {set_new_cursor();time14=time2;} TV.delay_frame(10); } if(GAME1.balls<0) return 0; else if(GAME1.level>5) return 0; else return 1; }

Добавляем меню для выбора игр.

Добавим в скетч меню для вывода трех игр, которые вы можно дописать самостоятельно.

void loop() { switch(menu(pmenu)) { case 1:start_game(); while(game1()>0); break; default: break; } } //***** меню для выбора игры int menu(int poz) { TV.clear_screen(); pmenu=max(poz,1); int pmenu1=pmenu; TV.println(60,30,”Game 1″); TV.println(60,50,”Game 2″); TV.println(60,70,”Game 3″); TV.draw_rect(50,5+20*pmenu,40,10,WHITE,INVERT); TV.delay(500); while(digitalRead(12)==LOW) { if(analogRead(A1)<100) pmenu=max(pmenu-1,1); else if(analogRead(A1)>900) pmenu=min(pmenu+1,3); else ; if(pmenu1!=pmenu) { TV.delay(500); TV.draw_rect(50,5+20*pmenu1,40,10,BLACK,INVERT); TV.draw_rect(50,5+20*pmenu,40,10,WHITE,INVERT); pmenu1=pmenu; } } return pmenu; }

И так, друзья мои, если у вас уже есть набор конструктор Arduino UNO R3 (стартовый набор) или вам стало скучно, с точки зрения техники — пожалуйста. У нас есть целая серия «уроков» для Ардуино. Хочу напомнить, что из этого набора можно сделать если не всё, то почти всё что будет работать или чем-то управлять. Игровая консоль, ну считайте джойстик для игры в пинг-понг и те только. У кого-то остался старый компьютер со старым не работающим джойстиком, теперь, всё это логика, которая управляется через Ардуино Кит (http://arduinokit.ru/).

С чего начать делать проект?

Я создал простую игровую приставку, которая может играть в пинг-понг и другие игры, помните как на стареньком компе Atari 2600, которой сейчас легко может эмулировать Arduino R3. Ардуино стал бы как тот старый компьютер, считывая входы и выходы данных с джойстика подключенного к телевизору через стандартную библиотеку TVout (Для Ардуино). Я создал этот проект только для того, что бы продемонстрировать, что любой (без особых навыков) может создавать свои собственные миниатюрные консоли и играть. Также я хотел бы что все кто прочитал эту статью смогут пользоваться этим советом совершенно бесплатно.

Что нужно для игровой приставки на Arduino?

5. Теперь, для игры в пинг-понг, нам нужно только 4-контактный разъём: Вперед (1) , Назад (2) , Кнопка (6) , и Земля (8). А для 2-го джойстика, нам нужно только контакты 1, 2 и 8 (Кнопка «главный» используется только, чтобы «начать игру» от имени Администратора, так вот почему только 1 игрок должен иметь эту кнопку — управлять) .

6. Если посмотреть на контакты сбоку, то я подключил эти 4 запайки с простыми проводами, которые идут к Arduino («Вперед» к выводу 2 , «Назад» к контакту 3, и в «Управление» к контакту 4). Я проделал то же самое для других 3 контактов для игрока 2 (на второй Джойстик): «Вперед» с контактом 7 и «Назад» к контакту 10). Не забудьте подключить оба джойстиков Контакт 8 на землю (экранный провод)!

7. Теперь, загрузите игру Ping-pong и проверить входы джойстика. Вставьте Arduino UNO R3 и все провода в необходимые разъёмы (я использовал сломанный внешний корпус старого жесткого диска) .

Когда я впервые попробовать сам сделать эту задачу, я сильно переживал, чтобы начать работу над этим проектом. Фуф было не легко, хотел завершить этот проект как можно скорее. Изначально моя главная проблема, то что я пытался реализовать старые джойстики от Атари к Arduino. Из-за этого некоторые интернет-блоггеры не правильно написали на своих форумах, что мол типа входы невозможно прочитать или припаять изнутри джойстика, однако глядя на это было весело использовать их стандартные контроллеры подсоединившись к ним из-вне, я почесал репу и много выслушал высказываний в свой адрес, мол «руки у меня не оттуда растут» или я паяльник не умею держать в руках. Тем не менее — мой проект работает! А вам слабо?

PS: Я думаю, что единственное, что я бы пересмотрел теперь по другому — хм. Ну например бы взял картридж от Сеги или Денди и сделал бы некий карт-ридер. Вот вам и телевизионная игровая приставка своими руками. Главное, что мой проект масштабирован, можно делать если не летающих роботов, так игровые приставки своими руками. Кстати говоря, в у наших друзей есть различные компоненты для Arduino UNO R3 (Конструкторы Ардуино). Если сильно захотеть, то всё можно сделать своими руками. И это работает!

mob_info