Главная » Статьи » Arduino с нуля » Arduino и Processing

PDE. Урок 4. Создание анимационной заставки в стиле Windows

В этом уроке мы напишем графическую программу, которая будет отображать работу заставки в стиле Windows. 

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

 

 

Весь урок будет разбит на 6 частей:

 

Общая часть

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

  • Создать "летающий" объект на экране;
  • Сделать так, чтобы при касании объекта с границей окна, шар отлетал в противоположном направлении. Причем угол падения должен быть равен углу отражения;
  • Реализовать изменение заливки фона. С каждым касанием объекта с границей, фон должен меняться. 

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

Как мы видим, у шара есть 12 путей направления. Он может лететь как снизу вверх, так и справа налево. Поэтому все эти случаи надо описать. Иначе программа не будет работать так, как нам нужно. 

С перемещением мы определились. Теперь нужно определить коснулся ли наш шар границы, или все еще находится в полете. Для этого нам необходимо знать текущие координаты шарика (x1 и y1), размеры экрана (W и H), размеры шара (диаметр, либо радиус), и конечно же направление движения шара. Общий вид шара при касании края поля (справа и снизу):

 

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

Мы видим, что если шар находится одной своей стороной на границе, то расстояние от его текущих координат (x1 и y1)  до края поля (W или H), будут равны радиусу (radius) этого шара. 

 

Также, определение касания, зависит от направления полета шарика. На рисунке написаны две формулы. Эти формулы справедливы, если шар будет двигаться либо вниз, либо вправо.  Если же шар будет лететь влево и вверх, то формулы  будут такими:

x = 0 + radius;

y = 0 + radius;

Ничего трудного здесь нет. Достаточно просто представить шарик, который касается своей стороной нулевых координатах окна (левый верхний угол).

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

 

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

Например, шарик прилетел по траектории 10 (слева вниз) и коснулся границы, значит он должен оттолкнутся и полететь дальше по траектории ​(снизу направо). Также и с другими направлениями. Прилетел по траектории ​(снизу налево), по траектории​(слева вверх) улетел. Думаю это понятно. Так что нужно будет описать полет шара после ударения об край поля, в зависимости от прилета. 

 

Ну и конечно же изменение заливки фона. С этим проблем возникнуть не должно. Если шар коснулся границы, просто меняем составляющие цвета и обновляем экран. Для более красивого эффекта, сделаем так, чтобы все составляющие изменялись случайным образом.

 

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

 

Скелет будущей программы

Написание программы всегда начинайте с составления небольшого плана (скелета программы). Это делается для упрощения программирования, так будет намного удобнее и понятнее. 

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

Примерно будущая программа будет выглядеть так:

//Параметры экрана

//Начальные параметры шара

//Текущие параметры шара

void setup()
{
 //Размер экрана

}

void draw()
{
 //заливка экрана
 
 //Отображение шара 
 
 //Вызов функций полета и проверки на столкновение 
 
}


//Функция полета
void sky()
{

}

//Функция проверки на столкновение
void clash()
{

}

//функция настройки цвета
void col()
{

}

 

Как Вы видите, мы описали все основные блоки, которые будем использовать в программе. Начиная от начальных параметров, заканчивая функциями. Теперь будем постепенно заполнять все операции, производимые в блоках. 

 

Основная часть

Начинать заполнение, конечно же, нужно с объявления переменных. Задаем начальные данные для экрана и шарика, а также все параметры, которые в процессе выполнения будут меняться. 

Начинаем с параметров экрана. Необходимо описать все составляющие цвета, из которых будет складываться заливка фона. 

//параметры экрана
float r = 0; //красный цвет
float g = 0; //зеленый цвет
float b = 0; //синий цвет

 

Теперь переходим к начальным параметрам шара. Здесь мы зададим диаметр шарика, а также его начальные координаты, и режим полета. Задавать эти параметры удобно тем, что если мы захотим изменить размер шара, или же его начальное положение, нам не нужно будет искать в коде те строки, где эти параметры используются. Достаточно будет изменить эти данные в самом начале программы. Режим полета отвечает за направление полета шара (если помните, у нас их 12). 

//Начальные параметры шара
float diametr = 50; //диаметр шара
float x0 = 30; //координата x начальная
float y0 = 30; //координата y начальная
int move = 1; //режим полета
float dx = 1; //шаг изменения координаты по x
float dy = 1; //шаг изменения координаты по y

 

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

//Текущие параметры шара
float x = x0; //координата x текущая
float y = y0; //координата y текущая

 

Теперь переходим к основным функциям нашей программы. В функции setup() мы создаем окно программы. Размеры могут быть любыми. Потом, в процессе написания, сделаем так, чтобы изменив размеры экрана, наша программа сама "адаптировалась" под новые размеры.

void setup()
{
 //Размер экрана
 size(300,300); //создаем окно
}

 

В функции draw(), изменяем заливку экрана, используя все три составляющие цвета. Отображаем шар, с начальными параметрами. Остальные функции пока не трогаем.

void draw()
{
 //заливка экрана
 background(r,g,b);
 
 //Отображение шара 
 fill(255);
 ellipse(x,y,diametr,diametr);

 //Вызов функций полета и проверки на столкновение
 
}

 

Давайте запустим программу на данном этапе разработки. Получаем такое окно:

 

 

Мы отобразили наш шар в окне, с начальными координатами x = y = 30px. Теперь нужно чтобы объект двигался. Для этого необходимо в функции draw() вставить функцию sky(). И саму функцию sky() заполнить.  Функция draw() изменится следующим образом:

void draw()
{
 //заливка экрана
 background(r,g,b);
 
 //Отображение шара 
 fill(255);
 ellipse(x,y,diametr,diametr);

 //Вызов функций полета и проверки на столкновение
 sky(); //полет 
}

 

В функции sky(), нужно описать все 12 случаев полета шара. Чтобы понять как это сделать, вернемся опять же к первому рисунку. Если траектория полета будет 8 (снизу направо), то координаты x и y будут меняться таким образом:

x = x + dx;

y = y - dy;

То есть, координата x будет увеличиваться на шаг изменения координаты dx, а координата y наоборот уменьшаться на шаг dy

А если взять, например, траекторию 5 (сверху налево), то координата x будет уменьшаться, а координата y увеличиваться.

Исходя из этого, составляем 12 случаев полета. Получаем следующее:

void sky()
{
 if (move == 1) //если полет сверху вниз
 {
 y = y + dy;
 }
 else if (move == 2) //если полет снизу вверх
 {
 y = y - dy;
 }
 else if (move == 3) //если полет слева направо
 {
 x = x + dx;
 }
 else if (move == 4) //если полет справа налево
 {
 x = x - dx;
 }
 else if (move == 5) //если полет сверху налево
 {
 x = x - dx;
 y = y + dy;
 }
 else if (move == 6) //если полет сверху направо
 {
 x = x + dx;
 y = y + dy;
 }
 else if (move == 7) //если полет снизу налево
 {
 x = x + dx;
 y = y - dy; 
 }
 else if (move == 8) //если полет снизу направо
 {
 x = x + dx;
 y = y - dy; 
 }
 else if (move == 9) //если полет слева вверх
 {
 x = x + dx;
 y = y - dy; 
 }
 else if (move == 10) //если полет слева вниз
 {
 x = x + dx;
 y = y + dy; 
 }
 else if (move == 11) //если полет справа вверх
 {
 x = x - dx;
 y = y - dy; 
 }
 else if (move == 12) //если полет справа вниз
 {
 x = x - dx;
 y = y + dy; 
 }
 
}

 

Когда все траектории полета описаны, запускаем программу:

 

Также мы можем изменить начальные координаты и траекторию полета, задав в начальных параметрах переменной move иное число:

 

И конечно же шаг изменения координат:

 

Поэкспериментируйте с этими параметрами, чтобы лучше понять весь принцип работы. 

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

Для этого, теперь, необходимо задействовать функцию clash(). Та самая функция, которая отвечает за проверку касания объекта с краем поля. В функции draw(), дописываем строку:

clash(); //проверяем на столкновение

 

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

Немного посидев над кодом, получаем следующее:

//Функция проверки на столкновение
void clash()
{
 if (((x + diametr/2) >= width) & (move == 6)) //Достижение конца окна справа, если полет сверху
 { 
 move = 12;
 col();
 }
 else if (((x - diametr/2) <= 0) & (move == 5)) //Достижение конца окна слева, если полет сверху
 {
 move = 10;
 col();
 }
 else if (((y - diametr/2) <= 0) & (move == 11)) //Достижение конца окна сверху, если полет справа
 {
 move = 5;
 col();
 } 
 else if (((y + diametr/2) >= height) & (move == 12)) //Достижение конца окна снизу, если полет справа
 {
 move = 7;
 col();
 } 
 else if (((x - diametr/2) <= 0) & (move == 7)) //Достижение конца окна слева, если полет снизу
 {
 move = 9;
 col();
 }
 else if (((y - diametr/2) <= 0) & (move == 9)) //Достижение конца окна сверху, если полет слева
 {
 move = 6;
 col();
 } 
 else if (((y + diametr/2) >= height) & (move == 10)) //Достижение конца окна снизу, если полет слева
 {
 move = 8;
 col();
 } 
 else if (((x - diametr/2) <= 0) & (move == 4)) //Достижение конца окна слева, если полет справа
 {
 move = 3;
 col();
 }
 else if (((y - diametr/2) <= 0) & (move == 2)) //Достижение конца окна сверху, если полет снизу
 {
 move = 1;
 col();
 } 
 else if (((y + diametr/2) >= height) & (move == 1)) //Достижение конца окна снизу, если полет сверху
 {
 move = 2;
 col();
 } 
 else if (((x + diametr/2) >= width) & (move == 8)) //Достижение конца окна справа, если полет снизу
 { 
 move = 11;
 col();
 }
 else if (((x + diametr/2) >= width) & (move == 3)) //Достижение конца окна справа, если полет слева
 { 
 move = 4;
 col();
 } 
 
}

 

В программе присутствуют две зарезервированные переменные: width и height. Они обозначают размеры окна, ширины и высоты соответственно. Т.е если мы пишем width, то программа понимает это как значение размера нашего окна в ширину (хранятся размеры в функции size()). 

Хотелось бы объяснить всю функцию clash() на примере двух случайных условий:

 else if (((y + diametr/2) >= height) & (move == 10)) //Достижение конца окна снизу, если полет слева
 {
 move = 8;
 col();
 } 
 else if (((x - diametr/2) <= 0) & (move == 4)) //Достижение конца окна слева, если полет справа
 {
 move = 3;
 col();
 }

 

Исходя из того, что шар прилетел по траектории 10 (слева вниз), то вычисляем касание по следующей схеме: если мы определили, что сумма текущий координаты y и радиуса шара (diametr / 2) превышает или равна высоте нашего окна (т.е соприкасается с границей поля), мы задаем новую траекторию объекта. Так как шар прилетел по траектории 10 (слева вниз), то после столкновения, последующее направление будет 8 (снизу направо). И мы должны изменить цвет заливки, поэтому вызываем функцию col()

 

Все тоже самое и для второго условия, только тут шар летит по другой траектории. Поэтому и условие определения касания будет другим. Так же и последующее направление полета, после столкновения.  

И давайте сразу запишем функцию настройки цвета. Сделаем так, чтобы значения выбирались случайным образом. Для этого пишем следующее:

//функция настройки цвета
void col()
{
 r = random(255);
 g = random(255);
 b = random(255);
}

 

Конечный код программы:

//Параметры экрана
float r = 0; //красный цвет
float g = 0; //зеленый цвет
float b = 0; //синий цвет

//Начальные параметры шара
float diametr = 50; //диаметр шара
float x0 = 200; //координата x начальная
float y0 = 30; //координата y начальная

//Текущие параметры шара
float x = x0; //координата x текущая
float y = y0; //координата y текущая

//Изменяемые параметры
int move = 5; //режим полета
float dx = 1; //шаг изменения координаты по x
float dy = 1; //шаг изменения координаты по y

void setup()
{
 //Размер экрана
 size(300,300); //создаем окно
}

void draw()
{
 //заливка экрана
 background(r,g,b);
 
 //Отображение шара 
 fill(255);
 ellipse(x,y,diametr,diametr);

 //Вызов функций полета и проверки на столкновение
 sky(); //полет
 clash(); //проверяем на столкновение
 
}

//Функция полета
void sky()
{
 if (move == 1) //если полет сверху вниз
 {
 y = y + dy;
 }
 else if (move == 2) //если полет снизу вверх
 {
 y = y - dy;
 }
 else if (move == 3) //если полет слева направо
 {
 x = x + dx;
 }
 else if (move == 4) //если полет справа налево
 {
 x = x - dx;
 }
 else if (move == 5) //если полет сверху налево
 {
 x = x - dx;
 y = y + dy;
 }
 else if (move == 6) //если полет сверху направо
 {
 x = x + dx;
 y = y + dy;
 }
 else if (move == 7) //если полет снизу налево
 {
 x = x + dx;
 y = y - dy; 
 }
 else if (move == 8) //если полет снизу направо
 {
 x = x + dx;
 y = y - dy; 
 }
 else if (move == 9) //если полет слева вверх
 {
 x = x + dx;
 y = y - dy; 
 }
 else if (move == 10) //если полет слева вниз
 {
 x = x + dx;
 y = y + dy; 
 }
 else if (move == 11) //если полет справа вверх
 {
 x = x - dx;
 y = y - dy; 
 }
 else if (move == 12) //если полет справа вниз
 {
 x = x - dx;
 y = y + dy; 
 }
 
}

//Функция проверки на столкновение
void clash()
{
 if (((x + diametr/2) >= width) & (move == 6)) //Достижение конца окна справа, если полет сверху
 { 
 move = 12;
 col();
 }
 else if (((x - diametr/2) <= 0) & (move == 5)) //Достижение конца окна слева, если полет сверху
 {
 move = 10;
 col();
 }
 else if (((y - diametr/2) <= 0) & (move == 11)) //Достижение конца окна сверху, если полет справа
 {
 move = 5;
 col();
 } 
 else if (((y + diametr/2) >= height) & (move == 12)) //Достижение конца окна снизу, если полет справа
 {
 move = 7;
 col();
 } 
 else if (((x - diametr/2) <= 0) & (move == 7)) //Достижение конца окна слева, если полет снизу
 {
 move = 9;
 col();
 }
 else if (((y - diametr/2) <= 0) & (move == 9)) //Достижение конца окна сверху, если полет слева
 {
 move = 6;
 col();
 } 
 else if (((y + diametr/2) >= height) & (move == 10)) //Достижение конца окна снизу, если полет слева
 {
 move = 8;
 col();
 } 
 else if (((x - diametr/2) <= 0) & (move == 4)) //Достижение конца окна слева, если полет справа
 {
 move = 3;
 col();
 }
 else if (((y - diametr/2) <= 0) & (move == 2)) //Достижение конца окна сверху, если полет снизу
 {
 move = 1;
 col();
 } 
 else if (((y + diametr/2) >= height) & (move == 1)) //Достижение конца окна снизу, если полет сверху
 {
 move = 2;
 col();
 } 
 else if (((x + diametr/2) >= width) & (move == 8))  //Достижение конца окна справа, если полет снизу
 { 
 move = 11;
 col();
 }
 else if (((x + diametr/2) >= width) & (move == 3)) //Достижение конца окна справа, если полет слева
 { 
 move = 4;
 col();
 } 
 
}

//функция настройки цвета
void col()
{
 r = random(255);
 g = random(255);
 b = random(255);
}

 

Запускаем программу:

 

Чтобы изменить угол полета, измените шаг изменения координат, чтобы dx и dy отличались друг от друга. Кстати от этого меняется и скорость полета объекта. Можете также поэкспериментировать с координатами. 

 

Небольшие эксперименты

На основе этого кода можно создавать различные интересные графические программы. Немного изменив структуру. Например вот так:

 

Конечно можно доработать, и сделать более красочно. И зрелищнее. Можно сделать такой вариант: 

 

И многое другое, что Вы сами захотите. 

 

Задание для самостоятельного выполнения

Если Вы экспериментировали с параметрами шара, должны были заметить, что существуют недоработки кода. Заключается вот в таком поведении объекта:

 

 

Это было сделано специально, чтобы Вы и сами поняли из-за чего такое происходит, и смогли исправить все самостоятельно.

Вот собственно и задание. Исправьте код, чтобы программа обрабатывала все события правильно и без ошибок. 

 

Заключение

Это был четвертый урок в разделе "Arduino и Processing". Надеемся, что урок был для Вас полезен и понятен. Также надеемся, что выполнение задания не вызовет трудностей, и Вы справитесь самостоятельно. Если же возникнут вопросы или проблемы, задавайте их либо в комментариях, либо на форуме

 

 

Если Вам понравился наш урок, поделитесь им с друзьями.

 

 

 


←Предыдущий урок | Следующий урок→


 

Категория: Arduino и Processing | Добавил: GM (13.11.2015)
Просмотров: 3189 | Рейтинг: 5.0/2
Всего комментариев: 0
avatar