Arduino:Примеры/EsploraPong

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

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


Игра в Pong с помощью Esplora[1]

А не хотите ли поиграть с помощью Esplora в Pong? Этот пример покажет как.

Он создан, чтобы работать в паре со скетчем Processing (это язык программирования с открытым кодом). Esplora считывает данных со своих кнопок и слайдера, а затем через последовательный порт отправляет данные скетчу Processing, а он, в свою очередь использует их для перемещения «рокеток» в Pong.

Если Processing на вашем компьютере нет, его можно загрузить с сайта Processing, а затем установить, следуя руководству по инсталляции.

Скетч Processing к этому примеру можно скачать по этой ссылке(также с кодом можно ознакомиться ниже).

Вам нужно лишь распаковать этот фал в папке скетчей Processing, а затем открыть саму Processing и запустить там нужный файл с расширением *.pde.

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

  • Плата Arduino Esplora;

Необходимое ПО

Цепь

Для этого примера нужна лишь плата Arduino Esplora.

Элементы Esplora, необходимые для этого примера – кнопки и линейный потенциометр

Код для Arduino

Этот пример отправляет на компьютер данные о позиции слайдера линейного потенциометра и состоянии трех кнопок. Processing-скетч, связанный с этим Esplora-скетчем, считывает эти данные, чтобы управлять «рокетками» и другими элементами игры Pong.

Данные всегда отправляются в таком порядке: слайдер, 1-ая кнопка, 3-тья кнопка, 4-ая кнопка. Кроме того, эти данные разделены запятыми. Функция Serial.printIn() отправляет данные о последней кнопке, завершая их символом новой строки. Processing-скетчу этот символ нужен для того, чтобы знать, что он получил все необходимые данные от датчиков.

ПРИМЕЧАНИЕ: Одновременно к последовательным портам компьютера может иметь доступ только одна программа. То есть, если у вас открыт Serial Monitor, то скетчу Processing к последовательному порту Esplora доступ будет закрыт. Точно также, при запущенном скетче Processing открыть Serial Monitor и перепрограммировать Esplora будет нельзя.

/*

Игра в Pong с помощью Esplora

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

Слайдер отвечает за высоту «рокетки»,
кнопка 1 перезагружает игру,
кнопка 2 ставит мячик в центр,
кнопка 3 меняет игрока.

В эту игру можно играть и на одной, и на двух Esplora.

Создан 22 декабря 2012 Томом Иго (Tom Igoe).

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

*/

#include <Esplora.h>

void setup() {
  Serial.begin(9600);     // инициализируем последовательную передачу данных
}

void loop() {
  // Считываем данные от слайдера и трех кнопок
  int slider = Esplora.readSlider();
  int resetButton = Esplora.readButton(SWITCH_1);
  int serveButton = Esplora.readButton(SWITCH_3);
  int switchPlayerButton = Esplora.readButton(SWITCH_4);

  Serial.print(slider);                // выводим на Serial Monitor данные от слайдера
  Serial.print(",");                   // добавляем запятую
  Serial.print(resetButton);           // выводим данные от перезагружающей кнопки
  Serial.print(",");                   // добавляем еще запятую
  Serial.print(serveButton);           // выводим данные от кнопки, ставящей мячик в центр экрана
  Serial.print(",");                   // добавляем еще запятую
  Serial.println(switchPlayerButton);  // выводим данные от последней кнопки плюс символ новой строки
  delay(10);                           // делаем задержку перед тем, как отправлять следующую порцию данных
}

Код для Processing

ProcessingPong.pde

/*
Скетч к Processing для игры в Pong
 
Этот скетч принимает через последовательный порт входящие данные 
от одной или двух плат Esplora, тем самым позволяя играть в Pong.
«Эсплоры» пересылают скетчу четыре значения, разделенных запятыми 
и заканчивающихся переводом строки.

Это значения от четырех устройств ввода:
* слайдера (отвечает за ход «ракетки» вверх-вниз),
* кнопки номер 1 (перезагружает игру), 
* кнопки номер 2 (ставит мячик в центр),
* кнопки номер 3 (меняет игроков местами).

Эти значения отсылаются скетчу Processing при помощи Arduino-скетча
под названием «Игра в Pong с помощью Esplora» (EsploraPong).

Чтобы начать игру, выберите номер порта (портов) Esplora.

Создан 22 декабря 2012 Томом Иго (Tom Igoe).
 
Этот код не защищен авторским правом.
 */


import processing.serial.*;              // импортируем библиотеку Serial
import java.awt.Rectangle;               // импортируем Java-класс Rectangle

Serial[] EsploraList = new Serial[2];    // список девайсов, коммуникация с которыми будет осуществляться при помощи последовательного порта
int portCount = 0;                       // количество инициализированных последовательных портов
String portNumber = "";                  // строка, обозначающая следующий порт, который будет инициализирован

Rectangle leftPaddle, rightPaddle;       // прямоугольники, играющие роль «ракеток»

int resetButton = 1;              // значение для кнопки сброса
int serveButton = 1;              // значение для кнопки, которая ставит мячик в центр экрана
int switchSidesButton = 1;        // значение для кнопки, меняющей игроков местами 
int paddleHeight = 50;            // вертикальный размер «ракетки» 
int paddleWidth = 10;             // горизонтальный размер «ракетки»

int ballSize = 10;     // размер мячика
int xDirection = 2;    // для перемещения мячика по горизонтали: влево «-2», вправо «2»
int yDirection = 2;    // для перемещения мячика по вертикали: вверх «-2», вниз «2»
int xPos, yPos;        // вертикальная и горизонтальная позиция мячика 

boolean ballInMotion = false;  // должен ли мячик двигаться или нет 

int leftScore = 0;      // счетчик очков для игрока слева 
int rightScore = 0;     // счетчик очков для игрока справа

int fontSize = 20;      // размер шрифтов на экране
void setup() {
  size(640, 480);       // размер для экрана программы

  // Инициализируем «ракетки»:
  leftPaddle = new Rectangle(50, height/2, paddleWidth, paddleHeight);
  rightPaddle = new Rectangle(width-50, height/2, paddleWidth, paddleHeight);

  // Делаем так, чтобы у рисуемых нами фигур не было контура:
  noStroke();

  // Инициализируем мячик в центре экрана:
  xPos = width/2;
  yPos = height/2;

  // Создаем шрифт. Это будет третий шрифт, доступный системе:
  PFont myFont = createFont(PFont.list()[2], fontSize);
  textFont(myFont);
}


void draw() {
  // Очищаем экран:
  background(0);
  fill(255);

  // Если было инициализировано недостаточно портов... :
  if (portCount < 2) {
    // Требуем список последовательных портов:
    String[] portList = Serial.list();
    // Выводим на экране инструкции:
    text("Type the port number of Esplora #" + (portCount+1), 20, 20);  //  "Введите номер порта Esplora #"
    text("(or type enter to finish):", 20, 40);  //  "Или нажмите Enter, чтобы закончить"

    // Выводим на экране список портов:
    for (int i = 0; i < portList.length; i++) {
      text("port " + i + ":  " + portList[i], 20, (i+4)*20);
    }
  } 
  
  // Если портов достаточно... :
  else {
    // Рисуем первую «ракетку»:
    rect(leftPaddle.x, leftPaddle.y, leftPaddle.width, leftPaddle.height);
    // Рисуем вторую «ракетку»:
    rect(rightPaddle.x, rightPaddle.y, rightPaddle.width, rightPaddle.height);
    // Вычисляем позицию мячика и рисуем его:
    if (ballInMotion == true) {
      animateBall();
    }

    // Показываем счетчики очков:
    text(leftScore, fontSize, fontSize);
    text(rightScore, width-fontSize, fontSize);
  }
}


// Функция serialEvent будет запускаться автоматически всякий раз, 
// когда в буфере появятся байты от bufferUntil(): 
void serialEvent(Serial thisPort) {
  // Считываем значения в буфере последовательного порта:
  String inputString = thisPort.readStringUntil('\n');

  // Отрезаем от входящей строки символы возврата строки и перевода строки:
  inputString = trim(inputString);

  // Делим входящую строку по запятым и конвертируем результат в целые числа:
  int sensors[] = int(split(inputString, ','));

  // Если получили от датчиков все необходимые данные, используем их:
  if (sensors.length == 4) {
    // Если это левая Esplora:
    if (thisPort == EsploraList[0] && EsploraList[0] != null) {
      // подгоняем значения от слайдера к диапазону хода «ракеток»:
      leftPaddle.y = int(map(sensors[0], 0, 1023, 0, height - leftPaddle.height));
    }
    // если это правая Esplora:
    if (thisPort == EsploraList[1] && EsploraList[1] != null) {
      rightPaddle.y = int(map(sensors[0], 0, 1023, 0, height- rightPaddle.height));
    }
    // Если кнопка сброса поменяла свое состояние, сбрасываем очки:
    if (resetButton == 1 && sensors[1] == 0) {
      leftScore = 0;
      rightScore = 0;
      resetBall();
      ballInMotion = true;
    }
    // Сохраняем текущее состояние кнопки сброса для сравнения при следующем считывании:
    resetButton = sensors[1];

    // Если кнопка для перемещения мячика в центр экрана поменяла свое состояние,
    // то ставим мячик в центр:
    if (serveButton == 1 && sensors[2] == 0) {
      resetBall();
      ballInMotion = true;
    }
    // сохраняем текущее состояние этой кнопки для сравнения при следующем считывании:
    serveButton = sensors[2]; 
    
    // Если кнопка смены игроков поменяла свое состояние,
    // то меняем правого и левого игроков местами:
    if (switchSidesButton == 1 && sensors[3] == 0) {
      switchSides();
    }
    // Сохраняем текущее состояние этой кнопки для сравнения при следующем считывании:
    switchSidesButton = sensors[3];
  }
}

void animateBall() {
  if (leftPaddle.contains(xPos, yPos) ||    // если позиция мячика внутри левой «ракетки» 
  rightPaddle.contains(xPos, yPos)) {       // или если позиция мячика внутри правой «ракетки»
    xDirection = -xDirection;               // реверсируем направление движения мячика по оси X
  }

  // Если мячик вышел за пределы экрана слева, засчитываем очки правому игроку:
  if (xPos < 0) {
    rightScore++;
    resetBall();
  }
  // Если мячик вышел за пределы экрана справа, засчитываем очки левому игроку:
  if (xPos > width) {
    leftScore++;
    resetBall();
  }

  // Делаем так, чтобы мячик не мог выйти за пределы экрана сверху и снизу:
  if ((yPos <= 0) || (yPos >=height)) {
    // Реверсируем движение мячика по оси Y:
    yDirection = -yDirection;
  }
  // Обновляем позицию мячика:
  xPos = xPos + xDirection;
  yPos = yPos + yDirection;

  // Рисуем мячик:
  rect(xPos, yPos, ballSize, ballSize);
}

void resetBall() {
  // Ставим мячик в центр:
  xPos = width/2;
  yPos = height/2;
}



void keyReleased() {
  // Если нажат Enter, останавливаем выбор номера порта:
  if (key == ENTER) {
    if (portNumber != "" && portCount < 2) {
      choosePort(int(portNumber));
    }
    portCount++;
  }

  // Если пользователь жмет на клавиши от 0 до 9, используем это как выбор номера порта:
  if (key >= '0' && key <= '9') {
    portNumber += key;
  }
}

void choosePort(int whichPort) {
  // Получаем название порта из списка последовательных портов:
  String portName = Serial.list()[whichPort];
  // Инициализируем следующую Esplora:
  EsploraList[portCount] = new Serial(this, portName, 9600);
  // Считываем байты в буфер последовательного порта до тех пор,
  // пока не доберемся до символа перевода строки (номер 10 в таблице ASCII):
  EsploraList[portCount].bufferUntil('\n');
  // Очищаем строку с номером порта:
  portNumber = "";
}

void switchSides() {
  Serial temp = EsploraList[0];      // на время сохраняем первый элемент
  EsploraList[0] = EsploraList[1];   // перемещаем второй элемент к первому
  EsploraList[1] = temp;             // перемещаем первый элемент ко второму
}

См.также

  1. Esplora Pong

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