Arduino:Примеры/TFTPong

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Вариация Pong на TFT-экране[1]

Этот скетч является примитивной вариацией игры Pong с использованием TFT-экрана Arduino.

В коде создается два объекта: прямоугольная платформа, способная двигаться в двух направлениях (по осям X и Y), а также мячик, способный отскакивать от этой платформы и от краев экрана. Позиция платформы управляется двумя потенциометрами (или другими аналоговыми сенсорами).

Кроме того, этот пример демонстрирует использование так называемого «обнаружения столкновений» объектов на экране, а также то, как быстро обновить изображения без необходимости стирать весь экран при каждом проходе через loop().

Необходимое оборудование

  • Плата Arduino Uno;
  • TFT-экран Arduino;
  • Макетная плата Breadboard;
  • Провода-перемычки
  • Два потенциометра на 10 кОм;

Цепь

Подключите контакты с питанием и «землей» к макетной плате.

Подключите оба потенциометра к макетной плате: их боковые контакты к «земле» и питанию, а центральные – к 0-ому и 1-ому аналоговым контактам.

Подключите TFT-экран к макетной плате. Подключить его нужно той стороной, где находятся стрелочка и маленькая голубая полоска. Также обратите внимание на ориентацию экрана. На этих рисунках он подсоединен вверх тормашками.

Контакты BL и +5V подключите к питанию, а GND – к «земле». CS-LD подключите к 10-ому контакту, DC – к 9-ому, RESET – к 8-ому, MOSI – к 11-ому, а SCK – к 13-ому. В случае с Leonardo вам надо будет использовать другие контакты, читайте об этом на странице о TFT-экране или в коде ниже.

Код

Чтобы использовать экран, подключаем библиотеки SPI и TFT.

#include <SPI.h>
#include <TFT.h>

Определяем контакты, которые будут использоваться для управления экраном, а затем создаем экземпляр класса TFT под названием TFTscreen. Будем обращаться к этому объекту каждый раз при работе с экраном.

#define cs   10
#define dc   9
#define rst  8  

TFT TFTscreen = TFT(cs, dc, rst);

Задаем переменные для текущих и предыдущих координат (X и Y) платформы и мяча, а также для направления полета мяча.

int paddleX = 0;
int paddleY = 0;
int oldPaddleX, oldPaddleY;
int ballDirectionX = 1;
int ballDirectionY = 1;

int ballX, ballY, oldBallX, oldBallY;

В блоке setup() инициализируем дисплей и очищаем фон экрана.

void setup() {
  TFTscreen.begin();
  TFTscreen.background(0,0,0); 
}

Секция loop() начинается с создания переменных, хранящих ширину и высоту экрана. Далее считываем данные с потенциометров и приспосабливаем их к необходимому диапазону.

void loop() {
  int myWidth = TFTscreen.width();
  int myHeight = TFTscreen.height();

  paddleX = map(analogRead(A0), 0, 1023, 0, myWidth) - 20/2; 
  paddleY = map(analogRead(A1), 0, 1023, 0, myHeight) - 5/2;

Задаем фоновый цвет (черный) и стираем предыдущую позицию платформы (если она сдвинулась).

TFTscreen.fill(0,0,0);

  if (oldPaddleX != paddleX || oldPaddleY != paddleY) {
    TFTscreen.rect(oldPaddleX, oldPaddleY, 20, 5);
  }

Делаем цвет заливки белым, а затем рисуем платформу.

TFTscreen.fill(255,255,255);
  TFTscreen.rect(paddleX, paddleY, 20, 5);

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

oldPaddleX = paddleX;
oldPaddleY = paddleY;

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

Для обновления позиции мяча воспользуемся пользовательской функцией moveBall().

if (millis() % ballSpeed < 2) {
  moveBall();
  }
}

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

void moveBall() {
  if (ballX > TFTscreen.width() || ballX < 0) {
    ballDirectionX = -ballDirectionX;
  }
  if (ballY > TFTscreen.height() || ballY < 0) {
    ballDirectionY = -ballDirectionY;
  }  
  if (inPaddle(ballX, ballY, paddleX, paddleY, 20, 5)) {
    ballDirectionY = -ballDirectionY;
  }

  ballX += ballDirectionX;
  ballY += ballDirectionY;

  TFTscreen.fill(0,0,0);

  if (oldBallX != ballX || oldBallY != ballY) {
    TFTscreen.rect(oldBallX, oldBallY, 5, 5);
  }

  TFTscreen.fill(255,255,255);
  TFTscreen.rect(ballX, ballY, 5, 5);

  oldBallX = ballX;
  oldBallY = ballY;
}

То есть, в функции inPaddle() проверяется, не занимают ли платформа и мяч одно и то же место в пространстве. Если да, то она возвращает значение TRUE, благодаря чему направление полета мяча реверсируется, как если бы он ударился о край экрана.

boolean inPaddle(int x, int y, int rectX, int rectY, int rectWidth, int rectHeight) {
  boolean result = false;

  if ((x >= rectX && x <= (rectX + rectWidth)) && 
    (y >= rectY && y <= (rectY + rectHeight))) {
    result = true; 
  }
  return result;  
}

Весь код полностью – ниже:

/*
Игра в Pong на TFT-экране Arduino

Этот пример для TFT-экрана Arduino считывает данные
от двух потенциометров, чтобы перемещать по осям X и Y
прямоугольную платформу. Эта платформа может взаимодействовать
с мячиком – ударяясь о нее, он будет отскакивать и лететь дальше.

Этот пример кода не защищен авторским правом.

Создан в декабре 2012 Томом Иго (Tom Igoe),
модифицирован 15 апреля 2013 Скоттом Фитцджеральдом (Scott Fitzgerald).

http://www.arduino.cc/en/Tutorial/TFTPong
*/

#include <TFT.h>  // библиотека для TFT-экрана Arduino
#include <SPI.h>

// Определяем контакты для Arduino Uno:
#define cs   10
#define dc   9
#define rst  8

// Определяем контакты для Arduino Leonardo (раскомментируйте, чтобы использовать):
// #define cs   7
// #define dc   0
// #define rst  1

TFT TFTscreen = TFT(cs, dc, rst);

// Переменные для позиции мяча и платформы, а также для направления полета мяча:
int paddleX = 0;
int paddleY = 0;
int oldPaddleX, oldPaddleY;
int ballDirectionX = 1;
int ballDirectionY = 1;

int ballSpeed = 10; // чем ниже это число, тем быстрее скорость мячика

int ballX, ballY, oldBallX, oldBallY;

void setup() {
  // Инициализируем дисплей:
  TFTscreen.begin();
  // Делаем экран черным:
  TFTscreen.background(0, 0, 0);
}

void loop() {

  // Сохраняем ширину и высоту экрана:
  int myWidth = TFTscreen.width();
  int myHeight = TFTscreen.height();

  // Подстраиваем позицию платформы к позициям потенциометров:
  paddleX = map(analogRead(A0), 512, -512, 0, myWidth) - 20 / 2;
  paddleY = map(analogRead(A1), 512, -512, 0, myHeight) - 5 / 2;

  // Делаем цвет заливки черным и стираем предыдущую позицию платформы, 
  // если ее новые координаты отличаются от предыдущих:
  TFTscreen.fill(0, 0, 0);

  if (oldPaddleX != paddleX || oldPaddleY != paddleY) {
    TFTscreen.rect(oldPaddleX, oldPaddleY, 20, 5);
  }

  // Рисуем платформу и сохраняем ее текущие координаты как предыдущие:
  TFTscreen.fill(255, 255, 255);

  TFTscreen.rect(paddleX, paddleY, 20, 5);
  oldPaddleX = paddleX;
  oldPaddleY = paddleY;

  // Обновляем позицию мяча и рисуем его на экране:
  if (millis() % ballSpeed < 2) {
    moveBall();
  }
}

// Эта функция определяет позицию мяча на экране:
void moveBall() {
  // Если мячик улетает за пределы экрана, реверсируем направление его полета:
  if (ballX > TFTscreen.width() || ballX < 0) {
    ballDirectionX = -ballDirectionX;
  }

  if (ballY > TFTscreen.height() || ballY < 0) {
    ballDirectionY = -ballDirectionY;
  }

  // Проверяем, не занимают ли мячик и платформа одно и то же место на экране:
  if (inPaddle(ballX, ballY, paddleX, paddleY, 20, 5)) {
    ballDirectionX = -ballDirectionX;
    ballDirectionY = -ballDirectionY;
  }

  // Обновляем позицию мяча:
  ballX += ballDirectionX;
  ballY += ballDirectionY;

  // Стираем предыдущую позицию мяча:
  TFTscreen.fill(0, 0, 0);

  if (oldBallX != ballX || oldBallY != ballY) {
    TFTscreen.rect(oldBallX, oldBallY, 5, 5);
  }


  // Рисуем текущую позицию мяча:
  TFTscreen.fill(255, 255, 255);
  TFTscreen.rect(ballX, ballY, 5, 5);

  oldBallX = ballX;
  oldBallY = ballY;

}

// Эта функция проверяет, не пересекаются ли позиции мяча и платформы:
boolean inPaddle(int x, int y, int rectX, int rectY, int rectWidth, int rectHeight) {
  boolean result = false;

  if ((x >= rectX && x <= (rectX + rectWidth)) &&
      (y >= rectY && y <= (rectY + rectHeight))) {
    result = true;
  }

  return result;
}

См.также

Внешние ссылки