すこしふしぎ.

VR/HI系院生による技術ブログ.まったりいきましょ.(友人ズとブログリレー中.さぼったら焼肉おごらなきゃいけない)

【openFrameworks】ofxTweenで連続アニメーションさせる【ofxTween】

こんばんは.1000chです.

この前ふと思いついたんですが,コード中case1,case2を簡単に切り替えたいときに

/*
#case1
/*/
#case2
//*/

としておくとcase2,

//*
#case1
/*/
#case2
//*/

としておくとcase1が適用でき,冒頭の/有る無しでtoggleできます.

よく切り替えが発生する部分なら#indef使えって話なんですけど.後々消すつもりでちょっとテストしたいなー,って時には便利...じゃないかなぁ?

さて,それとは関係なく今回もoFネタいきますよ.

ofxTweenでchainable animation

前回,ofxTweenの簡単な使い方を見ていきました. 基本は

  • setParametersして
  • updateする

って言う感じでしたね. ただ,コレだとTween.jsでいうような

target.alpha = 1;
Tween.get(target).wait(500).to({alpha:0, visible:false}, 1000).call(handleComplete);
function handleComplete() {
    //Tween complete
}

っていう感じのメソッドチェーンがやりづらいです. てことで今回はofxTweenつかったメソッドチェーンのやり方を考えていこうと思います.

ofxTweenを読む

さてじゃーどうやるのか,って話で,ofxTweenのコードを見るしかありません. とりあえずヘッダを眺めていると..

public:
    // 中略
    ofEvent<int> end_E;

なんてのがいるじゃないですか. 察するにtween終了時に発火されるイベントである予感がします.

tweenを進めているのはupdateメソッドなので,そいつをのぞいてみます.

//ofxTween.cppより抜粋. line170~
float ofxTween::update() {
    if(!completed){ // まだtweenが終わってないなら

        if(frameBased){ //フレームベースのtween(今回関係ないのでコメント割愛)
            ofxEasingArgs args;
            elapsed++;
            args.t= elapsed;
            args.d= float(duration);
            for(unsigned i=0; i<from.size(); i++){
                args.b=from[i];
                args.c=change[i];

                easingFunction->notify(this,args);
                pTarget[i] = args.res;
            }
            if(pTarget[0]==to[0])
                running=false;
            else
                running = true;
        }else{ // timestampベースのtween
                        // start時のtimestamp+delayから数えてduration分過ぎているか (*)
            if (timestamp.isElapsed(duration)){ 
                                // 各ターゲットの値を全て目的の値にセット
                for(unsigned i=0; i<from.size(); i++){
                    pTarget[i] = to[i];
                }
                running = false; //まだtweenしているかフラグoff
                completed = true; //tween終わったかフラグon.以降このループは呼ばれない
                ofNotifyEvent(end_E,id); // ofxTween::end_Eイベント発行. 引数は自身のid. (**)
            }

            else if(timestamp.elapsed()>0){ // start時からdelay分過ぎているか
                ofxEasingArgs args;
                float elapsedTime = float(timestamp.elapsed());
                args.t= elapsedTime;
                args.d= float(duration);
                for(unsigned i=0; i<from.size(); i++){
                    args.b=from[i];
                    args.c=change[i];
                                        //現在timestampでeasingした値を計算させる
                    easingFunction->notify(this,args); 
                    pTarget[i] = args.res;
                }
                running = true;
            }
        }
    }
    return getTarget(0); //0番目ターゲットの現在値をreturn
}

ちょいちょいコメントしましたが,ポイントは中盤のtween終了ブロックです.

(*)の条件判定でtween開始時からduration分過ぎている場合にこの処理に入り, 各種フラグを設定したのち,(**)でofNotifyEventしています.思った通りです.

ここのイベント発行はofEvent<int> end_Eとあるようにint型引数をわたします. でソレが何かというと,ofxTween::setParametersで指定したid,というわけです. (注:同一tween内のpositionとは関係無い)

idって何に使うのか,と思ってたのですが,要するにどのtweenが終了したかの識別に使える訳ですね (注:end_E自体は各インスタンスに固有なので,tweenAとtweenBの識別は不要です.idの使い方の例を後に紹介します).

とまぁココまでわかったことをまとめると

  • tween終了時にインスタンスが持つofxTween::end_Eイベントが発火される
  • ofxTween::setParametersで指定したidが引数として渡される

ここまでわかってしまえば,いつぞや書いた記事で紹介したように ofAddListenerでtween終了時のコールバックを設定することができそうです.

連続したtweenアニメーションをつくる

では,いよいよ連続したtweenアニメーションを作ってみましょう.

一つのofxTweenインスタンスを生成し,ofRectのx,yをそれぞれtweenさせます(複数値tweenに関しては前回記事参照).

# ofApp.h
#pragma once

#include "ofMain.h"
#include "ofxTween.h"

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 windowResized(int w, int h);
        void dragEvent(ofDragInfo dragInfo);
        void gotMessage(ofMessage msg);
    
    ofxTween my_tween;
        
    ofxEasingBounce easing_bounce;
    ofxEasingBack   easing_back;
    ofxEasingLinear easing_linear;
    ofxEasingSine   easing_sine;
    
    void tweenCallback(int &e);
};
// ofApp.cpp
#include "ofApp.h"

void ofApp::setup(){
    ofSetRectMode(OF_RECTMODE_CENTER);
    ofBackground(0);
    
    // ダミー,変化しないtween
    my_tween.setParameters(0, easing_bounce, ofxTween::easeIn, 100, 100, 1000, 0);
    my_tween.addValue(100,100);
    
    // point-1
    ofAddListener(my_tween.end_E, this, &ofApp::tweenCallback);
}

void ofApp::update(){
    my_tween.update();
}

void ofApp::draw(){
    ofRect(my_tween.getTarget(0), my_tween.getTarget(1), 50, 50);
}

void ofApp::mousePressed(int x, int y, int button){
    my_tween.setParameters(1, easing_bounce, ofxTween::easeOut, 100, 800, 1000, 0);
    my_tween.addValue(100, 100);
}

void ofApp::tweenCallback(int &e){

    // point- 2
    switch (e) {
        case 1:
            my_tween.setParameters(2, easing_back, ofxTween::easeOut, 800, 800, 1000, 500);
            my_tween.addValue(100, 600);
            break;
        case 2:
            my_tween.setParameters(3, easing_linear, ofxTween::easeIn, 800, 300, 1000, 500);
            my_tween.addValue(600, 600);
            break;
        case 3:
            my_tween.setParameters(4, easing_sine, ofxTween::easeInOut, 300, 100, 1000, 300);
            my_tween.addValue(600, 100);
            break;
        case 4:
            my_tween.setParameters(1, easing_bounce, ofxTween::easeOut, 100, 800, 1000, 500);
            my_tween.addValue(100, 100);
            break;
        default:
            break;
    }
}

順に解説します.

ポイントの1点目は,生成したtweenのend_Eイベントをlistenしている点(point-1). ofAppクラス内に,tweenが終了したら次のtweenを発動させる処理を書き,これをコールバックとして登録します.今回はtweenCallbackというヤツですね.

ポイントの2点目は,tweenCallback内部の処理です. 先述したように,end_EイベントのコールバックにはsetParametersで指定したidが渡されます. setParametersで与えるidは自由な値を使えるので,この値を利用して今どのtweenが終了したのかを識別出来ます.

そこで,わたされるid(コールバック引数的にはint &e)をswitch文でルーティングすることで,次に実行するtweenを切り替えることが出来ます. 今回は「1がおわったら2,2が終わったら3,..4が終わったら1に戻る」という形にしてみました. これにより,「最初は右にいって,つぎに下に動いて,その後左に動いて..」という処理が可能となります.

もちろん各setParametersではduration,delay,easing等個別で設定できますので, 1-2間はゆっくり変化させる,3-4間には少しdelayを入れる,等も自由自在ですね.

なお今回,プログラム起動時はid=0として動かないtweenを指定してあります. mousePressedが呼ばれた時にid=1のtweenがsetされる形ですね.

動かすとこんな感じです.

いかがでしょうか.連続アニメーションっぽくできてるのではないでしょうか.

この例のようにループさせるのであればupdate内でflagつくって管理とかもできますが, ループさせない場合,例えば「クリックされたときに一旦右に行って,次に左に行って止まる」みたいな一回動かすだけだけど,ちょっと複雑みたいな場合には使えるんじゃないかな.

まとめ

  • ofxTweenちょこっと読んでみた
  • ofxTweenつかった連続アニメーションをつくってみた