Cocos2d-x3.0以降のEventDispatcher制御について(その1)
はじめに
モーダルレイヤー的なことをやりたくて、色々調べてました。ソース追ったので大体わかったつもりだけど、実際の動きで検証したほうがわかりやすいかなーと。
SceneとScene上に配置したSpriteで見てみる
イメージ
数値は、ローカルZIndex
1 | -- ActorSprite --| | BattleScene | --------------------------------------- |
サンプルコード
// // BattleSecne.cpp // Cocos2dRogueLike // // Created by kyokomi on 2014/02/22. // // #include "BattleSecne.h" #include "ActorSprite.h" BattleScene::BattleScene() { } BattleScene::~BattleScene() { } Scene* BattleScene::scene() { Scene *scene = Scene::create(); BattleScene *layer = BattleScene::create(); scene->addChild(layer); return scene; } bool BattleScene::init() { if ( !Layer::init() ) { return false; } Size winSize = Director::getInstance()->getWinSize(); // TouchEvent settings auto listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = CC_CALLBACK_2(BattleScene::onTouchBegan, this); listener->onTouchMoved = CC_CALLBACK_2(BattleScene::onTouchMoved, this); listener->onTouchEnded = CC_CALLBACK_2(BattleScene::onTouchEnded, this); this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this); // Spriteを配置 ActorSprite::ActorDto actorDto = ActorSprite::createDto(); actorDto.playerId = 4; auto pActorSprite = ActorSprite::createWithActorDto(actorDto, 1); pActorSprite->setPosition(Point(winSize.width / 2, winSize.height / 2)); this->addChild(pActorSprite, 1, 1); pActorSprite->runBottomAction(); // actorにタッチイベントを設定 auto actorListener = EventListenerTouchOneByOne::create(); actorListener->onTouchBegan = [pActorSprite](Touch* touch, Event* event) -> bool { CCLOG("%s : %s(%d)", "ActorSprite", "onTouchBegan", __LINE__); return true; }; actorListener->onTouchMoved = [pActorSprite](Touch* touch, Event* event) { CCLOG("%s : %s(%d)", "ActorSprite", "onTouchMoved", __LINE__); }; actorListener->onTouchEnded = [pActorSprite](Touch* touch, Event* event) { CCLOG("%s : %s(%d)", "ActorSprite", "onTouchEnded", __LINE__); }; pActorSprite->getEventDispatcher()->addEventListenerWithSceneGraphPriority(actorListener, pActorSprite); return true; } bool BattleScene::onTouchBegan(Touch *touch, Event *unused_event) { CCLOG("%s : %s(%d)", "BattleScene", __FUNCTION__, __LINE__); return true; } void BattleScene::onTouchMoved(Touch *touch, Event *unused_event) { CCLOG("%s : %s(%d)", "BattleScene", __FUNCTION__, __LINE__); } void BattleScene::onTouchEnded(Touch *touch, Event *unused_event) { CCLOG("%s : %s(%d)", "BattleScene", __FUNCTION__, __LINE__); }
actorをタッチしたときの結果
予想通りActorのtouchEventを処理してからSceneのtouchEventが呼ばれる。
cocos2d: ActorSprite : onTouchBegan(58) cocos2d: BattleScene : onTouchBegan(75) cocos2d: ActorSprite : onTouchEnded(65) cocos2d: BattleScene : onTouchEnded(89)
onTouchBeganでfalseを返してみる
変更したコード
bool BattleScene::init() { // 〜 省略 〜 actorListener->onTouchBegan = [pActorSprite](Touch* touch, Event* event) -> bool { CCLOG("%s : %s(%d)", "ActorSprite", "onTouchBegan", __LINE__); return false; // edit falseにしてみる }; // 〜 省略 〜 }
結果
ActorのonTouchBeganでイベントが終了してonTouchEndedが呼ばれない。 SceneのonTouchEndedは、普通に呼ばれる。
cocos2d: ActorSprite : onTouchBegan(58) cocos2d: BattleScene : onTouchBegan(75) cocos2d: BattleScene : onTouchEnded(88)
ActorSpriteにSpriteをaddChildしてみる
ActorSpriteのonTouchBeganは、return falseのままです。
変更したコード
bool BattleScene::init() { // 〜 省略 〜 // add // 武器をプレイヤーにadd auto pWeaponSprite = Sprite::create("icon_set/item_768.png"); pActorSprite->addChild(pWeaponSprite); // 武器にタッチイベントを設定 auto weaponListener = EventListenerTouchOneByOne::create(); weaponListener->onTouchBegan = [pWeaponSprite](Touch* touch, Event* event) -> bool { CCLOG("%s : %s(%d)", "WeaponSprite", "onTouchBegan", __LINE__); return true; }; weaponListener->onTouchMoved = [pWeaponSprite](Touch* touch, Event* event) { CCLOG("%s : %s(%d)", "WeaponSprite", "onTouchMoved", __LINE__); }; weaponListener->onTouchEnded = [pWeaponSprite](Touch* touch, Event* event) { CCLOG("%s : %s(%d)", "WeaponSprite", "onTouchEnded", __LINE__); }; pWeaponSprite->getEventDispatcher()->addEventListenerWithSceneGraphPriority(weaponListener, pWeaponSprite); // 〜 省略 〜 }
イメージ
数値は、ローカルZIndex
0 | - WeaponSprite -| | 1 | ----- ActorSprite ----- | | BattleScene | --------------------------------------- |
結果
Weapon -> Actor -> Sceneの順番にイベントが実行される。 Actorは、onTouchBeganでfalseを返しているのでonTouchEndedが呼ばれない。
cocos2d: WeaponSprite : onTouchBegan(75) cocos2d: ActorSprite : onTouchBegan(58) cocos2d: BattleScene : onTouchBegan(92) cocos2d: WeaponSprite : onTouchEnded(82) cocos2d: BattleScene : onTouchEnded(105)
Sceneの新しくLayerを追加する(ZIndexは2)
わかりにくくなるので、一旦ActorSpriteのonTouchBeganはtrueに戻す。
変更したコード
bool BattleScene::init() { // 〜 省略 〜 actorListener->onTouchBegan = [pActorSprite](Touch* touch, Event* event) -> bool { CCLOG("%s : %s(%d)", "ActorSprite", "onTouchBegan", __LINE__); return true; // edit trueに戻す }; // 〜 省略 〜 // add // 青の半透明レイヤーを追加 auto pLayer = LayerColor::create(Color4B::BLUE); pLayer->setOpacity(128); pLayer->setContentSize(winSize); this->addChild(pLayer, 2, 2); // 武器にタッチイベントを設定 auto layerListener = EventListenerTouchOneByOne::create(); layerListener->onTouchBegan = [pLayer](Touch* touch, Event* event) -> bool { CCLOG("%s : %s(%d)", "LayerColor", "onTouchBegan", __LINE__); return true; }; layerListener->onTouchMoved = [pLayer](Touch* touch, Event* event) { CCLOG("%s : %s(%d)", "LayerColor", "onTouchMoved", __LINE__); }; layerListener->onTouchEnded = [pLayer](Touch* touch, Event* event) { CCLOG("%s : %s(%d)", "LayerColor", "onTouchEnded", __LINE__); }; pLayer->getEventDispatcher()->addEventListenerWithSceneGraphPriority(layerListener, pLayer); // 〜 省略 〜 }
イメージ
数値は、ローカルZIndex
2 | --------------------------- Layer --------- | | 0 | - WeaponSprite -| | | | 1 | ----- ActorSprite ----- | | | | BattleScene | ---------------------------------------------------- |
結果
予想どおりZIndexに従って順番に呼ばれている。
LayerColor -> Weapon -> Actor -> Scene
cocos2d: LayerColor : onTouchBegan(94) cocos2d: WeaponSprite : onTouchBegan(75) cocos2d: ActorSprite : onTouchBegan(58) cocos2d: BattleScene : onTouchBegan(111) cocos2d: LayerColor : onTouchEnded(101) cocos2d: WeaponSprite : onTouchEnded(82) cocos2d: ActorSprite : onTouchEnded(65) cocos2d: BattleScene : onTouchEnded(124)
LayerColorのonTouchBeganでfalseを返す
Layer以降touchイベントはどうなるか?
変更したコード
bool BattleScene::init() { // 〜 省略 〜 layerListener->onTouchBegan = [pLayer](Touch* touch, Event* event) -> bool { CCLOG("%s : %s(%d)", "LayerColor", "onTouchBegan", __LINE__); return false; // edit falseにしてみる }; // 〜 省略 〜 }
結果
LayerColorのonTouchEndedが呼ばれなくなる。 Sceneが呼ばれるのと同じで、Actorには影響しない。
cocos2d: LayerColor : onTouchBegan(94) cocos2d: WeaponSprite : onTouchBegan(75) cocos2d: ActorSprite : onTouchBegan(58) cocos2d: BattleScene : onTouchBegan(111) cocos2d: WeaponSprite : onTouchEnded(82) cocos2d: ActorSprite : onTouchEnded(65) cocos2d: BattleScene : onTouchEnded(124)
画面キャプチャー(最終版)
まとめ
- ローカルZIndexの順番にTouchイベントが呼ばれ、addChild元の親要素は一番最後に呼ばれる
- onTouchBeganでのreturn false;の影響は、listener内のみ有効
思った以上に長くなったので、次回に続きます。
次回は、addEventListenerWithSceneGraphPriorityではなく、addEventListenerWithFixedPriorityを利用してEventの優先順位の検証をします。
あとMenuItemを混ぜたりする予定。 まあMenuItemは、addEventListenerWithSceneGraphPriorityを使ってるので今回のSpriteとかと同じ感じになるけど。