@TangWill
2019-06-15T18:38:15.000000Z
字数 19673
阅读 753
DataStruct
在游戏中,我们常常需要实现弹出一个模态对话框,比如说游戏暂停,退出提示对话框等
对话框特点如下:
1.可定制的,比如说背景图,标题,文本,按钮等,根据需要添加和设置
2.需要屏蔽对话框下层的触摸
3.为了友好的效果显示,把不可触摸的部分变为灰色
Dialog.h
#ifndef __DIALOG_H__
#define __DIALOG_H__
#include "cocos2d.h"
#include "ui/CocosGUI.h"
#include "Theme.h"
#include "Network.h"
#include "User.h"
#define SCREEN_WIDTH 1200
#define SCREEN_HEIGHT 900
USING_NS_CC;
using namespace cocos2d::ui;
using std::string;
class Dialog final :
public LayerColor
{
public:
Dialog() : menu(nullptr), labelMenu(nullptr), backGround(nullptr), title(nullptr), contentText(nullptr) {}
~Dialog();
bool init() override;
CREATE_FUNC(Dialog);
static Dialog* create(const string& background, const Size& size);
// touch事件监听
bool onTouchBegan(Touch* touch, Event* event) override { return true; }
void onTouchMoved(Touch* touch, Event* event) override {}
void onTouchEnded(Touch* touch, Event* event) override {}
void onEnter() override;
void onExit() override;
// 添加标题
void setTitle(const string& title, int fontSize = 20);
// 添加文本内容
void setContentText(const string& text, const int fontSize, const int padding, const int paddingTop);
// 添加button、label或listView
bool addButton(MenuItem* menuItem) const;
bool addLabel(MenuItem* menuItem) const;
void addListView(bool dialogType, bool inMenu, bool classical, bool noLeadingIn = true);
private:
Theme* theme = Theme::getInstance();
// 文字内容两边和顶部的空白区
int contentPadding = 0;
int contentPaddingTop = 0;
Size dialogContentSize;
// 初始化scene中心位置,方便使用
const Point center = Point(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
int position = 11; // 滚动条位置
bool rank = false; // 对话框类型
bool rankType = false; // 排行榜类型
bool isInMenu = true; // 排行榜是否在menuScene
bool isClassical = true; // 游戏模式
bool changed = false; // 判断排行榜是否改变
ListView* listView = nullptr;
Label* nameLabel = nullptr;
Label* rankLabel = nullptr;
Label* scoreLabel = nullptr;
Label* content = nullptr;
Layout* layout = nullptr;
EventListenerMouse* listenerMouse = nullptr;
// 排行榜
vector<pair<string, int>> classicalRank;
vector<pair<string, int>> plusRank;
void backgroundFinish(); // 初始化对话框内容及布局
void initRankDialog(); // 初始化排行榜对话框
void initNormalDialog(); // 初始化普通对话框
void getRankByType(bool type); // 获取排行榜内容
// set and get
CC_SYNTHESIZE_RETAIN(Menu*, menu, MenuButton);
CC_SYNTHESIZE_RETAIN(Menu*, labelMenu, MenuLabel);
CC_SYNTHESIZE_RETAIN(ui::Scale9Sprite*, backGround, BackGround);
CC_SYNTHESIZE_RETAIN(Label*, title, LabelTitle);
CC_SYNTHESIZE_RETAIN(Label*, contentText, LabelContentText);
};
#endif
Dialog.cpp
#include "Dialog.h"
Dialog::~Dialog()
{
CC_SAFE_RELEASE(menu);
CC_SAFE_RELEASE(labelMenu);
CC_SAFE_RELEASE(contentText);
CC_SAFE_RELEASE(title);
}
bool Dialog::init()
{
if (!LayerColor::init()) return false;
// 初始化需要的 Menu button菜单
auto menu = Menu::create();
menu->setPosition(Size::ZERO);
setMenuButton(menu);
// label菜单
auto labelMenu = Menu::create();
labelMenu->setPosition(Size::ZERO);
setMenuLabel(labelMenu);
// 添加layer的事件监听事件
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(Dialog::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(Dialog::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(Dialog::onTouchEnded, this);
auto dispatcher = Director::getInstance()->getEventDispatcher();
dispatcher->addEventListenerWithSceneGraphPriority(listener, this);
// 设置背景版透明,达到对话框下层变灰暗效果
setColor(Color3B(0, 0, 0));
setOpacity(128);
return true;
}
Dialog* Dialog::create(const string& background, const Size& size)
{
auto layer = Dialog::create();
layer->setBackGround(Scale9Sprite::create(background));
layer->dialogContentSize = size;
return layer;
}
#pragma region Override
void Dialog::onEnter()
{
LayerColor::onEnter();
Layer::onEnter();
_eventDispatcher->pauseEventListenersForTarget(this->getParent(), true); // 阻止事件向下传递
//添加背景图片
auto background = getBackGround();
background->setContentSize(dialogContentSize);
background->setPosition(Point(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2));
this->addChild(background, 0, 0);
// 弹出效果
Action* dialog = Sequence::create(
ScaleTo::create(0.0f, 0.0f),
ScaleTo::create(0.3f, 1.0f),
CallFunc::create(CC_CALLBACK_0(Dialog::backgroundFinish, this)),
nullptr
);
background->runAction(dialog);
}
void Dialog::onExit()
{
Layer::onExit();
LayerColor::onExit();
_eventDispatcher->resumeEventListenersForTarget(this->getParent(), true); // 还原事件监听
}
#pragma endregion
#pragma region Add Component
void Dialog::setTitle(const string& title, const int fontSize/*=20*/)
{
const auto label = Label::createWithTTF(title, theme->semiBoldFont, fontSize);
setLabelTitle(label);
}
void Dialog::setContentText(const string& text, const int fontSize, const int padding, const int paddingTop)
{
const auto label = Label::createWithTTF(text, theme->semiBoldFont, fontSize);
setLabelContentText(label);
contentPadding = padding;
contentPaddingTop = paddingTop;
}
bool Dialog::addButton(MenuItem* menuItem) const
{
// 添加精灵菜单项
const auto center = Point(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
menuItem->setPosition(center);
getMenuButton()->addChild(menuItem);
return true;
}
bool Dialog::addLabel(MenuItem* menuItem) const
{
// 添加标签菜单项
const auto center = Point(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
menuItem->setPosition(center);
getMenuLabel()->addChild(menuItem);
return true;
}
void Dialog::addListView(bool dialogType, bool inMenu, bool classical, bool noLeadingIn)
{
// 预加载排行榜,解决卡顿
if (noLeadingIn && User::getInstance()->isConnected())
{
classicalRank = Network::getInstance()->getRank(true);
plusRank = Network::getInstance()->getRank(false);
}
// 设置窗口类型为排行榜窗口,并判断排行榜显示游戏模式
rank = dialogType;
isInMenu = inMenu;
isClassical = classical;
}
#pragma endregion
void Dialog::backgroundFinish()
{
// 判断窗口类型为排行榜窗口或普通窗口,true为排行榜窗口
if (rank)
{
initRankDialog();
}
else
{
initNormalDialog();
}
rank = false; // 默认为普通对话框
}
void Dialog::initNormalDialog()
{
// 显示对话框标题
if (getLabelTitle())
{
getLabelTitle()->setPosition(
center + Vec2(0, dialogContentSize.height / 2 - title->getContentSize().height / 2 - 15));
this->addChild(getLabelTitle());
}
// 显示文本内容
if (getLabelContentText())
{
auto content = getLabelContentText();
content->setLineBreakWithoutSpace(true);
content->setMaxLineWidth(dialogContentSize.width - 2 * contentPadding);
content->setPosition(Vec2(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2));
content->setHorizontalAlignment(TextHAlignment::LEFT);
this->addChild(content);
}
// 添加label或button,并设置其位置
//label
this->addChild(getMenuLabel());
auto adaptHeight = 0;
if (title) adaptHeight = title->getContentSize().height;
const auto labelHeight = (dialogContentSize.height - adaptHeight -40) / (getMenuLabel()
->getChildrenCount() + 2);
// 布局labelMenuItem
auto labels = getMenuLabel()->getChildren();
auto i = 0;
for (auto menuItem : labels)
{
auto node = dynamic_cast<Node*>(menuItem);
node->setPosition(Point(SCREEN_WIDTH / 2,
(SCREEN_HEIGHT + dialogContentSize.height / -adaptHeight + 50) /
2 - dialogContentSize.height / 2.5 + labelHeight * (getMenuLabel()->
getChildrenCount() - i)));
i++;
}
// button
this->addChild(getMenuButton());
const auto buttonWidth = dialogContentSize.width / (getMenuButton()->getChildrenCount() + 1);
auto nodes = getMenuButton()->getChildren();
i = 0;
for (auto menuItem : nodes)
{
auto node = dynamic_cast<Node*>(menuItem);
node->setPosition(Point(SCREEN_WIDTH / 2 - dialogContentSize.width / 2 + buttonWidth * (i + 1),
SCREEN_HEIGHT / 2 - dialogContentSize.height / 2.5));
i++;
}
}
void Dialog::initRankDialog()
{
// 显示对话框标题
if (getLabelTitle())
{
getLabelTitle()->setPosition(center + Vec2(0, dialogContentSize.height / 2 - 75.0f));
this->addChild(getLabelTitle());
}
this->addChild(getMenuButton());
// 添加button,设置布局
const auto buttonWidth = dialogContentSize.width / (getMenuButton()->getChildrenCount() + 1);
auto nodes = getMenuButton()->getChildren();
auto i = 0;
for (auto menuItem : nodes)
{
auto node = dynamic_cast<Node*>(menuItem);
node->setPosition(Point(SCREEN_WIDTH / 2 - dialogContentSize.width / 2 + buttonWidth * (i + 1),
SCREEN_HEIGHT / 2 - dialogContentSize.height / 2.5));
i++;
}
listView = ListView::create();
listView->setDirection(ScrollView::Direction::VERTICAL);
listView->setTouchEnabled(true);
listView->setBounceEnabled(true);
listView->setBackGroundImageScale9Enabled(true);
listView->setAnchorPoint(Point(0.5f, 0.5f));
listView->setContentSize(Size(500, 400));
listView->setPosition(Point(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2));
listView->setScrollBarAutoHideTime(0);
listView->setScrollBarColor(Color3B::WHITE);
this->addChild(listView);
//添加鼠标事件侦听
listenerMouse = EventListenerMouse::create();
listenerMouse->setEnabled(true);
listenerMouse->onMouseScroll = [&](EventMouse* event)
{
const auto y = event->getScrollY(); //滚轮上滑y值大于0,下滑y值小于0
if (y > 0)
{
if (position % 11 < 10)
position++;
listView->scrollToPercentVertical(10 * (position % 11), 0.5, true);
}
else
{
if (position % 11 > 0)
position--;
listView->scrollToPercentVertical(10 * (position % 11), 0.5, true);
}
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(listenerMouse, this);
// 不同游戏模式的排行榜切换按钮
auto changeTypeButton = Button::create(theme->menuChangeTypeButtonNormalBackground,
theme->menuChangeTypeButtonSelectedBackground,
theme->menuChangeTypeButtonDisabledBackground); // 切换到进阶
auto backTypeButton = Button::create(theme->menuBackTypeButtonNormalBackground,
theme->menuBackTypeButtonSelectedBackground,
theme->menuBackTypeButtonDisabledBackground); // 切换到经典
changeTypeButton->setPosition(Point(SCREEN_WIDTH / 2 + listView->getContentSize().width / 2 + 50,
SCREEN_HEIGHT / 2));
backTypeButton->setPosition(Point(SCREEN_WIDTH / 2 - listView->getContentSize().width / 2 - 50,
SCREEN_HEIGHT / 2));
backTypeButton->setOpacity(0); // 切换经典初始不可见
backTypeButton->addTouchEventListener(
[&, changeTypeButton, backTypeButton](Ref* sender, ui::Widget::TouchEventType type)
{
if (type == Widget::TouchEventType::ENDED)
{
title->setString("经典挑战排行");
backTypeButton->setOpacity(0);
changeTypeButton->runAction(FadeIn::create(0.25));
if (changed)
getRankByType(true);
changed = false;
}
});
changeTypeButton->addTouchEventListener(
[&, changeTypeButton, backTypeButton](Ref* sender, ui::Widget::TouchEventType type)
{
if (type == Widget::TouchEventType::ENDED)
{
title->setString("进阶挑战排行");
changeTypeButton->setOpacity(0);
backTypeButton->runAction(FadeIn::create(0.25));
if (!changed)
getRankByType(false);
changed = true;
}
});
// 如果在menuScene中,排行榜可切换,gameScene中排行榜不可切换
if (isInMenu)
{
this->addChild(changeTypeButton);
this->addChild(backTypeButton);
}
// 根据游戏模式初始化排行榜
this->getRankByType(isClassical);
}
void Dialog::getRankByType(bool type)
{
rankType = type;
if (content)
{
this->removeChild(content);
listView->setScrollBarEnabled(true);
listView->setScrollBarAutoHideTime(0);
}
if (!(rankType ? classicalRank.empty() : plusRank.empty()))
{
if (listView->getChildrenCount() > 0)listView->removeAllChildren();
for (auto i = 0; i < 10; i++)
{
auto icon = ImageView::create(theme->iconSet + std::to_string(i) + ".png");
layout = Layout::create();
layout->setLayoutType(Layout::Type::ABSOLUTE);
layout->setContentSize(Size(500, icon->getContentSize().height + 30));
icon->setPosition(Vec2(icon->getContentSize().width / 2 + 60, 30));
layout->addChild(icon);
layout->setBackGroundColorType(Layout::BackGroundColorType::NONE);
rankLabel = Label::createWithTTF(std::to_string(i + 1), theme->semiBoldFont, 30);
rankLabel->setPosition(Vec2(icon->getContentSize().width / 2 + 10, 30));
layout->addChild(rankLabel);
if (i > (rankType ? classicalRank.size() : plusRank.size()) - 1)
{
nameLabel = Label::createWithTTF("------------------", theme->markerFeltFont, 30);
nameLabel->setPosition(Vec2(
icon->getContentSize().width + nameLabel->getContentSize().width / 2 + 75,
30));
layout->addChild(nameLabel);
scoreLabel = Label::createWithTTF("---------", theme->markerFeltFont, 30);
scoreLabel->setPosition(Vec2(465 - scoreLabel->getContentSize().width / 2, 30));
layout->addChild(scoreLabel);
}
else
{
nameLabel = Label::createWithTTF(rankType ? classicalRank.at(i).first : plusRank.at(i).first,
theme->semiBoldFont, 30);
nameLabel->setPosition(Vec2(
icon->getContentSize().width + nameLabel->getContentSize().width / 2 + 75,
30));
layout->addChild(nameLabel);
scoreLabel = Label::createWithTTF(
std::to_string(rankType ? classicalRank.at(i).second : plusRank.at(i).second),
theme->semiBoldFont, 30);
scoreLabel->setPosition(Vec2(465 - scoreLabel->getContentSize().width / 2, 30));
layout->addChild(scoreLabel);
}
listView->pushBackCustomItem(layout);
listView->setBottomPadding(icon->getContentSize().height);
}
}
else
{
if (listView->getChildrenCount() > 0)listView->removeAllChildren();
listView->setScrollBarEnabled(false);
setContentText("抱歉! 网络连接失败!", 36, 60, 20);
content = getLabelContentText();
content->setLineBreakWithoutSpace(true);
content->setMaxLineWidth(dialogContentSize.width - 2 * contentPadding);
content->setPosition(Vec2(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2));
content->setHorizontalAlignment(TextHAlignment::LEFT);
this->addChild(content);
}
}
最近在做个小的示例项目,确定后打算用cocos2dx框架来做UI部分,毕竟它易于使用还跨平台,像我这样几乎完全没有cocos2dx基础的童鞋,也能快速上手。在开发过程中,我想像在桌面应用中那样,弹出一个窗口并给出一些文本提示。无奈好像cocos2dx并没有给出现成可用的东西,只能自己琢磨实现了。考虑过后,打算通过Layer类来构建一个子类,然后在Scene中去显示该Layer,从而模拟出“窗口”的效果。我查阅了一些资料发现,网上有很多类似的实现代码,但他们大多已经过时,因这两年cocos2dx的版本变化好像挺大。
按照上述的思路构建子类测试后发现,监听的事件不会被“阻隔”,例如:我们在Scene中监听了鼠标和键盘,然后在Scene中去添加了该Layer,并在该Layer中也监听了鼠标点击事件,当我们在该Layer中点击鼠标时,Scene中也会触发该事件。这就导致了一些奇怪的现象,和预期不符合。本文将试着简介如何用一种足够简单的方法,构建这样一个“模态对话框类”并解决包含事件不“阻隔”在内的几乎所有问题。
在开始前,先简要说明一下环境。本次实验编写于Linux平台。因实验于PC平台,我在示例所监听的都是鼠标和键盘事件,对于移动平台的触摸事件未做任何测试,但理论上也是可行的,具体请读者自测和变更,以适应特定的场景。
#ifndef MYGAME_POPUPLAYER_H
#define MYGAME_POPUPLAYER_H
#include "cocos2d.h"
USING_NS_CC;
class PopupLayer:public cocos2d::Layer
{
public:
PopupLayer();
virtual ~PopupLayer();
private:
std::string backgroundImage;
EventListenerMouse *listenerMouse;
public:
virtual bool init() override;
CREATE_FUNC(PopupLayer);
static PopupLayer* create(const std::string backgroundImage);
bool setBackgroundImage(const std::string &backgroundImage);
void okMenuItemCallback(Ref *pSender);
void cancelMenuItemCallback(Ref *pSender);
/**
* Event callback that is invoked every time when Node enters the 'stage'.
* If the Node enters the 'stage' with a transition, this event is called when the transition starts.
* During onEnter you can't access a "sister/brother" node.
* If you override onEnter, you shall call its parent's one, e.g., Node::onEnter().
* @lua NA
*/
virtual void onEnter() override;
/**
* Event callback that is invoked every time the Node leaves the 'stage'.
* If the Node leaves the 'stage' with a transition, this event is called when the transition finishes.
* During onExit you can't access a sibling node.
* If you override onExit, you shall call its parent's one, e.g., Node::onExit().
* @lua NA
*/
virtual void onExit() override;
};
#endif //MYGAME_POPUPLAYER_H
首先 PopupLayer继承自Layer类,你也可以继承自其他Layer类,如LayerColor,这样你可以获得不同的特性。这里仅仅定义了两个数据成员,一个std::string用于存储整个“窗口”的背景图文件名,一个EventListenerMouse指针存储鼠标监听器,稍后可以看到是如何使用它们的。最后,来看看添加了哪些成员方法。首先,我重写了基类的init()方法,如果你对cocos2dx不陌生,那么你应该知道在该方法中要做哪些事情。其次,我使用 CREATE_FUNC 宏来构建了一个默认的静态create()方法,这基本上也是cocos2dx的传统。为了进一步满足需求,我还提供了静态create()方法的一个重载,它将包含一个参数用于指定背景资源名。setBackgroundImage()方法可以用来设置和更改背景资源。okMenuItemCallback()和cancelMenuItemCallback()是“模态对话框”上两个按钮的回调方法。最后我还重写了基类的onEnter()和onExit()方法,这两个方法至关重要,稍后你将看到它们是如何相互配合完成整个功能的。
cocos2dx使用一个类对象前,总是先调用它的静态create()方法来创建该类的一个实例,所以我们先从它开始介绍。
PopupLayer* PopupLayer::create(const std::string backgroundImage)
{
PopupLayer *pl = new(std::nothrow) PopupLayer();
if (pl && pl->setBackgroundImage(backgroundImage) && pl->init())
{
pl->autorelease();
return pl;
}
else
{
delete pl;
pl = nullptr;
return nullptr;
}
}
可以看到,我们只增加了setBackgroundImage()方法的调用。接着就是init()方法了。
bool PopupLayer::init()
{
// 1. super init first
if ( !Layer::init() )
{
return false;
}
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
this->setAnchorPoint(Vec2::ZERO);
// add "HelloWorld" splash screen"
auto sprite = Sprite::create(backgroundImage);
// position the sprite on the center of the screen
sprite->setPosition(Vec2(visibleSize.width/2, visibleSize.height/2));
// add the sprite as a child to this layer
this->addChild(sprite, 1);
auto label = Label::createWithTTF("Are u sure exit?", "fonts/Marker Felt.ttf", 24);
label->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2 + 50));
label->setColor(Color3B(255,0,0));
this->addChild(label, 1);
auto okMenuItem = MenuItemFont::create("OK", CC_CALLBACK_1(PopupLayer::okMenuItemCallback, this));
okMenuItem->setPosition(Vec2(visibleSize.width / 2 - 100, visibleSize.height / 2 - 50));
okMenuItem->setColor(Color3B(255,0,0));
auto cancelMenuItem = MenuItemFont::create("Cancel", CC_CALLBACK_1(PopupLayer::cancelMenuItemCallback, this));
cancelMenuItem->setPosition(Vec2(visibleSize.width / 2 + 100, visibleSize.height / 2 - 50));
cancelMenuItem->setColor(Color3B(255,0,0));
auto pMenu = Menu::create(okMenuItem, cancelMenuItem, NULL);
pMenu->setPosition(Vec2::ZERO);
this->addChild(pMenu, 1);
return true;
}
init()方法也足够简单,创建了一个sprite来作为背景,一个label来显示一些文本信息和两个菜单按钮,并设置了这两个菜单按钮的回调方法。
最后是onEnter()方法和onExit()方法,这是两个重点。onEnter()方法是在该“模态对话框”Layer显示时会被调用。相反,onExit()方法会在它消失时被调用。这两个机制可以用来做一些事情。
void PopupLayer::onEnter()
{
Layer::onEnter();
if (listenerMouse == nullptr)
{
listenerMouse = EventListenerMouse::create();
listenerMouse->onMouseDown = [](EventMouse* event) {
};
}
if (listenerMouse)
{
_eventDispatcher->pauseEventListenersForTarget(this->getParent(), true);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listenerMouse, this);
}
}
void PopupLayer::onExit()
{
Layer::onExit();
if (listenerMouse)
{
_eventDispatcher->removeEventListener(listenerMouse);
_eventDispatcher->resumeEventListenersForTarget(this->getParent(), true);
}
}
在onEnter()方法中,我们先创建一个鼠标监听对象,并设置了它的回调函数,只不过回调函数为空,什么也不做。然后通过eventDispatcher对象的pauseEventListenersForTarget()方法来暂停“模态对话框”父级窗口的事件监听器,并把自己的监听器添加到事件分发器中。所谓的父级窗口指的是“模态对话框”的下层Layer。这里是“模态对话框”下层Layer屏蔽事件监听的关键所在,如果不屏蔽它,则在“模态对话框”上发生的监听事件也会传递给它的下层Layer。也有的文章说可以设置监听器的优先级来解决该问题,但我没能找到解决方法。
在onExit()方法中,我们来做一些清理还原工作,因为onExit()方法被调用,意味着“模态对话框”Layer即将消失,所以我们从事件分发器中删除它的鼠标监听器,并重新启用它下层Layer的事件监听器。这样便可以还原。
最后,来看看如何使用它。我通过cocos2dx创建了一个新工程,并对HelloWorld场景做了一些小的改动,以方便测试。下面是它的init()方法。
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
/////////////////////////////
// 2. add a menu item with "X" image, which is clicked to quit the program
// you may modify it.
// add a "close" icon to exit the progress. it's an autorelease object
auto closeItem = MenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
origin.y + closeItem->getContentSize().height/2));
// create menu, it's an autorelease object
auto menu = Menu::create(closeItem, NULL);
menu->setPosition(Vec2::ZERO);
this->addChild(menu, 1);
/////////////////////////////
// 3. add your codes below...
// add a label shows "Hello World"
// create and initialize a label
auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
// position the label on the center of the screen
label->setPosition(Vec2(origin.x + visibleSize.width/2,
origin.y + visibleSize.height - label->getContentSize().height));
// add the label as a child to this layer
this->addChild(label, 1);
auto label1 = Label::createWithTTF("Press the Enter key or click the left mouse button.", "fonts/Marker Felt.ttf", 12);
// position the label on the center of the screen
label1->setPosition(Vec2(origin.x + visibleSize.width/2, 100));
// add the label as a child to this layer
this->addChild(label1, 1);
// add "HelloWorld" splash screen"
auto sprite = Sprite::create("HelloWorld.png");
// position the sprite on the center of the screen
sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
// add the sprite as a child to this layer
this->addChild(sprite, 0);
//添加鼠标事件侦听
auto listenerMouse = EventListenerMouse::create();
listenerMouse->setEnabled(true);
listenerMouse->onMouseDown = [&](EventMouse* event) {
int mouseBtn = event->getMouseButton();
if (0 == mouseBtn) {
auto sprite = Sprite::create("HelloWorld.png");
sprite->setPosition(event->getLocationInView().x, event->getLocationInView().y);
sprite->setScale(0.5f,0.5f);
this->addChild(sprite, 1);
}
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(listenerMouse, this);
//添加键盘事件监听
auto listenerKeyboard = EventListenerKeyboard::create();
listenerKeyboard->setEnabled(true);
listenerKeyboard->onKeyPressed = [&](EventKeyboard::KeyCode keycode, Event* event){
if(EventKeyboard::KeyCode::KEY_ENTER == keycode){
auto pl = PopupLayer::create("background.png");
pl->setScale(0.5f,0.5f);
pl->setParent(this);
pl->setPosition(Vec2(visibleSize.width/2, visibleSize.height/2));
pl->setAnchorPoint(Vec2(0.5f,0.5f));
this->addChild(pl, 2);
}
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(listenerKeyboard, this);
return true;
}
我在里面增加了一个label,用于显示一些提示信息。增加了鼠标左键的事件监听,并在点击的位置绘制一张图片。这样做可以很好的演示当“模态对话框”Layer打开后,是否有效的屏蔽了下层Layer的事件监听。最后,添加了键盘监听,当我们敲击Enter键后,“模态对话框”Layer就会显示出来。值得特别注意的是,我调用了这样一个方法 pl->setParent(this);
用于设置了“模态对话框”Layer的父级Node,这是基类给提供的一个方法,千万别忘了设置它,其他的都足够简单了。
好了,你可以试着获取完整的代码,并编译运行一下,希望能解决你的问题或者给你一些新的思路。最后,我不太了解cocos2dx框架,如果本文或者示例中有什么错误或者不规范的地方,请指正,谢谢。