2016-02-19新增-------------------------------------------------------------------------------------------------------------
--quick-cocos2d-x_2.2.6
--返回按键监听
self.layer = display.newLayer()
self.layer:addNodeEventListener(cc.KEYPAD_EVENT, function(event)
if event.key == "back" then
--self.mainPopup:show("要退出吗", true)
--再按一次返回键退出
end
end)
self.layer:setKeypadEnabled(true)
self:addChild(self.layer)
原文-----------------------------------------------------------------------------------------------------------------------------
(Cocos-2.2.6 & Framework-3.5)
在玩cocos2d-x的时候,每次真机实测,都要按home键来‘退出’游戏,因为没有监听返回键,这让我想起了某个版本的手机QQ,也是没有监听返回键来着。
来简单写一下cocos2d-x监听安卓的返回键。
简单的用helloworld项目来实验
首先在.h文件覆盖onKeyReleased函数。
virtual void onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event);
在.cpp中init函数里面注册键盘监听
bool HelloWorld::init()
{
//省略一些代码
auto backKeyListener = EventListenerKeyboard::create();
backKeyListener->onKeyReleased = CC_CALLBACK_2(HelloWorld::onKeyReleased, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(backKeyListener, this);
}
之后捕捉返回键(不排除手机机型问题和引擎版本问题引起keyCode的不同,keyCode请自行真机测试,我这里三星S4的返回键对应KEY_BACK)
void HelloWorld::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event)
{
if (keyCode == EventKeyboard::KeyCode::KEY_BACK)
{
Director::getInstance()->end();
}
}
好了,很简单。返回键按下立马退出。但是也太简单了,我们比较常见的app或者游戏的返回键监听一般设计成这两种形式:
1、“再按一次返回键退出”。2、弹出对话框确认退出。
先来第一种。
我们要实现“再按一次返回键退出”,那就得让程序知道,用户到底按了几次返回键。
另外,这两次按下返回键的间隔也应该有个限制,总不能,第一按下的时候,提示“再按一次返回键退出”,过了10分钟再按返回键就直接退出了。
相应的步骤大概是这样。
在上一段的代码基础上,.h文件里面声明一个bool变量来标记是否按了第一次。
bool m_keyBackFlag;
之后在init函数里面初始化为false
m_keyBackFlag = false;
然后HelloWorld::onKeyReleased要改挺多,因为我们要区别对待是否按了第一次返回键,所以先套一个if(!m_keyBackFlag)else,之后再分别编写。
void HelloWorld::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event)
{
if (keyCode == EventKeyboard::KeyCode::KEY_BACK)
{
if (!m_keyBackFlag)// 在过去的1秒钟之内没按过返回键,提示信息再按一次
{
TTFConfig ttfConfig("fonts/jt.ttf", 24);
auto pMessage = Label::createWithTTF(ttfConfig,
"再按一次返回键",
TextHAlignment::LEFT, Director::getInstance()->getWinSize().width * 0.7);
pMessage->setPosition(Point(100, 80));
this->addChild(pMessage);
auto pMessageFadeOut = FadeOut::create(1.0f);
auto pMessageMoveBy = MoveBy::create(1.0f, Vec2(0, 75));
auto pSpawn = Spawn::create(pMessageMoveBy, pMessageFadeOut, nullptr);
pMessage->runAction(pSpawn);
m_keyBackFlag = true;
this->scheduleOnce(
[=](float dt){
this->m_keyBackFlag = false;
},
1,//1秒之内如果不按返回键,那么m_keyBackFlag变量重置
"keyBackFlag"
);
}
else//在过去的1秒钟之内曾按下返回键,现在按的是第二下
{
Director::getInstance()->end();
}
}
}
效果图
细心的同学可能发现了,我们的左下角的信息里节点数在涨个不停。原因是,虽然这个提示信息(Label)消失了,但是我们没有移除它。
所以,每次显示的提示信息(Label)都会一直存在于我们的项目中。所以我们要移除它。也就是,在它消失的那一刻,他的生命是周期也结束了。
我们既然写有了一个lambda,那就好好利用它。
首先在pMessage->runAction(pSpawn);下加一句pMessage->retain();。为什么,后面再提。
之后在lambda里面的this->m_keyBackFlag = false;后面加一句this->removeChild(pMessage);。
刚刚前面要调用pMessage的retain函数,原因是,我们需要在一段时间后才移除这个pMessage指针,但是呢,那个时候程序已经离开了这个代码快。
pMessage指针失效了,pMessage指向的Label还在(透明的)。所以我们需要调用retain(),让它摆脱cocos的内存管理机制,来让lambda能捕捉到它。
再上图这回节点数就正常了。(因为只加了两行代码,请自行添加吧,我不贴代码了)
==========================================================================================再看第二种,弹出确认框。
这里我先说一下,对话框的类我没有实现,只是用成员指针来模拟实现的。
弹出对话框的实现,bool变量的flag我还是留着的,防止按多次返回键来弹出创建多次对话框(因为我没有写对话框的类,只是当场创建精灵和按钮来模拟对话框的)
当然用别的方法来预防也是可以的。
相应的步骤大概是这样。
这里要解决的一个小问题是,在弹出退出确认框的时候,游戏本身的所有监听事件就要屏蔽了,防止被玩家误触。也可以说,弹出来的对话框优先级最高。
我这里大概的思路就是,先做一个遮盖层,这个层吸收所有的触摸事件,然后在这个层之上,创建对话框。
如果用户选择退出,则退出游戏。如果取消,则返回游戏,移除这个遮盖层和对话框。
思路是这样,实现起来可以用层(Layer),我这里不用层(Layer)来实现这个遮盖用的层。
我们用一个足够大ui::ImageView对象,设置交互性setTouchEnabled(true);,这样,就能做到我们想要的效果了。
当然,也可以用其他的有setTouchEnable成员的类,只不过,ui::ImageView的创建已经非常简便了。就用这个好了。
之后,再创建对话框,并且设置好回调函数就好了。
因为要用成员变量来模拟对话框,所以成员变量比较多,如果自己已经实现了对话框类,那么只要少量成员变量就好了。
.h文件中声明的变量和函数。我们依然需要覆盖onKeyReleased函数。另外声明两个回调函数,分别作为确定退出和取消退出的回调。
先上效果图。
对话框是用精灵和按钮模拟的,所以成员变量有点多,如果自己实现了对话框类的话,变量会少很多。
virtual void onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event);
void testCallback(Ref* pSender);
void testCallback2(Ref* pSender);
bool m_keyBackFlag;
ui::ImageView* m_cover;
Sprite* m_outBox;
Label* m_pMessage2;
Label* m_pLabelYes;
Label* m_pLabelNo;
MenuItemLabel* m_pItemYes;
MenuItemLabel* m_pItemNo;
Menu* m_pm;
那么初始化的时候可以全部赋空指针。
bool HelloWorld::init()
{
//省略了一些代码
m_cover = nullptr;
m_pMessage2 = nullptr;
m_pLabelYes = nullptr;
m_pLabelNo = nullptr;
m_pItemYes = nullptr;
m_pItemNo = nullptr;
m_pm = nullptr;
m_outBox = nullptr;
m_keyBackFlag = false;
//同样还要注册监听
auto backKeyListener = EventListenerKeyboard::create();
backKeyListener->onKeyReleased = CC_CALLBACK_2(HelloWorld::onKeyReleased, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(backKeyListener, this);
}
void HelloWorld::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event)
{
if (keyCode == EventKeyboard::KeyCode::KEY_BACK)
{
if (!m_keyBackFlag)
{
//遮挡层用ui::ImageView来吸收触屏事件
m_cover = ui::ImageView::create("colorBG.png");
m_cover->setPosition(Vec2(Director::getInstance()->getWinSize().width * 0.5, Director::getInstance()->getWinSize().height * 0.5));
m_cover->setTouchEnabled(true);
m_cover->setOpacity(128);
this->addChild(m_cover);
//下面都是对话框的模拟实现
m_outBox = Sprite::create("outBox.png");
m_outBox->setPosition(270, 450);
this->addChild(m_outBox);
TTFConfig ttfConfig("fonts/jt.ttf", 24);
m_pMessage2 = Label::createWithTTF(ttfConfig,
"确定要退出么?",
TextHAlignment::LEFT, Director::getInstance()->getWinSize().width * 0.7);
m_pMessage2->setPosition(Vec2(270, 500));
this->addChild(m_pMessage2);
m_pLabelYes = Label::createWithTTF(ttfConfig,
"是的",
TextHAlignment::LEFT, Director::getInstance()->getWinSize().width * 0.7);
m_pLabelNo = Label::createWithTTF(ttfConfig,
"不要",
TextHAlignment::LEFT, Director::getInstance()->getWinSize().width * 0.7);
m_pItemYes = MenuItemLabel::create(m_pLabelYes, CC_CALLBACK_1(HelloWorld::testCallback, this));
m_pItemNo = MenuItemLabel::create(m_pLabelNo, CC_CALLBACK_1(HelloWorld::testCallback2, this));
m_pItemYes->setPosition(Vec2(270, 450));
m_pItemNo->setPosition(Vec2(270, 400));
m_pm = Menu::create(m_pItemYes, m_pItemNo, nullptr);
m_pm->setPosition(Vec2(0, 0));
this->addChild(m_pm);
m_keyBackFlag = true;
}
}
}
void HelloWorld::testCallback(Ref* pSender)//确定退出的回调
{
Director::getInstance()->end();
}
void HelloWorld::testCallback2(Ref* pSender)//取消退出的回调
{//移除对话框
CCLOG("testCallback2");
this->m_keyBackFlag = false;
this->removeChild(m_pMessage2);
this->removeChild(m_pLabelYes);
this->removeChild(m_pLabelNo);
this->removeChild(m_pm);
this->removeChild(m_pItemYes);
this->removeChild(m_pItemNo);
this->removeChild(m_outBox);
this->removeChild(m_cover);
}