「がんばれない」けど「がんばりたい」

ITエンジニアの仕事のこと。AI、機械学習、ディープラーニング。地頭力。車のこと。

Oscillation|Nature of Code


■Oscillation

角度について。角度についてもvelocity, accelerationと同じ考え方が出来るという話。で、velocityはベクタだったがangleはスカラなので、少々扱いやすい。 下記は回転角angleをupdateで更新し、draw時にバトンを回転するプログラムだが、実行しみると分かるとおり、aVelocity(加速度)が増加していくので徐々に回転角も大きくなり速く回転していくように見える。

1)Angular Motion We can apply the same logic to a rotating objects.

float angle;
float aVelocity;
float aAccelaration;

//--------------------------------------------------------------
void testApp::setup(){
    
    ofSetFrameRate(60);
    ofEnableSmoothing();
    ofSetVerticalSync(true);
    ofBackground(0);
    
    aAccelaration = 0.1;
    aVelocity = 0.0;
    angle = 0.0;
}

//--------------------------------------------------------------
void testApp::update(){
    aVelocity += aAccelaration;
    angle += aVelocity;   
}

//--------------------------------------------------------------
void testApp::draw(){
    ofTranslate(ofGetWidth()/2,ofGetHeight()/2);
    ofRotate(angle);
    ofLine(-50,0,50,0);
    ofCircle(50,0,8,8);
    ofCircle(-50,0,8,8);
}

■マウスに追従するオブジェクトと、その向き

マウス位置に追従する長方形オブジェクトをコードで実現するには、applyForceに以下の順で作成したforceを与えれば出来る。

  1. マウスとオブジェクトの方向
  2. 1.を正規化したものに適当な倍数を掛ける
  3. 2.の値をapplyForceの引数として与える。

3.のapplyForceに与える時に、オブジェクトの質量を掛けたものを与えないと、マウスの位置を中心として一定の離れた位置を往復する事になる。。あとオブジェクトのvelocityはupdate()の中で更新されるが、limitを設けていないと、オブジェクトの位置がマウスの位置よりも離れたところになってしまうので、追従って感じにならない… この辺りの調整が難しいというか、経験なんかな…

あるポイントに追従する、もしくは、最終地点に到達するようにする為には、加速度が大きいと、それに伴う速度が大きくなるため、次に決まる位置(移動距離)も大きくなってしまう。ここを調整するためには、applyForceに与えるforce値と、それに反応してupdateされるオブジェクトのvelocityにlimitを設けるようなコーディングにする必要がある。

上記をふまえて、オブジェクトのdraw()内で、velocity(速度ベクトル)とマウス位置の成す角を求めてrotateしてみると以下のような効果と共にマウス追従がビジュアライズされて少し面白い。

#include 'testApp.h'


#define NUM_MOVERS 100


class Mover
{
    
public:
    
    Mover(){
    }
    
    virtual ~Mover()
    {
    }

    float mass;
    float topspeed;
    ofVec3f acceleration;
    ofVec3f velocity;
    ofVec3f location;
    
    
    void applyForces(ofVec3f force)
    {
        ofVec3f f = force / mass;
        acceleration += f;
    }
    
    void checkEdige()
    {
        
        if (location.x >= ofGetWidth())
        {
            location.x = ofGetWidth();
        }

        if (location.x <= 0.0)
        {
            location.x = 0.0;
        }
        
        if (location.y <= 0)
        {
            location.y = 0.0;
        }

        if (location.y >= ofGetHeight())
        {
            location.y = ofGetHeight();
        }
    }
    
    void limit_velocity(float limit)
    {
        if (velocity.length() > limit)
        {
            velocity.normalize();
            velocity *= limit;
        }
    }
    
    void setup()
    {
        mass = ofRandom(2,30);
        topspeed = 8;
        velocity.set(0.0, 0.0, 0.0);
        location.set(ofRandom(0, ofGetWidth() - mass), ofRandom(0, ofGetHeight() - mass));
        acceleration.set(0.0, 0.0, 0.0);
    }
    
    void update()
    {
        //速度にlimitを設ける
        velocity += acceleration;
        velocity.limit(topspeed);
        
        location += velocity;
        
        acceleration.set(0.0, 0.0, 0.0);
    }
    
    void draw()
    {
        //atan2の単位はラジアンなのでofRotateに渡す前にdegree単位に変換する
        float ang = atan2(velocity.y, velocity.x);
        float deg = RAD_TO_DEG * ang;
        
        
        ofPushMatrix();
        ofTranslate(location);
        ofRotate(deg);
        ofRect(0,0,0, 30, 10);
        ofPopMatrix();

        checkEdige();
    }
};


vector<Mover> movers;



//--------------------------------------------------------------
void testApp::setup(){
    
    ofSetFrameRate(60);
    ofEnableSmoothing();
    ofSetVerticalSync(true);
    ofBackground(0);
    
    movers.resize(NUM_MOVERS);
    for (int i = 0; i < NUM_MOVERS; i++)
    {
        Mover *m = &movers[i];
        m->setup();
    }
    
    ofSetColor(255);
    

}

//--------------------------------------------------------------
void testApp::update(){

    for (int i = 0; i < NUM_MOVERS; i++) {
        
        Mover* m = &movers[i];

        // add force
        ofVec3f mouseVector;
        mouseVector.set(mouseX, mouseY, 0);
        
        ofVec3f dir;
        dir = mouseVector - m->location;
        dir.normalize();
        dir *= 0.5;

        m->applyForces(dir*m->mass);

        m->update();
    }
}

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

    for (int i = 0; i < NUM_MOVERS; i++) {
        Mover* m = &movers[i];
        m->draw();
    }
}