cocos2dx3.1のマルチタッチをやってみた

ChainZ(クリエイター)
いろいろやってます。

音ゲーなどマルチタッチを使うことが多いと思うので、cocos2dxのマルチタッチについてちょっと研究してみた。使うバージョンは最新の3.1です。(2014.05.30)

cocos2dxでマルチタッチを使う

cocos2dxはデフォルトにマルチタッチがサッポートされていません。マルチタッチを使うには、ios/AppController.mmを編集しないといけません。

// Init the CCEAGLView
CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds]
                                 pixelFormat: kEAGLColorFormatRGBA8
                                 depthFormat: GL_DEPTH24_STENCIL8_OES
                          preserveBackbuffer: NO
                                  sharegroup: nil
                               multiSampling: NO
                             numberOfSamples: 0];

の直後に

[eaglView setMultipleTouchEnabled:YES];

を入れます。つまりGLViewにマルチタッチを有効にするというこどです。これで、cocos2dxがマルチタッチを使えるようになります。

リスナーを設置

HelloWorldScene.cppを開いて、init()の中に

auto listener = EventListenerTouchAllAtOnce::create();
// リスナーを作成

listener->setEnabled(true);
// 有効にする

ここ注意してほしいのは、EventListenerTouchAllAtOnceを使うことです。同じメソードの中に下記のコードを適当なところに書きます。

_eventDispatcher->addEventListenerWithFixedPriority(listener, 9999);
// このシーンにリスナーを設置。

その後、タッチを受け取るメソードを宣言する

std::function<void(const std::vector<Touch*>&, Event*)> onTouchesBegan;
std::function<void(const std::vector<Touch*>&, Event*)> onTouchesMoved;
std::function<void(const std::vector<Touch*>&, Event*)> onTouchesEnded;

よく見ればわかると思いますが、受け取った全てのTouchデータがvectorに格納されます。それをうまく利用して、タッチジェスチャーのロジカルを作ります。

参考になるかどうかはわかりませんが、実験的に基本のタッチジェスチャー書いてみた。(もっといいアルゴリズムがあると思いますが。。まだ考え中です。)

listener->onTouchesBegan = [this](std::vector<Touch*> touches, Event *event){
    //タッチが2カ所を検知されたら
    if (touches.size() == 2){
        _touch_type = TOUCH_2_FINGER_TAP;
    }

    //タッチが1カ所しかなかったら
    if (touches.size() == 1){

        // ここは少しハックになるが、タッチ1カ所しか検出されなくても、
        // 実際ユーザーが指二本でタッチしようとしても、微妙なずれがあるので、
        // 許容範囲内なら二本指タッチとします
        if (_touchTime != 0.0f &&
            _timer2 - _touchTime < 0.05f) {
            _touch_type = TOUCH_2_FINGER_TAP;
            return;
        }

        _touch_type = TOUCH_TAP;
    }
    //タッチ数を保存する、これはダブルタップ判定に使う。
    _num_touches += 1;
    _touchTime = _timer2;
};

listener->onTouchesMoved = [this](std::vector<Touch*> touches, Event *event){
    _swipe_type = 0;
    //移動したら、スワイプとします。
    if (touches.front()->getLocation().x - touches.front()->getPreviousLocation().x > 10.0f)
    {
        _swipe_type = 1;
    }
};

listener->onTouchesEnded = [this](std::vector<Touch*> touches, Event *event){
    //CCLOG("Touch Num When Touch End: %lu", touches.size());
    if (_touch_type == TOUCH_2_FINGER_TAP) {
        if (_swipe_type != 1) {
            this->unschedule(schedule_selector(HelloWorld::TwoFingerTap));
            // タイマーをクリア
            if (_num_touches >= 2) {
                CCLOG("2 finger double touch");

                //タッチ数とタッチ時間をリセット。
                _num_touches = 0;
                _touchTime = 0;

            } else {
                this->scheduleOnce(schedule_selector(HelloWorld::TwoFingerTap), .2f);
                //タップした場合は、0.2秒の遅延を設定。もし次のタップが0.2秒の間に発生したら、
                //このタイマーが消される
            }
        } else {
            CCLOG("2 finger swipe");
        }
    }

    if (_touch_type == TOUCH_TAP) {
        if (_swipe_type != 1) {
            this->unschedule(schedule_selector(HelloWorld::Tap));
            if (_num_touches >= 2) {
                CCLOG("double tap");
                _num_touches = 0;
                _touchTime = 0;
            } else {
                this->scheduleOnce(schedule_selector(HelloWorld::Tap), .2f);
            }
        } else {
            CCLOG("swipe");
        }
    }
    _swipe_type = 0;
    _timer = 0;
};

細かいとこは載ってないので、GitHubへどうぞ:https://github.com/chainzhang/touchtest

感想

マルチタッチは楽しいと思うけど、ユーザーの挙動は全て把握できないので、乱暴なユーザーの操作を想定してアルゴリズムを考えるとパニックになるぐらい悩む。