Приступим к созданию игры с условным названием "Арифметический сборщик". Игрок управляется джойстиком с возможностью перемещения по полю размером 128x90. При нулевых отклонениях джойстика игрок находится в центре. Максимальное отклонение джойстика соответствует максимальному перемещению игрока. С определенным интервалом времени генерируются объекты-цифры, которые движутся сверху вниз. По достижении нижнего положения экрана объект-цифра исчезает, уменьшая количество баллов игрока на величину данной цифры. Если игрок перехватывает объект-цифру на экране, то это приводит к исчезновению объекта-цифры и увеличению счетчика баллов игрока. С определенной периодичностью значок игрока меняет свое значение с цифры 0 до 9. При перехвате объекта-цифры счетчик баллов игрока увеличивается на сумму, равную сумме объекта-цифры и цифры игрока, если цифра игрока равняется цифре объекта-цифры, то счетчик баллов игрока увеличивается на произведение цифр. По достижении определенных порогов баллов, происходит переход на более высокий уровень игры, что приводит к увеличению скорости движения и скорости генерации объектов-цифр. Кроме того с 4 уровня игры столкновение игрока с объектом-цифрой приводит не только к увеличению счетчика игрока, но и уменьшению (если цифра игрока меньше цифры объекта-цифры). Если количество баллов игрока становится меньше 0, игра начинается сначала.
Вот видео того, что получилось
Скачать архив со скетчем и файлами библиотеки TVOut можно по ссылке
И сам процесс создания игры
Создание переменных игры
Для управления игрой создадим объекты для хранения текущего положения игры. Символ, отображающий игрока, и символы, отображающие объекты-цифры, выводятся как текстовая информация, поэтому поделим все поле игры на строки и все перемещения объектов будем производить как вывод символа в знакоместо на поле. Переменные MAX_X=31 и MAX_Y=14 определяют размер поля игры по количеству знакомест по горизонтали и вертикали. Переменная MAX_OBJ=30 определяет максимальное количество одновременно находящихся на поле игры объектов-цифр. Массив int FIGURA[30][3] хранит информации об объектах-цифрах, находящихся на поле игры следующим образом:
- FIGURA[i][0] – числовое значение объекта-цифры (0 – пустой объект);
- FIGURA[i][1] – текущая координата x объекта-цифры;
- FIGURA[i][2] – текущая координата 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[30][3]={{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[30][3]. Программа ищет первый пустой индекс в массиве (FIGURA[30][3]=0), и помещает в него новый объект-цифру. Цифровое значение и горизонтальная координата нового объекта генерируются функцией random, а вертикальная координата устанавливается равной нулю. Символ, отображающий новый объект-цифру, выводится на экран.
//****************** появление нового объекта-цифры void set_new_object() { int null_index=0; if(GAME1.count_objects)<> { for(int i=0;i;i++)<> { if(FIGURA[i][0]==0) {null_index=i;break;} } FIGURA[null_index][0]=random(1,9); FIGURA[null_index][1]=random(0,MAX_X); FIGURA[null_index][2]=0; // вывод на доску TV.set_cursor(FIGURA[null_index][1]*4,0); TV.print(FIGURA[null_index][0]); GAME1.count_objects++; } }
Функция движении объектов-цифр (go_object()) вызывается через время, определенное параметром GAME.vo12. Визуализация движения объектов-цифр происходит путем стирания символа из предыдущего положения объекта(запись символа пробела), и записью символа объекта в новое положение (вертикальная координата знакоместа объекта-цифры увеличивается на единицу). По достижении низа экрана происходит удаление объекта-цифры (запись 0 в элемент массива FIGURA[i][0]), а также уменьшение количества баллов игрока.
//****************** движение объекта-цифры void go_object() { for(int i=0;i;i++)<> { if(FIGURA[i][0]>0) { TV.set_cursor(FIGURA[i][1]*4,FIGURA[i][2]*6); TV.print(" "); if(FIGURA[i][2])<> { FIGURA[i][2]++; TV.set_cursor(FIGURA[i][1]*4,FIGURA[i][2]*6); TV.print(FIGURA[i][0]); } else { TV.tone(294,200); change_balls(FIGURA[i][0]*(-1)); FIGURA[i][0]=0; GAME1.count_objects--; } } } }
Проверка столкновения игрока и объектов-цифр.
При перемещении символа игрока по экрану необходимо проверять столкновения игрока с объектами-цифрами. Для этого используем функцию collision(). После установки символа, соответствующего игроку, проверяем элементы массива FIGURA на соответствие координат объектов-цифр координатам символа игрока. При совпадении координат выполняем следующие действия:
- уничтожается объект-цифра из массива FIGURA (запись 0 в FIGURA[[i][0]);
- уменьшается на 1 счетчик количества объектов-цифр (GAME.count_objects)
- производится изменение счетчика количества баллов игрока (вызов функции change_balls()).
//****************** проверка столкновения void collision() { for(int i=0;i;i++)<> { if(FIGURA[i][0]>0) { if(FIGURA[i][1]==GAME1.xk && FIGURA[i][2]==GAME1.yk) { TV.tone(740,200); if(FIGURA[i][0]==GAME1.tekCursor) change_balls(GAME1.tekCursor*GAME1.tekCursor); else if(FIGURA[i][0]>GAME1.tekCursor && GAME1.level>3) change_balls(FIGURA[i][0]*(-1)); else change_balls(FIGURA[i][0]+GAME1.tekCursor); FIGURA[i][0]=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; }
Комментарии (2) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
[Автор]