すこしふしぎ.

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

【openFrameworks】イベントドリブンな処理をつくる【ofEvent】

こんばんは.1000chです. うちのMBAさんがこんなことを言い出しました.

f:id:ism1000ch:20141022002211p:plain

つらたん...

...さてと.

最近またoFを触る機会が多かったりします. そこで今日はひとつoFネタでいきましょう.

jQuery風に,イベントドリブンな処理をつくってみます.

はじめに

oFでクリック/タップ時の処理を実装するとき,簡単なものであればofApp::mousePressedなりofApp::touchDownなりに処理を記述すればokです. クリックされた点を中心に新しい円が生成される,とかあるあるですね.

しかし,表示する要素が増えてくるとmousePressed,touchDown内だけで処理するのは辛くなってきます. 特に画面右上にあるボタンが押されたときみたいな処理になった途端,if文の嵐でカオスになります.

というわけで,こーゆー場合は * ボタンクラス自体が押されたかどうかを判定し, * 押された場合にofAppクラスの関数を呼ぶ みたいなことができるとうれしいですね.

jQueryでいうところの

$("#my_btn").click(function(){
    console.log("clicked");
});

的な感じで,オブジェクトごとのイベントでコールバック関数を呼び出したい.という話です.

で,そーゆーのを実装する為に,oFにはイベント関連の様々なクラスがあります. ofEventのテンプレートでオリジナルのイベントを作ったり,ofEventsのクラスメソッドで既存イベントで発火される関数を利用できるようになったり,割と自由度高くできそうです.

今回は,基本的なofEventの使い方をみていきましょう. 参考は公式リファレンスです.

openFrameworks - ofEventUtils

ofEvent

ofEventを利用したイベント処理は,以下のステップで考えると良いです.

  1. 発火するイベントをつくる
  2. イベントと,行いたい処理をひもづける
  3. イベントを発火する処理を記述する

jQueryでいうならば,

# 1.イベント作成
$e = $.event("my_event")

# 2.ひもづけ
$("#my_btn").on("my_event", function(){console.log("event fired")})

# 3.発火
$("#my_btn").trigger($e)

というイメージですね.ではステップごとにみていきましょう.

1.発火したいイベントを作る

イベントはofEventで作成します. ボタンクラスの中で定義しましょう.

class MyBtn{
public:
    ofEvent<float> my_event;
}

<float>はコールバック関数に与える値の型を表します. ココにクラスを与えることもできます. mousePressedではofMouseEventArgsクラスが引数になってますね,

2.イベントと処理をひもづける

メインとなるofAppクラス中でコールバック関数を定義し,MyBtnクラスのmy_eventにバインドします. ofApp::setup関数内でひもづけておくとよいでしょう.

void ofApp::setup(){
    // my_btnはMyBtnクラスのインスタンス
    ofAddListener(my_btn.my_event, this, &ofApp::my_callback);
}

void ofApp::my_callback(float &val){
    cout << val << endl;
}

ハマリポイントとして,コールバック関数の引数を参照にすることがあります. ココを普通に値渡しにしようとするとコンパイルエラーになります...

3.イベントを発火する

イベント発火はofNotifyEvent関数を使います. ボタンクラス内の適当なところで記述しましょう.

void MyBtn::fireEvent(){
    float val = 10.0;
    ofNotifyEvent(my_event, val);
}

今回の場合だとfireEventメソッドを呼ぶとイベントが発火される形です. (先の例のようなことをするのであれば,タッチイベントをボタンクラスがlistenした上でofAppクラスにnotifyする,という形になりそうです. タッチイベントの監視に関しては次回やりますね)

ハマりポイントとして,

ofNotifyEvent(my_event, 10.0);

とすると,型が違うよ!と怒られます.10.0だとconst doubleになるんだそうな. 上記であれば動きますのでまぁそれでよいかと.

以上をまとめますとこんな形になります.

MyBtn

//MyBtn.h
#include "ofMain.h"

class MyBtn{
public:
    
    ofEvent<float> my_event;
    
    void fireEvent();
};

// MyBtn.cpp
#include "MyBtn.h"

void MyBtn::fireEvent(){
    float val = 10;
    ofNotifyEvent(my_event,val);
}

ofApp

// ofApp.h
#include "ofMain.h"
#include "ofxiOS.h"
#include "ofxiOSExtras.h"

#include "MyBtn.h"

class ofApp : public ofxiOSApp {
    
    public:
        void setup();
        //...
        void deviceOrientationChanged(int newOrientation);
    
    void callback(float &val);

    MyBtn my_btn;
};


// ofApp.cpp
#include "ofApp.h"

void ofApp::setup(){   
    ofAddListener(my_btn.my_event, this, &ofApp::callback);
}

void ofApp::callback(float &val){
    cout << val << endl;
}

void ofApp::draw(){
    my_btn.fireEvent();
}

draw()がよばれるたびにMyBtn::fireEvent()が叩かれ,my_eventが発火され,callback()が呼ばれる,という形ですね,

この例は基本だけの単純なものですが,MyBtnクラスにタイマー機能などを持たせれば,遅延してイベント発生,ということもできそうですね.

まとめ

  • ofEventの使い方の基本となるとこをまとめた
  • 知っておくと.oF応用の幅が広がりそう