openFrameworksで加速度検知してカメハメハっぽいのを作る

##概要
この記事は、openFrameworksの外部機器やマウスなどの何かしらの入力から加速度を求め、ある一定の加速度に達した時、カメハメハみたいなやつを出したいのでどうすればいいかというコアな質問に答えるメモです。

そもそもカメハメハは例えばマウス入力で今回こんな感じになります。
Particleを出すのと、`ofSetBackgroundAuto(bool bAuto)`で背景の更新を制御することで実現する方法をメモします。

![gif](https://raw.githubusercontent.com/Hiroki11x/openFrameworksSample/master/AccelerationDetector/bin/movie.gif)

ソースコードはこちら
https://github.com/Hiroki11x/openFrameworksSample/tree/master/AccelerationDetector

##準備
今回はParticleを使っているので、そのためのクラス `Particle.hpp`,`Particle.cpp`を以下のように用意しました。

```Particle.hpp
//
//  Particles.hpp
//  mySketch
//
//  Created by HirokiNaganuma on 2016/08/06.
//
//

#ifndef Particle_hpp
#define Particle_hpp

#include "ofMain.h"

class Particle{
private:
    int alpha;
    ofVec2f mPosition;
    ofVec2f mSpeed;
    float radius;
    float startTime;
    float entireTime;
public:
    void init();
    void setPosition(const float x, const float y);
    void setPosition(const ofVec2f pos);
    void setSpeed(const float x,const float y);
    void setSpeed(const ofVec2f speed);
    void update();
    void draw();
    void draw(const ofColor &color);
    bool isEntire();
};
#endif /* Particle_hpp */

```


```Particle.cpp
//
//  Particle.cpp
//  mySketch
//
//  Created by HirokiNaganuma on 2016/08/06.
//
//

#include "Particle.hpp"

void Particle::setSpeed(const float x,const float y){
    mSpeed = ofVec2f(x,y);
}

void Particle::setSpeed(const ofVec2f speed){
    mSpeed = speed;
}

void Particle::init(){
    alpha =255;
    startTime = ofGetElapsedTimef();
    radius = ofRandom(10,30);
    entireTime =ofRandom(1,5);
}

void Particle::setPosition(const float x,const float y){
    mPosition = ofVec2f(x,y);
}

void Particle::setPosition(const ofVec2f pos){
    mPosition = pos;
}

void Particle::update(){
    mPosition += mSpeed;
    alpha-=3;
    radius*=1.02;
}

void Particle::draw(){
    ofPushStyle();
    ofSetColor(255, alpha*ofRandom(1));
    ofDrawCircle(mPosition, radius);
    ofPopStyle();
}

void Particle::draw(const ofColor &color){
    ofPushStyle();
    ofSetColor(color, alpha*ofRandom(1));
    ofDrawCircle(mPosition, radius);
    ofPopStyle();
}


bool Particle::isEntire(){
    if(ofGetElapsedTimef() - startTime > entireTime){
        return true;
    }else{
        return false;
    }
}
```


`init()`は本来コンストラクタで行ってもいいです。この`init()`でそのParticleが生成された時間を記録し、ある一定の時間後に(`entireTime`)、`bool Particle::isEntire()`でそのインスタンスを消すかを判定して、Particleが残り続けないようにしています。

次に今準備したParticleクラスの利用をします。

##その利用

```ofApp.h

#include "ofMain.h"
#include "Particle.hpp"

class ofApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();

		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void mouseEntered(int x, int y);
		void mouseExited(int x, int y);
		void windowResized(int w, int h);
		void dragEvent(ofDragInfo dragInfo);
		void gotMessage(ofMessage msg);


    ofVec2f past_pos,pos;
    vector<Particle *> particles;

    bool backgroundauto_frag;
    float start_time;
};

```

現在のある入力座標(KinectでもLeapmotionでもマウスでも良い)を`pos`とし、1フレーム前の入力座標を`past_pos`としています。
`particles`としてParticleのポインタ型のvectorも用意しておきました。
この二つはParticleを加速度をもとに生成するためのものです。

`bool backgroundauto_frag`と`float start_time`はかめはめ波っぽく軌跡を残したいのでそれを制御するためのものです。

次に、実装ファイルはこのように書いてみました。


```ofApp.cpp

//--------------------------------------------------------------
void ofApp::setup(){
    ofBackground(0);
    backgroundauto_frag = true;
    ofSetBackgroundAuto(backgroundauto_frag);
}

//--------------------------------------------------------------
void ofApp::update(){
    pos = ofVec2f(mouseX,mouseY);

    if(ofDist(past_pos.x, past_pos.y, pos.x, pos.y)>50){

        start_time = ofGetElapsedTimef();
        backgroundauto_frag=false;
        ofSetBackgroundAuto(backgroundauto_frag);

        for(int i = 0;i<5;i++){
            particles.push_back(new Particle());
            particles.back()->init();
            particles.back()->setPosition(pos);
            particles.back()->setSpeed((pos-past_pos)/2);
        }
    }

    past_pos = pos;

    //Particleの自然消滅を促す
    for(int i = 0; i<particles.size();i++){
        particles.at(i)->update();
        if(particles.at(i)->isEntire()){
            particles.erase(particles.begin() + i);
        }
    }

    if(!backgroundauto_frag && ofGetElapsedTimef()-start_time > 1){
        ofBackground(0);
        backgroundauto_frag=true;
        ofSetBackgroundAuto(backgroundauto_frag);
    }

}

//--------------------------------------------------------------
void ofApp::draw(){

    ofDrawBitmapStringHighlight("Num of Particle: "+ofToString(particles.size()), 20,20);
    ofSetColor(255);
    for(int i = 0; i<particles.size();i++){
        particles.at(i)->draw();
    }
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    if(key == OF_KEY_RETURN){
        for(int i = 0;i<5;i++){
            particles.push_back(new Particle());
            particles.back()->init();
            particles.back()->setPosition(pos);
            particles.back()->setSpeed((pos-past_pos)/2);
        }
    }
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}


```

`setup()`で`ofSetBackgroundAuto()`関係の各種の初期化を行い、
`update()`で、毎回入力座標を`pos`に入れて、前回のフレームの座標と比較することで、どれだけ座標変化があったか(加速度)を計算し、Particleの生成、`ofSetBackgroundAuto()`の制御をおこなっています。

また、自然と一定時間でParticleが消滅するように、`isEntire()`のチェックも`update()`で行っています。

```cpp
if(!backgroundauto_frag && ofGetElapsedTimef()-start_time > 1){
        ofBackground(0);
        backgroundauto_frag=true;
        ofSetBackgroundAuto(backgroundauto_frag);
}
```

の部分は、今backgroundauto_fragがfalseで背景更新を行わないようにしている(上から重ね放題の状態)時で、Particleの生成から1秒以上経った時、`ofSetBackgroundAuto()`の制御をしなおし、通常の背景更新を行う状態に戻すチェックをしています。

`draw()`は、Particleがあればそれを描画しているだけです。
`keyPressed(int key)`は加速度検知しなくてもParticleを生成するようにしています。