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

    Введение


    В предыдущей статье был рассмотрен пример создания простейшей одномерной игры — «а-ля избегай столкновений». С такими набором способностей ШГ явно не оправдывает своего названия. По факту получается шарик-уклонист, что не звучит гордо. Так почему бы не расширить возможности «геймплея» и не добавить к игре элемент интерактивности?
    Для начала предлагается перенести действие игры на плоскость. Ввести в игру условие победы — счет, при достижении которого, появится заветное для геймера «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 «Демонстрация»



    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое