简述
无论是否针对Cocos2d-x,事件处理都是很重要的部分。一般都含有鼠标,键盘事件,还有一些就是根据自己特性特有的。因为Cocos2d-x是游戏引擎,所以在游戏中 点击 /滑动 用到的 单点触摸事件(EventListenerTouchOneByOne)、多点触发事件(EventListenerTouchAllAtOnce)就很好理解了。除了这些 还有加速计事件(EventListenerAcceleration),自定义事件(EventListenerCustom)等。
基本元素
EventListener 类是事件监听器的基类,如果需要自定义监听器与不同的回调,需要继承该类 。该类的继承关系如图所示。
(2)事件分发器
EventDispatcher类表示一个事件的分发器,它的作用是管理事件监听器及事件分发,当一个事件触发时,它会向所有需要的对象发送用户操作信息。在Node类中有个_eventDispatcher成员变量,通过它管理节点(如场景、层,精灵等)的多有事件分发情况。EventDispatcher 类还有一个集合列表,用来保存所有添加了的事件监听器。
常用的函数有:
void addEventListenerWithSceneGraphPriority(EventListener*listener,Node*node) :添加场景优先事件监听器。响应事件优先级基于Node绘制顺序,zOrder顺序高的先调用。自顶向下的传播。
void addEventListenerWithFixPriority(EventListener* listener,Node* node):添加固定优先值的事件监听器。优先级值低的事件监听器将先于优先级值高的事件监听器被调用。
示例
列举几个例子更清楚的理解。
(1)TouchOneByOneTest (单点触发事件 EventListenerTouchOneByOne)
// 创建三个精灵对象
auto sprite1 = Sprite::create("CyanSquare.png");
sprite1->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2) + Vec2(-80, 80));
addChild(sprite1, 10);
auto sprite2 = Sprite::create("MagentaSquare.png");
sprite2->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2));
addChild(sprite2, 20);
auto sprite3 = Sprite::create("YellowSquare.png");
sprite3->setPosition(Vec2(0, 0));
sprite2->addChild(sprite3, 1);
//创建一个单点触摸事件监听器,处理触摸事件逻辑
auto listener1 = EventListenerTouchOneByOne::create();
//设置是否向下传递触摸
listener1->setSwallowTouches(true);
//通过 lambda 表达式 直接实现触摸事件的响应函数
listener1->onTouchBegan = [](Touch* touch, Event* event){
auto targer = static_cast(event->getCurrentTarget());
Point locationInNode = targer->convertToNodeSpace(touch->getLocation());
Size s = targer->getContentSize();
Rect rect = Rect(0, 0, s.width, s.height);
if (rect.containsPoint(locationInNode))
{
log("onTouchBegin...x=%f, y=%f", locationInNode.x, locationInNode.y);
targer->setOpacity(180);
return true;
}
return false;
};
listener1->onTouchMoved = [](Touch* touch, Event *event){
auto target = static_cast(event->getCurrentTarget());
//移动精灵
target->setPosition(target->getPosition() + touch->getDelta());
};
//[=]值传递
listener1->onTouchEnded = [=](Touch* touch, Event *event){
auto target = static_cast(event->getCurrentTarget());
// 设置透明度
target->setOpacity(255);
std::string name;
if (target == sprite2)
{
name = "MagentaSquare.png";
sprite1->setZOrder(100);
subtitle->setString(FontToUTF8("响应事件的是酒红色滑块,青色滑块的ZOrder值修改为100"));
}
else if (target == sprite1)
{
name = "CyanSquare.png";
sprite1->setZOrder(0);
subtitle->setString(FontToUTF8("响应事件的是青色滑块,青色滑块的ZOrder值修改为0"));
}
else{
name = "YellowSquare.png";
}
log(FontToUTF8("onTouchEnded.. 您触摸的是%s"), name.c_str());
};
//添加场景优先事件监听器
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, sprite1);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite3);
注意:在使用addEventListenerWithSceneGraphPriority或者addEventListenerWithFixedPriority函数时,会对当前使用的事件监听器添加一个已注册的标记,这使得它不能够被添加多次。所以当我们再次使用listener1的时候,需要使用clone()函数创建一个新的克隆。
(2)TouchAllatOnceTest(多点触发事件 EventListenerTouchAllAtOnce)
多点触发事件略微与单点触发事件不同,主要体现在:人家是多点,o(* ̄︶ ̄*)o,从函数
void onTouchesBegan(const std::vector
自定义事件不是由系统自动触发的,而是需要人为干涉,这一点很需要注意。
void TouchAllatOnceTest::onTouchesBegan(const std::vector& touches, Event *event)
{
if (touches.size() >= 2)
{
auto touch1 = touches.at(0);
Vec2 mPoint1 = Director::getInstance()->convertToGL(touch1->getLocation());
auto touch2 = touches.at(1);
Vec2 mPoint2 = Director::getInstance()->convertToGL(touch2->getLocation());
// 获得新触摸点两点之间的距离
_distance = sqrt((mPoint1.x - mPoint2.x)*(mPoint1.x - mPoint2.x) + (mPoint1.y - mPoint2.y)*(mPoint1.y - mPoint2.y));
}
}
void TouchAllatOnceTest::onTouchesMoved(const std::vector& touches, Event *event)
{
if (touches.size() >= 2)
{
auto touch1 = touches.at(0);
Vec2 mPoint1 = Director::getInstance()->convertToGL(touch1->getLocation());
auto touch2 = touches.at(1);
Vec2 mPoint2 = Director::getInstance()->convertToGL(touch2->getLocation());
// 获得新触摸点两点之间的距离
double mdistance = sqrt((mPoint1.x - mPoint2.x)*(mPoint1.x - mPoint2.x) + (mPoint1.y - mPoint2.y)*(mPoint1.y - mPoint2.y));
_mscale = mdistance / _distance*_mscale;
_bgSprite->setScale(_mscale);
}
}
void TouchAllatOnceTest::onTouchesEnded(const std::vector& touches, Event *event)
{
}
void TouchAllatOnceTest::onTouchesCancelled(const std::vector& touches, Event *event)
{
}
auto listener = EventListenerCustom::create("game_custom_event", [=](EventCustom* event){
statusLabel->setString(StringUtils::format("receive custom event [%s]times", event->getUserData()));
});
_eventDispatcher->addEventListenerWithFixedPriority(listener, 1);
static int count = 0;
auto sendItem = MenuItemFont::create("click send custom event", [=](Ref* sender){
++count;
char* buf = new char[10];
sprintf(buf, "%d", count);
EventCustom event("game_custom_event");
event.setUserData(buf);
//将此事件分发出去 从而触发之前所实现的逻辑 这里发送出去 在上面响应
_eventDispatcher->dispatchEvent(&event);
//释放数据占据的内存
CC_SAFE_DELETE_ARRAY(buf);
});