您的位置:首页 > 移动开发 > Cocos引擎

cocos2d-x (四):触摸事件处理机制

2015-04-11 17:16 357 查看
触摸事件可以分为如下四个状态
开始、移动、结束、取消
cocos2d-x使用EventTouch来表示一个触摸事件
触摸点的状态使用 EventCode来表示 
class CC_DLL EventTouch : public Event
{
public:
static const int MAX_TOUCHES = 15;

enum class EventCode
{
BEGAN,
MOVED,
ENDED,
CANCELLED
};

EventTouch();

inline EventCode getEventCode() const { return _eventCode; };
inline const std::vector<Touch*>& getTouches() const { return _touches; };


}
根据状态不同,同一个触摸点会经历多次事件分发。

首先介绍事件监听器的2种类型
EventListenerTouchOneByOne 监听的是单点触摸
class CC_DLL EventListenerTouchOneByOne : public EventListener
{
public:
static const std::string LISTENER_ID;

static EventListenerTouchOneByOne* create();

virtual ~EventListenerTouchOneByOne();

void setSwallowTouches(bool needSwallow);
bool isSwallowTouches();

/// Overrides
virtual EventListenerTouchOneByOne* clone() override;
virtual bool checkAvailable() override;
//

public:

typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;
typedef std::function<void(Touch*, Event*)> ccTouchCallback;

ccTouchBeganCallback onTouchBegan;
ccTouchCallback onTouchMoved;
ccTouchCallback onTouchEnded;
ccTouchCallback onTouchCancelled;

CC_CONSTRUCTOR_ACCESS:
EventListenerTouchOneByOne();
bool init();

private:
std::vector<Touch*> _claimedTouches;
bool _needSwallow;

friend class EventDispatcher;
};


EventListenerTouchAllAtOnce 监听的就是多点触摸
class CC_DLL EventListenerTouchAllAtOnce : public EventListener
{
public:
static const std::string LISTENER_ID;

static EventListenerTouchAllAtOnce* create();
virtual ~EventListenerTouchAllAtOnce();

/// Overrides
virtual EventListenerTouchAllAtOnce* clone() override;
virtual bool checkAvailable() override;
//
public:

typedef std::function<void(const std::vector<Touch*>&, Event*)> ccTouchesCallback;

ccTouchesCallback onTouchesBegan;
ccTouchesCallback onTouchesMoved;
ccTouchesCallback onTouchesEnded;
ccTouchesCallback onTouchesCancelled;

CC_CONSTRUCTOR_ACCESS:
EventListenerTouchAllAtOnce();
bool init();
private:

friend class EventDispatcher;
};


可以看到这单点触摸和多点触摸成员变量定义的主要区别
EventListenerTouchOneByOne
    typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;
    typedef std::function<void(Touch*, Event*)> ccTouchCallback;
EventListenerTouchAllAtOnce
typedef std::function<void(const std::vector<Touch*>&, Event*)> ccTouchesCallback;

它们都定义了4个回调函数,分别用来处理触摸点的开始、移动、结束及取消4中不同的状态
其中,EventListenerTouchOneByOne里的ccTouchBeganCallback的返回值为bool,因为onTouchBegan是必须实现的,否则将
接收不到任何触摸事件的通知,它的返回值是用来告诉EventDispatcher是否将该触摸点后续的触摸状态传递给订阅者,如果为
fasle,onTouchesMoved、onTouchesEnded、onTouchesCancelled将接收不到任何回调。

setSwallowTouches是用来设置是否阻止一个触摸点向后面的订阅者继续分发的。setSwallowTouches(true)即为阻止,例如通常两个按钮
不能同时处理一个触摸点。
在EventDispaycher内部,每次触摸事件,它首先将每个触摸点单独作用在每个EventListenerTouchOneByOne订阅者上面,然后再将所有触摸点集合作用在每个EventListenerTouchAllAtOnce订阅者上面的

注册订阅者:
可以用如下两种方法来注册一个订阅者
void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)  
可以先看下这两种方法的实现方式:
void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
{
CCASSERT(listener && node, "Invalid parameters.");
CCASSERT(!listener->isRegistered(), "The listener has been registered.");

if (!listener->checkAvailable())
return;

listener->setSceneGraphPriority(node);
listener->setFixedPriority(0);
listener->setRegistered(true);

addEventListener(listener);
}

void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)
{
CCASSERT(listener, "Invalid parameters.");
CCASSERT(!listener->isRegistered(), "The listener has been registered.");
CCASSERT(fixedPriority != 0, "0 priority is forbidden for fixed priority since it's used for scene graph based priority.");

if (!listener->checkAvailable())
return;

listener->setSceneGraphPriority(nullptr);
listener->setFixedPriority(fixedPriority);
listener->setRegistered(true);
listener->setPaused(false);

addEventListener(listener);
}


EventDispatcher分发事件的顺序如下:
priority<0,scene graph(priority=0),priority>0
首先分发优先级小于0的订阅者,按优先级从小到大分发,然后分发所有与node元素关联的订阅者,他们按照自身关联的node在ui场景中的层级从前往后分发,最后分发所有有限级大于0的订阅者。也是按照优先级从小到大的顺序分发。
其中的 addEventListenerWithSceneGraphPriority 的事件监听器优先级是 0 ;而且在 addEventListenerWithFixedPriority 中的事件监听器的优先级不可以设置为 0,因为这个是保留给 SceneGraphPriority
使用的

综上所述,我们来看如何实现各种触摸事件
步骤:新建精灵->新建监听器->按需求实现监听器的4个回调函数(onTouchBegan必须实现)->把精灵作为订阅者添加到监听器中
1.单点触摸例子
//新建精灵
auto label = LabelTTF::create("click me ","Courier",30);
label->setPosition(size.width/2,size.height/2);
addChild(label);
//新建监听器
auto listener = EventListenerTouchOneByOne::create();
//按需求实现监听器的4个回调函数(onTouchBegan必须实现)
listener->onTouchBegan = [label](Touch * t,Event *e){
if(e->getCurrentTarget()->getBoundingBox().containsPoint(t->getLocation())){
log("onTouchBegan");
}
//返回值如果是 false 后面的时间就不会再被触发 返回值是true TouchMoved End会被触发
return false;
};
listener->onTouchMoved = [](Touch *t ,Event *e){
log("onTouchMoved");
};
listener->onTouchEnded = [](Touch *t,Event *e){
log("onTouchEnded");
};
//把精灵作为订阅者添加到监听器中
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener,label);


2.多点触摸
  多点触摸实现过程与单点触摸类似
auto listener = EventListenerTouchAllAtOnce::create();
listener->onTouchesBegan=[](std::vector<Touch*> s,Event *e){
log("onTouchesBegan");
return true;
};
listener->onTouchesMoved=[](std::vector<Touch*> s,Event *e)
{

};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener,this);


3.加速度传感器
//加速度传感器默认是关闭的
Device::setAccelerometerEnabled(true);

//加速度传感器
Director::getInstance()->getEventDispatcher()->
addEventListenerWithSceneGraphPriority(
EventListenerAcceleration::create([](Acceleration *a,Event *e){
log("x:%g,y:%g,z:%g",a->x,a->y,a->z);
}),this);

4.物理按键
物理按键对手机来说通常就是指返回键,如下便是当手机按下返回键的时候进行的处理
auto listener = EventListenerKeyboard::create();
listener->onKeyReleased=[](EventKeyboard::KeyCode code,Event *e){
log("key code: %d",code);
switch (code)
{
case EventKeyboard::KeyCode::KEY_BACKSPACE:
Director::getInstance()->end();
break;
default:
break;
}
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener,this);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息