Игровой проект Arduino — копия Flappy Bird для Arduino
<основной класс="главный сайт" id="главный">
В этом проекте Arduino мы создадим крутую игру Arduino, фактически являющуюся копией популярной игры Flappy Bird для смартфонов, используя Arduino и сенсорный TFT-экран. Вы можете узнать, как это работает, посмотрев следующее видео или прочитав письменный текст ниже.
Обзор
Игра достаточно простая, но интересная и затягивающая. С помощью сенсорного экрана мы управляем птицей и пытаемся избежать движущихся столбов, скорость которых увеличивается по мере нашего продвижения. Также игра может сохранить ваш лучший результат, даже если вы отключите питание.[/column]
В предыдущем уроке (Учебник по Arduino TFT) мы подробно изучили, как использовать сенсорные экраны TFT с Arduino, и оставили пример игры для объяснения в этом уроке. Итак, теперь, как и в предыдущем уроке, мы шаг за шагом объясним код этой игры для Arduino.
Исходный код
Так как код немного длиннее и для лучшего понимания я буду размещать исходный код программы в разделах с описанием для каждого раздела. И в конце этой статьи я опубликую полный исходный код.
Мы будем использовать библиотеки UTFT и URTouch, созданные Хеннингом Карлсеном. Вы можете загрузить эти библиотеки с его сайта www.RinkyDinkElectronics.com. Также мы будем использовать библиотеку EEPROM для хранения наивысшего результата в EEPROM. EEPROM — это память, которая может хранить данные, даже когда плата выключена.
После того, как мы подключили библиотеки, нам нужно создать объекты UTFT и URTouch, а также определить переменные, необходимые для игры. В разделе настройки нам нужно инициировать отображение и касание, прочитать наивысший балл из EEPROM и запустить игру с помощью пользовательской функции инициироватьGame().
#include <UTFT.h>
#include <URTouch.h>
#include <EEPROM.h>
//==== Creating Objects
UTFT myGLCD(SSD1289,38,39,40,41); //Parameters should be adjusted to your Display/Schield model
URTouch myTouch( 6, 5, 4, 3, 2);
//==== Defining Fonts
extern uint8_t SmallFont[];
extern uint8_t BigFont[];
extern uint8_t SevenSegNumFont[];
extern unsigned int bird01[0x41A]; // Bird Bitmap
int x, y; // Variables for the coordinates where the display has been pressed
// Floppy Bird
int xP = 319;
int yP = 100;
int yB = 50;
int movingRate = 3;
int fallRateInt = 0;
float fallRate = 0;
int score = 0;
int lastSpeedUpScore = 0;
int highestScore;
boolean screenPressed = false;
boolean gameStarted = false;
void setup() {
// Initiate display
myGLCD.InitLCD();
myGLCD.clrScr();
myTouch.InitTouch();
myTouch.setPrecision(PREC_MEDIUM);
highestScore = EEPROM.read(0); // Read the highest score from the EEPROM
initiateGame(); // Initiate the game
}
Code language: Arduino (arduino)
Итак, с помощью пользовательской функции инициироватьGame() мы нарисуем начальное состояние игры, и вот как мы это сделаем. Сначала нам нужно очистить экран, затем нарисовать синий фон, нарисовать нижнюю часть, добавить текст и вызвать пользовательскую функцию drawBird(), чтобы нарисовать птицу. После этого нам нужен цикл while, который предотвратит запуск игры, пока мы не коснемся экрана. Итак, пока мы находимся в этом состоянии, если мы нажмем правый верхний угол, мы можем сбросить наивысший балл до нуля, а если мы нажмем в любом другом месте экрана, мы выйдем из цикла while и попадем в основной цикл кода, который запустит игру.
// ===== initiateGame - Custom Function
void initiateGame() {
myGLCD.clrScr();
// Blue background
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(0,0,319,239);
// Ground
myGLCD.setColor(221,216,148);
myGLCD.fillRect(0, 215, 319, 239);
myGLCD.setColor(47,175,68);
myGLCD.fillRect(0, 205, 319, 214);
// Text
myGLCD.setColor(0, 0, 0);
myGLCD.setBackColor(221, 216, 148);
myGLCD.setFont(BigFont);
myGLCD.print("Score:",5,220);
myGLCD.setFont(SmallFont);
myGLCD.print("HowToMechatronics.com", 140, 220);
myGLCD.setColor(0, 0, 0);
myGLCD.setBackColor(114, 198, 206);
myGLCD.print("Highest Score: ",5,5);
myGLCD.printNumI(highestScore, 120, 6);
myGLCD.print(">RESET<",255,5);
myGLCD.drawLine(0,23,319,23);
myGLCD.print("TAP TO START",CENTER,100);
drawBird(yB); // Draws the bird
// Wait until we tap the sreen
while (!gameStarted) {
if (myTouch.dataAvailable()) {
myTouch.read();
x=myTouch.getX();
y=myTouch.getY();
// Reset higest score
if ((x>=250) && (x<=319) &&(y>=0) && (y<=28)) {
highestScore = 0;
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(120, 0, 150, 22);
myGLCD.setColor(0, 0, 0);
myGLCD.printNumI(highestScore, 120, 5);
}
if ((x>=0) && (x<=319) &&(y>=30) && (y<=239)) {
gameStarted = true;
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(0, 0, 319, 32);
}
}
}
// Clears the text "TAP TO START" before the game start
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(85, 100, 235, 116);
}
Code language: Arduino (arduino)
В разделе основного цикла у нас есть переменная xP, которая используется для рисования столбов, а также переменная yP. В начале переменная xP имеет значение 319 в качестве размера экрана, а переменная yP имеет значение 100, которое является высотой первой стойки. На каждой итерации значение переменной xP уменьшается на значение переменной movingRate, которая в начале имеет значение 3, а по ходу игры увеличивается.
// The Main Loop Section
void loop() {
xP=xP-movingRate; // xP - x coordinate of the pilars; range: 319 - (-51)
drawPilars(xP, yP); // Draws the pillars
// yB - y coordinate of the bird which depends on value of the fallingRate variable
yB+=fallRateInt;
fallRate=fallRate+0.4; // Each inetration the fall rate increase so that we can the effect of acceleration/ gravity
fallRateInt= int(fallRate);
// Checks for collision
if(yB>=180 || yB<=0){ // top and bottom
gameOver();
}
if((xP<=85) && (xP>=5) && (yB<=yP-2)){ // upper pillar
gameOver();
}
if((xP<=85) && (xP>=5) && (yB>=yP+60)){ // lower pillar
gameOver();
}
// Draws the bird
drawBird(yB);
// After the pillar has passed through the screen
if (xP<=-51){
xP=319; // Resets xP to 319
yP = rand() % 100+20; // Random number for the pillars height
score++; // Increase score by one
}
//==== Controlling the bird
if (myTouch.dataAvailable()&& !screenPressed) {
fallRate=-6; // Setting the fallRate negative will make the bird jump
screenPressed = true;
}
// Doesn't allow holding the screen / you must tap it
else if ( !myTouch.dataAvailable() && screenPressed){
screenPressed = false;
}
// After each five points, increases the moving rate of the pillars
if ((score - lastSpeedUpScore) == 5) {
lastSpeedUpScore = score;
movingRate++;
}
}
Code language: Arduino (arduino)
Вот принцип работы игры:у нас есть столбы шириной 50 пикселей, которые движутся справа налево, и каждый следующий столб имеет разную случайную высоту. Логично, что для того, чтобы заставить их двигаться, после каждой итерации нам нужно очищать экран и перерисовывать графику со столбами в их новом положении. Однако мы не можем этого сделать из-за низкой частоты обновления экрана, что может привести к мерцанию графики. Для активации всех своих пикселей экрану требуется немного больше времени, поэтому нам придется импровизировать и перерисовывать только то, что движется.
Итак, давайте посмотрим, как это будет делать пользовательская функция drawPilars(). Он принимает переменные xP и yP и, используя их и функцию fillRect(), рисует столбы. Таким образом, на каждой итерации он рисует столбы в их новом месте с дополнительными синими прямоугольниками с левой и правой стороны, которые очищают предыдущий нарисованный столб, и таким образом мы фактически делаем импровизацию, просто перерисовывая движущиеся столбы. Операторы if здесь — дополнительная импровизация, потому что по какой-то причине функция fillRect() не работала, если ее параметр «x2» имел значение, выходящее за размер экрана. Кроме того, в конце этой пользовательской функции нам нужно напечатать значение достигнутого результата.
// ===== drawPlillars - Custom Function
void drawPilars(int x, int y) {
if (x>=270){
myGLCD.setColor(0, 200, 20);
myGLCD.fillRect(318, 0, x, y-1);
myGLCD.setColor(0, 0, 0);
myGLCD.drawRect(319, 0, x-1, y);
myGLCD.setColor(0, 200, 20);
myGLCD.fillRect(318, y+81, x, 203);
myGLCD.setColor(0, 0, 0);
myGLCD.drawRect(319, y+80, x-1, 204);
}
else if( x<=268) {
// Draws blue rectangle right of the pillar
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(x+51, 0, x+60, y);
// Draws the pillar
myGLCD.setColor(0, 200, 20);
myGLCD.fillRect(x+49, 1, x+1, y-1);
// Draws the black frame of the pillar
myGLCD.setColor(0, 0, 0);
myGLCD.drawRect(x+50, 0, x, y);
// Draws the blue rectangle left of the pillar
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(x-1, 0, x-3, y);
// The bottom pillar
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(x+51, y+80, x+60, 204);
myGLCD.setColor(0, 200, 20);
myGLCD.fillRect(x+49, y+81, x+1, 203);
myGLCD.setColor(0, 0, 0);
myGLCD.drawRect(x+50, y+80, x, 204);
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(x-1, y+80, x-3, 204);
}
// Draws the score
myGLCD.setColor(0, 0, 0);
myGLCD.setBackColor(221, 216, 148);
myGLCD.setFont(BigFont);
myGLCD.printNumI(score, 100, 220);
}
Code language: Arduino (arduino)
Вернувшись в раздел цикла, у нас есть переменная yB, которая представляет собой положение птицы по оси y, и она зависит от скорости падения, которая после каждой итерации увеличивается, и таким образом мы получаем эффект ускорения или гравитации. Кроме того, здесь мы проверяем наличие столкновений и используем операторы if, чтобы ограничить птицу так, что если она ударится о вершину, землю или столбы, игра закончится.
Далее идет пользовательская функция drawBird(), и давайте посмотрим, как она работает. Птица на самом деле является фотографией, преобразованной в растровое изображение с помощью инструмента ImageConverter565, созданного Хеннингом Карлсеном. Файл «.c», созданный с помощью инструмента, необходимо включить в каталог, чтобы он загружался при запуске скетча. Также мы должны определить растровое изображение таким образом, и с помощью функции drawBitmap() мы будем рисовать фотографию на экране. Птица имеет фиксированную координату X и переменную yB в качестве координаты Y. Как и в случае со столбами, мы очистим предыдущее состояние птицы, нарисовав два синих прямоугольника над и под птицей.
//====== drawBird() - Custom Function
void drawBird(int y) {
// Draws the bird - bitmap
myGLCD.drawBitmap (50, y, 35, 30, bird01);
// Draws blue rectangles above and below the bird in order to clear its previus state
myGLCD.setColor(114, 198, 206);
myGLCD.fillRoundRect(50,y,85,y-6);
myGLCD.fillRoundRect(50,y+30,85,y+36);
}
Code language: Arduino (arduino)
Вернувшись в цикл, мы видим, что после того, как столб прошел через экран, переменная xP будет сброшена до 319, yP получит новое случайное значение от 20 до 100 для высоты столбов, а счет увеличится на единицу. Следующим оператором if мы управляем птицей. Если мы нажмем на экран, мы установим отрицательную скорость падения, что заставит птицу прыгать, а оператор else if не позволит этому случиться, если мы просто удерживаем экран. Последнее выражение if относится к сложности игры и увеличивает скорость движения столбов после каждого точного очка.
Хорошо, теперь осталось посмотреть, как работает пользовательская функция gameOver(). После задержки в одну секунду он очистит экран, напечатает счет и некоторый текст, если счет выше, чем самый высокий балл, он запишет его в EEPROM, он сбросит все переменные в их начальные значения позиции и в конце он вызовет пользовательскую функцию инициироватьGame() для перезапуска игры.
//======== gameOver() - Custom Function
void gameOver() {
delay(1000); // 1 second
// Clears the screen and prints the text
myGLCD.clrScr();
myGLCD.setColor(255, 255, 255);
myGLCD.setBackColor(0, 0, 0);
myGLCD.setFont(BigFont);
myGLCD.print("GAME OVER", CENTER, 40);
myGLCD.print("Score:", 100, 80);
myGLCD.printNumI(score,200, 80);
myGLCD.print("Restarting...", CENTER, 120);
myGLCD.setFont(SevenSegNumFont);
myGLCD.printNumI(2,CENTER, 150);
delay(1000);
myGLCD.printNumI(1,CENTER, 150);
delay(1000);
// Writes the highest score in the EEPROM
if (score > highestScore) {
highestScore = score;
EEPROM.write(0,highestScore);
}
// Resets the variables to start position values
xP=319;
yB=50;
fallRate=0;
score = 0;
lastSpeedUpScore = 0;
movingRate = 3;
gameStarted = false;
// Restart game
initiateGame();
}
Code language: Arduino (arduino)
Вот и все, и я надеюсь, что объяснение кода было достаточно ясным. Если у вас есть какие-либо вопросы, не стесняйтесь задавать их в разделе комментариев ниже.
Вот полный код игры:
/* Arduino Game Proejct
* Program made by Dejan Nedelkovski,
* www.HowToMechatronics.com
*/
/* This program uses the UTFT and URTouch libraries
* made by Henning Karlsen.
* You can find and download them at:
* www.RinkyDinkElectronics.com
*/
#include <UTFT.h>
#include <URTouch.h>
#include <EEPROM.h>
//==== Creating Objects
UTFT myGLCD(SSD1289,38,39,40,41); //Parameters should be adjusted to your Display/Schield model
URTouch myTouch( 6, 5, 4, 3, 2);
//==== Defining Fonts
extern uint8_t SmallFont[];
extern uint8_t BigFont[];
extern uint8_t SevenSegNumFont[];
extern unsigned int bird01[0x41A]; // Bird Bitmap
int x, y; // Variables for the coordinates where the display has been pressed
// Floppy Bird
int xP = 319;
int yP = 100;
int yB = 50;
int movingRate = 3;
int fallRateInt = 0;
float fallRate = 0;
int score = 0;
int lastSpeedUpScore = 0;
int highestScore;
boolean screenPressed = false;
boolean gameStarted = false;
void setup() {
// Initiate display
myGLCD.InitLCD();
myGLCD.clrScr();
myTouch.InitTouch();
myTouch.setPrecision(PREC_MEDIUM);
highestScore = EEPROM.read(0); // Read the highest score from the EEPROM
initiateGame(); // Initiate the game
}
void loop() {
xP=xP-movingRate; // xP - x coordinate of the pilars; range: 319 - (-51)
drawPilars(xP, yP); // Draws the pillars
// yB - y coordinate of the bird which depends on value of the fallingRate variable
yB+=fallRateInt;
fallRate=fallRate+0.4; // Each inetration the fall rate increase so that we can the effect of acceleration/ gravity
fallRateInt= int(fallRate);
// Checks for collision
if(yB>=180 || yB<=0){ // top and bottom
gameOver();
}
if((xP<=85) && (xP>=5) && (yB<=yP-2)){ // upper pillar
gameOver();
}
if((xP<=85) && (xP>=5) && (yB>=yP+60)){ // lower pillar
gameOver();
}
// Draws the bird
drawBird(yB);
// After the pillar has passed through the screen
if (xP<=-51){
xP=319; // Resets xP to 319
yP = rand() % 100+20; // Random number for the pillars height
score++; // Increase score by one
}
//==== Controlling the bird
if (myTouch.dataAvailable()&& !screenPressed) {
fallRate=-6; // Setting the fallRate negative will make the bird jump
screenPressed = true;
}
// Doesn't allow holding the screen / you must tap it
else if ( !myTouch.dataAvailable() && screenPressed){
screenPressed = false;
}
// After each five points, increases the moving rate of the pillars
if ((score - lastSpeedUpScore) == 5) {
lastSpeedUpScore = score;
movingRate++;
}
}
// ===== initiateGame - Custom Function
void initiateGame() {
myGLCD.clrScr();
// Blue background
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(0,0,319,239);
// Ground
myGLCD.setColor(221,216,148);
myGLCD.fillRect(0, 215, 319, 239);
myGLCD.setColor(47,175,68);
myGLCD.fillRect(0, 205, 319, 214);
// Text
myGLCD.setColor(0, 0, 0);
myGLCD.setBackColor(221, 216, 148);
myGLCD.setFont(BigFont);
myGLCD.print("Score:",5,220);
myGLCD.setFont(SmallFont);
myGLCD.print("HowToMechatronics.com", 140, 220);
myGLCD.setColor(0, 0, 0);
myGLCD.setBackColor(114, 198, 206);
myGLCD.print("Highest Score: ",5,5);
myGLCD.printNumI(highestScore, 120, 6);
myGLCD.print(">RESET<",255,5);
myGLCD.drawLine(0,23,319,23);
myGLCD.print("TAP TO START",CENTER,100);
drawBird(yB); // Draws the bird
// Wait until we tap the sreen
while (!gameStarted) {
if (myTouch.dataAvailable()) {
myTouch.read();
x=myTouch.getX();
y=myTouch.getY();
// Reset higest score
if ((x>=250) && (x<=319) &&(y>=0) && (y<=28)) {
highestScore = 0;
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(120, 0, 150, 22);
myGLCD.setColor(0, 0, 0);
myGLCD.printNumI(highestScore, 120, 5);
}
if ((x>=0) && (x<=319) &&(y>=30) && (y<=239)) {
gameStarted = true;
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(0, 0, 319, 32);
}
}
}
// Clears the text "TAP TO START" before the game start
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(85, 100, 235, 116);
}
// ===== drawPlillars - Custom Function
void drawPilars(int x, int y) {
if (x>=270){
myGLCD.setColor(0, 200, 20);
myGLCD.fillRect(318, 0, x, y-1);
myGLCD.setColor(0, 0, 0);
myGLCD.drawRect(319, 0, x-1, y);
myGLCD.setColor(0, 200, 20);
myGLCD.fillRect(318, y+81, x, 203);
myGLCD.setColor(0, 0, 0);
myGLCD.drawRect(319, y+80, x-1, 204);
}
else if( x<=268) {
// Draws blue rectangle right of the pillar
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(x+51, 0, x+60, y);
// Draws the pillar
myGLCD.setColor(0, 200, 20);
myGLCD.fillRect(x+49, 1, x+1, y-1);
// Draws the black frame of the pillar
myGLCD.setColor(0, 0, 0);
myGLCD.drawRect(x+50, 0, x, y);
// Draws the blue rectangle left of the pillar
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(x-1, 0, x-3, y);
// The bottom pillar
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(x+51, y+80, x+60, 204);
myGLCD.setColor(0, 200, 20);
myGLCD.fillRect(x+49, y+81, x+1, 203);
myGLCD.setColor(0, 0, 0);
myGLCD.drawRect(x+50, y+80, x, 204);
myGLCD.setColor(114, 198, 206);
myGLCD.fillRect(x-1, y+80, x-3, 204);
}
// Draws the score
myGLCD.setColor(0, 0, 0);
myGLCD.setBackColor(221, 216, 148);
myGLCD.setFont(BigFont);
myGLCD.printNumI(score, 100, 220);
}
//====== drawBird() - Custom Function
void drawBird(int y) {
// Draws the bird - bitmap
myGLCD.drawBitmap (50, y, 35, 30, bird01);
// Draws blue rectangles above and below the bird in order to clear its previus state
myGLCD.setColor(114, 198, 206);
myGLCD.fillRoundRect(50,y,85,y-6);
myGLCD.fillRoundRect(50,y+30,85,y+36);
}
//======== gameOver() - Custom Function
void gameOver() {
delay(3000); // 1 second
// Clears the screen and prints the text
myGLCD.clrScr();
myGLCD.setColor(255, 255, 255);
myGLCD.setBackColor(0, 0, 0);
myGLCD.setFont(BigFont);
myGLCD.print("GAME OVER", CENTER, 40);
myGLCD.print("Score:", 100, 80);
myGLCD.printNumI(score,200, 80);
myGLCD.print("Restarting...", CENTER, 120);
myGLCD.setFont(SevenSegNumFont);
myGLCD.printNumI(2,CENTER, 150);
delay(1000);
myGLCD.printNumI(1,CENTER, 150);
delay(1000);
// Writes the highest score in the EEPROM
if (score > highestScore) {
highestScore = score;
EEPROM.write(0,highestScore);
}
// Resets the variables to start position values
xP=319;
yB=50;
fallRate=0;
score = 0;
lastSpeedUpScore = 0;
movingRate = 3;
gameStarted = false;
// Restart game
initiateGame();
}
Code language: Arduino (arduino)
Вот файл для загрузки с эскизом Arduino, изображением птицы и растровым файлом для птицы.
Производственный процесс
- Создание монитора Ambilight с помощью Arduino
- Игра Arduino Pong - OLED-дисплей
- Игровой контроллер Arduino
- Игра Arduino Touch Breakout
- Игра Giant Animatronics Lego Minfig Operation Game
- Детектор звуковой частоты
- Tech-TicTacToe
- Таймер обратного отсчета Arduino
- Игра Pixel Chaser
- Радиоуправляемый автомобиль Porsche (проект Arduino)