前回は、FeatherのTextInputについてご紹介しました。
今回は、cocos2d-x v3で、モーダルダイアログを実装する方法についてご紹介します。
ご存知の方もいると思いますが
モーダルダイアログとは、表示中の画面にある部品の操作を不可能にし、且つ一番上位層に表示するダイアログボックスのことです。
よく、入力画面や警告画面として使用されることが多いです。
アプリを作成していると、いろいろな場面でモーダルダイアログを使用する場面がでてきますが
その都度、同じような処理をそれぞれで書いていては非常に効率が悪いです。
そのため、今回は汎用的に使用することが出来るモーダルダイアログを作成してみました。
汎用部品として作成するにあたり、共通化した箇所は以下の点です。
- ダイアログより奥にある部品のタッチ排他制御
- ダイアログ外タッチでのダイアログ自体の削除
他のNodeオブジェクトと同様の使用感を持たせるために、Layerクラスを継承しています。
作成したクラスのソースコードを以下に記載しました。
細かい説明はコメントにて記載しております。
■ModalWindow.h
#include "Layer.h" USING_NS_CC; class ModalWindow : public Layer { public: static ModalWindow *create(); virtual bool init(); //ダイアログ削除処理 void close(); //タッチ・ダウン処理 virtual bool onTouchBegan( Touch* touch, Event* event ); //タッチ・リリース処理 virtual void onTouchEnded( Touch* touch, Event* event ); protected: //ダイアログ背景用Node Node *_background; //ダイアログ外の透過カラーレイヤー LayerColor *_colorSheet; //タッチイベント登録 void _setupEventListener(); };
■ModalWindow.cpp
#include "ModalWindow.h" USING_NS_CC; ModalWindow *ModalWindow::create() { ModalWindow *ret = new (std::nothrow) ModalWindow(); if (ret && ret->init()) { ret->autorelease(); return ret; } else { CC_SAFE_DELETE(ret); return nullptr; } } bool ModalWindow::init() { if ( !Layer::init() ) { return false; } //ダイアログ外透過カラーレイヤー生成 _colorSheet = LayerColor::create( Color4B( 255, 255, 255, 64 ) ); addChild( _colorSheet, 0, 999 ); //タッチイベント設定 _setupEventListener(); return true; } void ModalWindow::_setupEventListener() { //タッチ用イベントリスナーの生成 auto touchListener = EventListenerTouchOneByOne::create(); //自身より奥にある部品へタッチイベントを流さないための設定 touchListener->setSwallowTouches( true ); //タッチダウンイベント設定 touchListener->onTouchBegan = CC_CALLBACK_2( ModalWindow::onTouchBegan, this ); //タッチリリースイベント設定 touchListener->onTouchEnded = CC_CALLBACK_2( ModalWindow::onTouchEnded, this ); //生成したイベントリスナーをディスパッチャーへ登録 _eventDispatcher->addEventListenerWithSceneGraphPriority( touchListener, this ); } bool ModalWindow::onTouchBegan( Touch *touch, Event *event ) { //ダイアログ背景設定されている場合 if ( _background != nullptr ) { //背景の矩形範囲を取得 Rect rect = _background->getBoundingBox(); //タッチ座標取得 Vec2 location = touch->getLocation(); //タッチ座標が背景矩形範囲内かの判定 if ( rect.containsPoint( location ) ) { CCLOG("on Touch Background!!"); //ダイアログ削除は、ダイアログ外タッチ(カラーレイヤーのタッチ)で行うため //背景がタッチされた場合は、リリース処理を行わないためにfalseを返却する return false; } } CCLOG("on Touch ColorSheet!!"); //「ダイアログ背景内のタッチがない == ダイアログ外タッチ」のため //trueを返却し、タッチリリース処理を行う return true; } void ModalWindow::onTouchEnded( Touch *touch, Event *event ) { //タッチリリースのタイミングで、ダイアログ削除要求 this->close(); } void ModalWindow::close() { //自身の削除 this->removeFromParentAndCleanup( true ); }
いろいろな箇所で使用が考えられるということは、その時々でレイアウトが違ったり、ダイアログ内でのチェックボックス選択、名前入力、確認用ダイアログ等、用途がまるで違う機能であることも考えるため上記のModalWindowクラスは、最低限の共通化項目のみを実装しました。
上記の実装に加え、カラーレイヤーのタッチ有効フラグを用意し、onTouchEnded関数内にあるダイアログクローズ処理をフラグによって分岐させ、setSwallowTouchの値を変更する関数を用意すると、「モーダル⇔モードレス」の切り替えを実現できます。
カラーレイヤーの色を設定する関数を用意したり、フェードやスライド等のActionを併用したダイアログ表示関数を作成し、動きのあるポップアップへ派生させるというのも面白いかもしれません。
また、ModalWindowクラスを更に継承し、各機能を別々のクラスとして用意することで拡張性やメンテナンス性の向上にも繋がり、且つ、多様なレイアウトや機能に対応することが出来るため、皆さんも是非、自分だけのオリジナルモーダルダイアログクラスを作成してみて下さい。
以上で、モーダルダイアログを実装する方法についてご紹介は終了です。
最後までご覧頂き、ありがとうございました。
※ModalWindowクラスを継承した、「はい・いいえ」ボタンを持つ簡単な確認用ダイアログクラスのサンプルソース。
■ConfirmWindow.h
#include "ModalWindow.h" USING_NS_CC; class ConfirmWindow : public ModalWindow { public: static ConfirmWindow *create(); virtual bool init(); virtual void close(); };
■ConfirmWindow.cpp
include "ConfirmWindow.h" USING_NS_CC; ConfirmWindow *ConfirmWindow::create() { ConfirmWindow *ret = new (std::nothrow) ConfirmWindow(); if (ret && ret->init()) { ret->autorelease(); return ret; } else { CC_SAFE_DELETE(ret); return nullptr; } } bool ConfirmWindow::init() { if ( !ModalWindow::init() ) { return false; } Size visibleSize = Director::getInstance()->getVisibleSize(); //ダイアログ背景の設定 _background = Sprite::create("bg.png"); _background->setPosition( Vec2( visibleSize.width * 0.5, visibleSize.height * 0.5 ) ); this->addChild( _background ); Size size = _background->getContentSize(); auto yes = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", [=](Ref*sender) { CCLOG("on Touch YES!!"); }); yes->setPosition( Vec2( size.width * 0.25, size.height * 0.2 ) ); auto no = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", [=](Ref*sender) { CCLOG("on Touch NO!!!"); }); no->setPosition( Vec2( size.width * 0.75, size.height * 0.2 ) ); auto menu = Menu::create( yes, no, NULL ); menu->setPosition( Vec2::ZERO ); _background->addChild( menu ); return true; } void ConfirmWindow::close() { ModalWindow::close(); }
弊社では全国各地の請負い(ご自宅)で作業協力頂ける、フリーランスエンジニアの方を常時探しております。
ご興味ある方は、お気軽にお問い合わせ下さい。