这篇文章主要介绍touch事件机制,还会涉及到一些的介绍Cocos2d-x的内存回收相关内容。
一、触摸事件
简单介绍一下Touch事件流程:玩家触摸到设备屏幕后,引擎会把触摸事件派发给注册了事件监听的相应代理,触摸的具体信息会以参数的形式传递给相应回调函数。
GameEngin-> EventDispatcher -> ??->onTouchXXX
TouchEvent(触摸事件)分为2种 :EventListenerTouchAllAtOnce(多点触摸) 和 EventListenerTouchOneByOne (单点触摸)。
以单点触摸为例,创建一个Touch事件后,要给 touch->onTouchBegan onTouchCancelled onTouchEnded onTouchMoved 赋值相应的回调,这4个回调分别代表了
onTouchBegan 开始接触屏幕
onTouchCancelled
事件被取消(比如程序切到后台)
onTouchEnded
触摸事件结束,手指离开屏幕
onTouchMoved 手指在屏幕上滑动
这4个回调方法里,除了第一个
onTouchBegan 的返回值是bool,其他都是void。那么这个bool有什么用呢? 看一下CCEventDispatcher的源码(CCEventDispatcher.cpp),不难发现,
当开始触摸事件返回true时,之后的触摸结束、触摸移动等事件才能触发,反之,如果TouchBegan返回false,后面的流程不会被触发。
下面贴出给node添加Touch事件的流程
<span style="white-space:pre"> </span>//添加点击监听(单点触摸)
EventListenerTouchOneByOne* tevent = EventListenerTouchOneByOne::create();
tevent->setSwallowTouches(true);
tevent->onTouchBegan = [&](Touch* touch, Event*){
for(auto key : m_clickDelegates)
{
key.second(touch);
}
return false;
};
tevent->onTouchCancelled = [&](Touch*, Event*){
};
tevent->onTouchEnded = [&](Touch*, Event*){
};
tevent->onTouchMoved = [&](Touch*, Event*){
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(tevent,this);
setSwallowTouches
代表是否阻止当前事件向下一层级派发。
二、cocos2d::Map std::map (这里用大写的Map代表Cocosd中的,小写的map代表标准库函数中的)
cocos2d::Map和std::map功能基本一样,不同的是,Map沿用了cocos2dx的内存回收机制,当添加一个value到Map中时,value.retain()会被调用,移除后,value.release()会被调用,这样,我们就不用手动的去处理map内部引用计数了,但为了使用这个便利,Map<k,v>中的v 必须是 Ref* 类型 !。 这样,如果想用Ref* 做key,而value是一个 std::function<void(Touch*)>类型,就只能使用标准库函数了,但这样就要自己处理Map内部Ref* 类型的引用计数。
之前一直相当然的认为对std::map遍历的时候,是直接遍历所有的value,但结果遍历的类型是 std::pair<k,v> 。
下面看一下单点触摸层简单实现
class LayerTouch:public cocos2d::Layer
{
public:
LayerTouch();
~LayerTouch();
virtual bool init();
static LayerTouch* create();
//添加当前回调事件到点击事件回调map中
void addClickDelegate(cocos2d::Ref*,std::function
);
private:
//点击事件回调字典
std::map
> m_clickDelegates;
};
#include "LayerTouch.h"
USING_NS_CC;
LayerTouch::LayerTouch():m_clickDelegates(std::map
>()) { } LayerTouch::~LayerTouch() { for(std::map
>::iterator iter = m_clickDelegates.begin();iter != m_clickDelegates.end();iter++) { iter->first->release(); } m_clickDelegates.clear(); } void LayerTouch::addClickDelegate(cocos2d::Ref* clickDelegate,std::function
callback) { m_clickDelegates[clickDelegate] = callback; clickDelegate->retain(); } bool LayerTouch::init() { if(Layer::init()) { //添加点击监听(单点触摸) EventListenerTouchOneByOne* tevent = EventListenerTouchOneByOne::create(); tevent->setSwallowTouches(true); tevent->onTouchBegan = [&](Touch* touch, Event*){ for(auto key : m_clickDelegates) { key.second(touch); } //true 会继续监听后面的touch move,touch end等事件,false则不会 return false; }; tevent->onTouchCancelled = [&](Touch*, Event*){ }; tevent->onTouchEnded = [&](Touch*, Event*){ }; tevent->onTouchMoved = [&](Touch*, Event*){ }; Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(tevent,this); return true; } return false; } LayerTouch* LayerTouch::create() { LayerTouch* tlayer = new LayerTouch(); if(tlayer && tlayer->init()) { tlayer->autorelease(); return tlayer; }else { CC_SAFE_RELEASE(tlayer); } return nullptr; }