SpyLOG

Программируем БОС-игру

Для создания игры нам потребуется сюжет и часть предыдущего скетча Processing.

Фраза "сердце из груди выпрыгивает" и понимание, что прыгать естественнее мячику, дает нам игровую задачу: синхронизировать движение мяча с эталонным "прыгуном".

Для вычисления траектории перемещения мяча используем периодическую функцию - синус:

 sin(millis()*TWO_PI*r/(60*1000))  ,где r - пульс в ударах в минуту;

Деформацию мяча имитируем изменением "высоты" эллипса:

 d*(1-abs(s/3))  , где s - текущая величина синуса, а d - диаметр;

Для замыливания нижнего положения мяча добавим управление прозрачностью:

 fill(g.fillColor, 255*(1-(s+1)/2))  .

Получается вот такой метод анимации движения мяча

void anim(int x, int r) {
  float s=sin(millis()*TWO_PI*r/(60*1000));
  fill(g.fillColor, 255*(1-(s+1)/2)); 
  ellipse(x, s*(height-d)*0.4+height/2+(height+d)*0.05, d, d*(1-abs(s/3)));
}

Для управления частотой пульса эталонного мяча задействуем клавиатуру

void keyPressed() {
  switch(keyCode) {
  case UP: 
    if (refR<120) refR+=refStep; 
    break;
  case DOWN: 
    if (refR>60) refR-=refStep; 
    break;
  }
}

Рисуем скромную ленту регистрации, мячики и величины пульсов

stroke(255);  
strokeWeight(3);
int rate=0;
synchronized(knocks) {
  for (int m : knocks) {
    int x=width-pxPerSec*(currTime-m)/1000;
    line(x, 1, x, 5);
  }

  rate=rate();
}

stroke(0);
fill(102, 204, 0);
anim(150, rate);
fill(102, 204, 0);
textSize(30);
text(rate, 10, 40);

fill(204, 102, 0);
anim(250, refR);
fill(204, 102, 0);
text(refR, width-textWidth(""+refR)-10, 40);

Скетч целиком

import processing.serial.*;
import java.util.*;

final boolean SIM=true;
final int pxPerSec=50;
final int d=80;
final int refStep=2;

ArrayDeque<Integer> knocks=new ArrayDeque<Integer>();

int timeWindow=1000*10;
int refR=90;

void setup() {
  size(400, 300);
  if (SIM) {
    thread("imitator");
  } else {
    println(Serial.list());
    Serial port=new Serial(this, "COM3", 9600);
    port.bufferUntil('\n');
  }
  timeWindow=1000*width/pxPerSec;
}

void draw() {
  background(0, 0, 0);
  int currTime=millis();

  stroke(255);  
  strokeWeight(3);
  int rate=0;
  synchronized(knocks) {
    for (int m : knocks) {
      int x=width-pxPerSec*(currTime-m)/1000;
      line(x, 1, x, 5);
    }

    rate=rate();
  }

  stroke(0);
  fill(102, 204, 0);
  anim(150, rate);
  fill(102, 204, 0);
  textSize(30);
  text(rate, 10, 40);

  fill(204, 102, 0);
  anim(250, refR);
  fill(204, 102, 0);
  text(refR, width-textWidth(""+refR)-10, 40);
}

void anim(int x, int r) {
  float s=sin(millis()*TWO_PI*r/(60*1000));
  fill(g.fillColor, 255*(1-(s+1)/2)); 
  ellipse(x, s*(height-d)*0.4+height/2+(height+d)*0.05, d, d*(1-abs(s/3)));
}

void serialEvent(Serial p) { 
  p.readString();
  addKnock(millis());
} 

void addKnock(int m) {
  synchronized(knocks) {
    knocks.add(m);
    while (m-knocks.peek ()>timeWindow) knocks.remove();
  }
}

int rate() {
  if (knocks.size()>1) {
    return round(60f*1000*(knocks.size()-1)/(knocks.peekLast()-knocks.peekFirst()));
  }
  return 0;
}

void imitator() {
  while (true) {
    try {
      Thread.sleep((int)random(500, 1000));
    }
    catch (Exception e) {
    }
    addKnock(millis());
  }
}

void keyPressed() {
  switch(keyCode) {
  case UP: 
    if (refR<120) refR+=refStep; 
    break;
  case DOWN: 
    if (refR>60) refR-=refStep; 
    break;
  }
}

Вот теперь в Arduino IDE перезаливаем в плату скетч с отключенным эмулятором, меняем значение SIM на false в Processing, отключаем ноутбук от розетки или от зарядника, надеваем на ухо клипсу и играем.

Tech@Psycheya.ru

Полное или частичное использование текста, изображений и других материалов данной публикации запрещено.