前回は、Pepperでファイルのダウンロードを行いました。
今回は、cocos2d-xのタッチイベントについてのお話です。
■環境
・cocos2d-x ver3.3
TableViewとMenuの併用については、以下の構成を想定します。
この時、タッチ開始箇所がMenu上の場合、TableViewのスクロールを行うことが出来ません。
これは、タッチイベントの「優先度」と「制御設定」によって起こる問題で
cocos2d-xでのタッチイベントの「優先度(Priority)」は、下記のように決まります。
- 設定priority値が低いほうが優先
- priority値が同じ場合、重なり順(z座標)が手前にあるほうが優先
- Nodeが親子関係にある場合、子Nodeが優先
今回のケースで、関係しているのは3番です。
図1を見ても分かるように、MenuはTableViewがもつcellの子Nodeとなるため
タッチイベントが実行される順序は次のようになります。
Menuタッチイベント→TableViewタッチイベント
この流れだけを見ると、タッチ開始箇所がMenuだった場合でも
TableViewのスクロールが行われるように見えますが
実際にはスクロールはせず、TableViewは静止したままです。
理由としては、リスナーの通知制御設定です。
cocos2d-xのタッチイベントリスナーにはsetSwallowTouchesという関数が用意されており
指定したNodeにのみイベントを発生させる場合はtrue
下のNodeにもイベントを発生させる場合はfalse
を設定することで、タッチイベントの通知を制御することが出来ます。
Menuクラスでは、内部でsetSwallowTouches関数にtrueを設定しており
タッチイベントがTableViewクラスに登録されているリスナーまで通知されないため
下記のように設定することで、タッチ開始箇所がMenuの場合でもスクロールを可能にすることができます。
Menu.cpp
bool Menu::initWithArray(const Vector<MenuItem*>& arrayOfItems) { // 〜省略〜 auto touchListener = EventListenerTouchOneByOne::create(); // 変更前 //touchListener->setSwallowTouches(true); // 変更後 // trueをfalseに変更 touchListener->setSwallowTouches(false); touchListener->onTouchBegan = CC_CALLBACK_2(Menu::onTouchBegan, this); touchListener->onTouchMoved = CC_CALLBACK_2(Menu::onTouchMoved, this); touchListener->onTouchEnded = CC_CALLBACK_2(Menu::onTouchEnded, this); touchListener->onTouchCancelled = CC_CALLBACK_2(Menu::onTouchCancelled, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this); // 〜省略〜 }
上記設定で、Menu上スクロールには対応できましたが、この状態では不完全です。
スクロール後、タッチエンド時にMenu上に指があった場合、Menuに登録したタッチイベントが実行されてしまいます。
一般的な挙動としては、スクロール後の一発目のエンドイベントではMenuやButtonのタッチイベントは弾きたいところです。
この挙動を実現するためには、TableViewを拡張します。
TableView(ScrollView)クラスでは、ドラッグフラグ用の変数が_touchMovedという名前で宣言されていますが
protectedのため、getter関数を追加します。
CCTableView.h
class CC_EX_DLL TableView : public ScrollView, public ScrollViewDelegate { public: // 〜省略〜 //追加 bool isMoved(); }
CCTableView.cpp
//追加 bool TableView::isMoved() { return _touchMoved; }
_touchMoved用のgetter関数を追加できましたら、あとはMenuタッチイベント処理のなかで、ドラッグ判定を入れるだけとなります。
TableView *tableView = dynamic_cast<TableView*>( this->getChildByTag( 0 ) ); if ( tableView->isMoved() ) { CCLOG( "Moved return" ); return; }
以上で、TableViewとMenu併用時のイベント制御についての説明は終了となります。
今回の方法はフレームワークのソースコードを直接修正するオープンソースならではのやり方ですが
バージョンアップの際、その都度修正しなおさなければいけないため
TableViewやMenuを継承した新規クラスを作成し、それらのクラスで今回の設定するのがよいでしょう
最後まで御覧いただきありがとうございました。
<TableView・Menu併用サンプルソース>
AppMain.h
#include "cocos2d.h" #include "cocos-ext.h" USING_NS_CC; USING_NS_CC_EXT; class AppMain : public cocos2d::Layer, public TableViewDelegate, public TableViewDataSource { public: static cocos2d::Scene* createScene(); virtual bool init(); //============================= // TableView関連 //============================= virtual void scrollViewDidScroll(ScrollView* view){}; virtual void scrollViewDidZoom(ScrollView* view){}; virtual Size cellSizeForTable(TableView* table); virtual TableViewCell* tableCellAtIndex(TableView* table,ssize_t idx); virtual ssize_t numberOfCellsInTableView(TableView* table); virtual void tableCellTouched(TableView* table,TableViewCell* cell); /** * メニュータッチイベント */ void onTouchMenu(Ref*sender); CREATE_FUNC(AppMain); };
AppMain.cpp
bool AppMain::init() { if ( !Layer::init() ) { return false; } // TableViewを作成 auto tableView = TableView::create( this, Director::getInstance()->getVisibleSize() ); tableView->setTag( 0 ); tableView->setDirection( TableView::Direction::VERTICAL ); tableView->setVerticalFillOrder( TableView::VerticalFillOrder::TOP_DOWN ); tableView->setDelegate( this ); addChild( tableView ); tableView->reloadData(); return true; } Size AppMain::cellSizeForTable(TableView *table) { Size visibleSize = Director::getInstance()->getVisibleSize(); return Size( visibleSize.width, visibleSize.height*.1 ); } TableViewCell* AppMain::tableCellAtIndex(TableView *table, ssize_t idx) { TableViewCell *cell = table->dequeueCell(); cell = new TableViewCell(); cell->autorelease(); //メニュー生成 auto *defaultSkin = Sprite::create( "button.png" ); auto *downSkin = Sprite::create( "button.png" ); downSkin->setColor( Color3B( 120, 120, 120 ) ); auto *menuItem = MenuItemSprite::create( defaultSkin, downSkin, CC_CALLBACK_1( AppMain::onTouchMenu, this ) ); Menu* menu = Menu::create( menuItem, NULL ); cell->addChild( menu ); return cell; } void AppMain::onTouchMenu(Ref*sender) { TableView *tableView = dynamic_cast<TableView*>( this->getChildByTag( 0 ) ); if ( tableView->isMoved() ) { CCLOG( "Moved return" ); return; } CCLOG( "button Touch" ); } ssize_t AppMain::numberOfCellsInTableView(TableView *table) { return 20; } void AppMain::tableCellTouched(TableView* table, TableViewCell* cell) { CCLOG( "cell touch" ); }
弊社では全国各地の請負い(ご自宅)で作業協力頂ける、フリーランスエンジニアの方を常時探しております。
ご興味ある方は、お気軽にお問い合わせ下さい。