Как стать автором
Обновить

Разработка игр на Processing с управлением через плату Arduino Uno, часть 2

Время на прочтение5 мин
Количество просмотров9.9K

Введение


В предыдущей статье был рассмотрен пример создания простейшей одномерной игры — «а-ля избегай столкновений». С такими набором способностей ШГ явно не оправдывает своего названия. По факту получается шарик-уклонист, что не звучит гордо. Так почему бы не расширить возможности «геймплея» и не добавить к игре элемент интерактивности?
Для начала предлагается перенести действие игры на плоскость. Ввести в игру условие победы — счет, при достижении которого, появится заветное для геймера «You won». Выдать ШГ пацифистское орудие для более быстрого набора очков и взаимодействия с ШП, по возможности изменить визуальное оформление проекта на более привлекательное.
Собственно, решением этих задач я занялся в рамках своего очередного проекта «выходного дня». Что получилось, читайте далее.

Шаг 1 «Задание на разработку»


-ШГ должен иметь 2 степени свободы.
-ШГ имеет возможность быстрого набора очков и замедления скорости движения ШП по средству попадания по последнему специального луча.
-Реализовать новые правила — игра ведется до 1000 очков. За простой уход от столкновения начисляется 1 очко. За удержание луча на шарике +1 очко за каждые 50*10^-3 c (в программе на Arduino установлена задержка 50 мс)

Шаг 2 «Описание подключения аналогового стика»


Стик имеет 5 пинов для подключения: пин VCC подключаетя к питанию +5V, GND — к земле на плате Arduino Uno, X — к аналоговому входу А0, Y — к А1, D — к цифровому входу D2.

Шаг 3 «Передача нескольких координат по Serial соединению»


Данные о положении стика передаются вместе, чтобы максимально быстро обеспечить обработку их Processingом без задержки по времени. Для описания текущего положения аналогового стика достаточно 7 бит XXXYYYB — 3 для координаты Х, еще 3 — для координаты Y, 1 бит — для контроля нажатия кнопки на стике. Код прикладываю ниже:
Код для платы Arduino Uno
#include <stdio.h>
int xPin = A0, yPin = A1, buttonPin = 2;
int xPosition = 0, yPosition = 0, buttonState = 0;
char strbuf[20]; //
void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600); 
  pinMode(xPin, INPUT);
  pinMode(yPin, INPUT);
  //activate pull-up resistor on the push-button pin
  pinMode(buttonPin, INPUT_PULLUP);   }
void loop() {
  xPosition = map(analogRead(xPin),0,1023,0,639);
  yPosition = map(analogRead(yPin),0,1023,0,639);
  buttonState = digitalRead(buttonPin);
  sprintf(strbuf,"%03d%03d%1d",xPosition,yPosition,buttonState);
  Serial.println(strbuf); 
  delay(50); }// add some delay between reads

После подключения стика и прошивки Arduino Uno, в мониторе Serial-порта можно увидеть примерно следующее.

Это и есть вектор состояния стика в определенном нами формате. Кстати, иногда «проскакивают» артефакты — вырожденные векторы из 2-х 3-х бит. Крайне неприятное явление. Из-за них игра на Processing «крашится». Возможно, это объясняется дефектом на моей плате стика, возможно — нет. В любом случае, бороться с артефактами на уровне Arduino я не стал. Для этого предусмотрено специальное условие в коде игры на Processing (см. Шаг 4). Его задача состоит в проверке целостности передаваемых данных по Serial соединению.

Шаг 4 «Кодирование игры»


Подробно комментировать код не буду. Я пытался сделать его понятным. Оценить получилось или нет можно под спойлером. На всякий случай привожу таблицу переменных:
radiusOfHero-радиус ШГ;
radiusOfEnemy — радиус ШП;
radiusOfBullet — радиус пули; выпускаемой ШГ;
Counter — счетчик очков;
speedOfEnemy — коэффициент, прямо пропорционально влияет на скорость падения ШП;
DeltaPositionOfHeroX — приращение положения ШГ по оси X, получаемое от стика;
positionOfHeroX1- конечная координата перемещения ШГ по оси Х (на конец такта считывания);
positionOfHeroX0 — начальная координата по оси X(на начало такта);
DeltapositionOfHeroY, positionOfHeroY1, positionOfHeroY0 — то же по оси Y;
strbuf — строка, в которую считываются показания состояния аналогово стика, передаваемые с Arduino Uno.
Код игры на Processing
import processing.serial.*;//I/O library
Serial port;
PShape bot;
PFont font;
PImage img;
int radiusOfHero=100, radiusOfEnemy, radiusOfBullet=5, Counter=0, Fire;
float speedOfEnemy=1, DeltaPositionOfHeroX, positionOfHeroX1, positionOfHeroX0=640.0,
DeltapositionOfHeroY, positionOfHeroY1, positionOfHeroY0=640.0,
positionOfEnemyY = 0.0 ,positionOfEnemyX=0.0, positionOfBulletX=0.0,positionOfBulletY=0.0;
String strbuf="3223220";
void setup()
{
  size(640, 640);
  port = new Serial(this, "COM4", 9600);
  port.bufferUntil('\n');
  bot = loadShape("2.svg");
  font = loadFont("AgencyFB-Bold-200.vlw");
  img = loadImage("img.png");  // Load the image into the program  
  textFont(font,200);
}
void draw() {
    background(0);
    image(img, 0, 0);
    fill(255);
    text(Counter, 400,170);
//==========definiton of hero==========
fill(0, 200, 102);
positionOfHeroX1=positionOfHeroX0+(0.05*(DeltaPositionOfHeroX-width/2));
if (positionOfHeroX1<0){positionOfHeroX1=0.0;}
if (positionOfHeroX1>width){positionOfHeroX1=width;}
positionOfHeroY1=positionOfHeroY0+(0.05*(DeltapositionOfHeroY-height/2));
if (positionOfHeroY1<0){positionOfHeroY1=0.0;}
if (positionOfHeroY1>height){positionOfHeroY1=height;}
ellipse(positionOfHeroX1, positionOfHeroY1, radiusOfHero, radiusOfHero);
positionOfHeroX0=positionOfHeroX1;
positionOfHeroY0=positionOfHeroY1;
fill(244);
       positionOfBulletY= positionOfHeroY1-radiusOfHero/2;
      if (Fire==0){
            for(int i = 0; i < (positionOfHeroY1); i++){
                      positionOfBulletX = positionOfHeroX1; 
                      positionOfBulletY= positionOfBulletY-height/100;
                      ellipse(positionOfBulletX, positionOfBulletY, radiusOfBullet, radiusOfBullet);           }
                       }
//===============definition of enemy===============
fill(255,0,0);
radiusOfEnemy=round(random(60));{
   for(int i = 0; i < height; i++) 
     positionOfEnemyY=positionOfEnemyY+0.02*speedOfEnemy;
     ellipse(positionOfEnemyX, positionOfEnemyY, radiusOfEnemy*2, radiusOfEnemy*2); }
        if (positionOfEnemyY>height) {
        positionOfEnemyY=0.0;
        positionOfEnemyX = round(random(width)); 
        Counter++;}
//==========definition of counter==========        
        if (Counter>1000){
        text("YOU WON!", 50,height/2);
}
//==========clash==========
if (abs(positionOfHeroX1-positionOfEnemyX) < (radiusOfHero+radiusOfEnemy)/2 & 
   (abs(positionOfHeroY1-positionOfEnemyY) < (radiusOfHero+radiusOfEnemy)/2)){
background(255,0,0);
shape(bot, positionOfHeroX1-radiusOfHero/2,positionOfHeroY1-radiusOfHero, 100, 100);  
Counter=-1;
fill(255);
textFont(font,150);
text("TURN AWAY!", 0,height/2);}
//==========Checking of target hit==========
    if (((abs(positionOfBulletX-positionOfEnemyX) < (radiusOfBullet+radiusOfEnemy)/2))& (Fire==0))
      {speedOfEnemy=0.05;// decreasing of enemy speed
      Counter++;}
    else speedOfEnemy=0.2;}
   
void serialEvent (Serial port) {
  if(port.available()>0){
   strbuf=port.readStringUntil('\n'); 
if (strbuf.length()<7) {//condition to prevent artefacts
strbuf="3223220";
  }
DeltaPositionOfHeroX=float(strbuf.substring(0, 3)); 
DeltapositionOfHeroY=float(strbuf.substring(3, 6)); 
Fire=int(strbuf.substring(6, 7));
}
}

Шаг 5 «Демонстрация»



Теги:
Хабы:
+4
Комментарии0

Публикации

Изменить настройки темы

Истории

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн