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

Cocos2d-x 3.0 新特性体验-触摸事件处理机制

2014-04-09 15:20 453 查看
在cocos2d-x 2.x版本中,相信大家都抱怨过其中的触摸机制;在3.0版本中,采用了全新的触摸事件处理机制。

在官方的文档中:点击打开链接 这篇文章有对新的事件分发机制的介绍。

下面,我将通过引擎中自带的sample来探索一下这个新的触摸事件处理机制。

注:例子来自Test cpp/NewEventDispatcherTest

一、例子1



(1)创建三个精灵

[cpp] view
plaincopy





auto sprite1 = Sprite::create("Images/CyanSquare.png");

sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 80));

addChild(sprite1, 10); //其中 10 表示 zOreder



auto sprite2 = Sprite::create("Images/MagentaSquare.png");

sprite2->setPosition(origin+Point(size.width/2, size.height/2));

addChild(sprite2, 20);



auto sprite3 = Sprite::create("Images/YellowSquare.png");

sprite3->setPosition(Point(0, 0));

sprite2->addChild(sprite3, 1); //注意 sprite3 是添加到 sprite2 上的

(2)创建一个单点触摸事件监听器,处理触摸事件逻辑

[cpp] view
plaincopy





// Make sprite1 touchable

auto listener1 = EventListenerTouchOneByOne::create();//创建一个触摸监听

listener1->setSwallowTouches(true); //设置是否想下传递触摸



//通过 lambda 表达式 直接实现触摸事件的回掉方法

listener1->onTouchBegan = [](Touch* touch, Event* event){

auto target = static_cast<Sprite*>(event->getCurrentTarget());



Point locationInNode = target->convertToNodeSpace(touch->getLocation());

Size s = target->getContentSize();

Rect rect = Rect(0, 0, s.width, s.height);



if (rect.containsPoint(locationInNode))

{

log("sprite began... x = %f, y = %f", locationInNode.x, locationInNode.y);

target->setOpacity(180);

return true;

}

return false;

};



listener1->onTouchMoved = [](Touch* touch, Event* event){

auto target = static_cast<Sprite*>(event->getCurrentTarget());

target->setPosition(target->getPosition() + touch->getDelta());

};



listener1->onTouchEnded = [=](Touch* touch, Event* event){

auto target = static_cast<Sprite*>(event->getCurrentTarget());

log("sprite onTouchesEnded.. ");

target->setOpacity(255);

if (target == sprite2)

{

sprite1->setZOrder(100);

}

else if(target == sprite1)

{

sprite1->setZOrder(0);

}

};



_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, sprite1);

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2);

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite3);

①其中的触摸监听类型为:EventListenerTouchOneByOne 表示的是单点触摸;而EventListenerTouchAllAtOnce 表示的就是多点触摸。

[cpp] view
plaincopy





class EventListenerTouchOneByOne : public EventListener

{

public:

static const std::string LISTENER_ID;



static EventListenerTouchOneByOne* create();



virtual ~EventListenerTouchOneByOne();



void setSwallowTouches(bool needSwallow);



/// Overrides

virtual EventListenerTouchOneByOne* clone() override;

virtual bool checkAvailable() override;

//



public:

std::function<bool(Touch*, Event*)> onTouchBegan;

std::function<void(Touch*, Event*)> onTouchMoved;

std::function<void(Touch*, Event*)> onTouchEnded;

std::function<void(Touch*, Event*)> onTouchCancelled;



private:

EventListenerTouchOneByOne();

bool init();



std::vector<Touch*> _claimedTouches;

bool _needSwallow;



friend class EventDispatcher;

};





class 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:

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

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

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

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



private:

EventListenerTouchAllAtOnce();

bool init();

private:



friend class EventDispatcher;

};

看起来很是熟悉吧,和cocos2dx 2.x 版本中的 target touch 和 standard touch 差不多吧!只是使用的形式不太一样罢了。还有在3.0版本中,不需要注册触摸事件代理delegate了。

② _eventDispatcher

事件监听器包含以下几种:

触摸事件 (EventListenerTouch)

键盘响应事件 (EventListenerKeyboard)

加速记录事件 (EventListenerAcceleration)

鼠标响应事件 (EventListenerMouse)

自定义事件 (EventListenerCustom)

以上事件监听器统一由
_eventDispatcher
来进行管理。

_eventDispatcher 是 Node 的属性,通过它管理当前节点(如
场景 、层、精灵等 )的所有事件分发情况。但是它本身是一个单例模式值的引用,在 Node 构造函数中,通过 "Director::getInstance()->getEventDispatcher();" 获取,有了这个属性,我们能更为方便的调用。

有两种方式将 事件监听器 listener1 添加到 事件调度器_eventDispatcher 中:

[cpp] view
plaincopy





void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)

void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)

看看这两种方式的实现代码:

[cpp] view
plaincopy





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);

}

从中我们可以知道: 其中的 addEventListenerWithSceneGraphPriority 的事件监听器优先级是 0 ;而且在 addEventListenerWithFixedPriority 中的事件监听器的优先级不可以设置为 0,因为这个是保留给 SceneGraphPriority
使用的。

注意:(1) 这里当我们再次使用 listener1 的时候,需要使用
clone()
方法创建一个新的克隆,因为在使用
addEventListenerWithSceneGraphPriority
或者
addEventListenerWithFixedPriority
方法时,会对当前使用的事件监听器添加一个已注册的标记,这使得它不能够被添加多次。

看看clone()方法的代码:

[cpp] view
plaincopy





EventListenerTouchOneByOne* EventListenerTouchOneByOne::clone()

{

auto ret = new EventListenerTouchOneByOne();

if (ret && ret->init())

{

ret->autorelease();



ret->onTouchBegan = onTouchBegan;

ret->onTouchMoved = onTouchMoved;

ret->onTouchEnded = onTouchEnded;

ret->onTouchCancelled = onTouchCancelled;



ret->_claimedTouches = _claimedTouches;

ret->_needSwallow = _needSwallow;

}

else

{

CC_SAFE_DELETE(ret);

}

return ret;

}

(2)另外,有一点非常重要,FixedPriority listener添加完之后需要手动remove,而SceneGraphPriority listener是跟node绑定的,在node的析构函数中会被移除。

[cpp] view
plaincopy





_eventDispatcher->cleanTarget(this);

CC_SAFE_RELEASE(_eventDispatcher);

二、例子2

在上面的例子中,使用的是 addEventListenerWithSceneGraphPriority 添加触摸监听器,也就是单点触摸。其结点的触摸优先级都是相同的 0 。那么上层的结点 是比 下层的结点 先处理触摸事件的。

下面看看如何使用 addEventListenerWithFixedPriority 自定义结点的触摸优先级。



(1)首先自定义精灵,其中可以设置精灵接受触摸的优先级。

[cpp] view
plaincopy





class TouchableSpriteWithFixedPriority : public Sprite

{

public:



CREATE_FUNC(TouchableSpriteWithFixedPriority);



TouchableSpriteWithFixedPriority()

: _listener(nullptr)

, _fixedPriority(0)

, _useNodePriority(false)

{

}



void setPriority(int fixedPriority) { _fixedPriority = fixedPriority; _useNodePriority = false; };

void setPriorityWithThis(bool useNodePriority) { _useNodePriority = useNodePriority; _fixedPriority = true; }



void onEnter() override

{

Sprite::onEnter();



auto listener = EventListenerTouchOneByOne::create();

listener->setSwallowTouches(true);



listener->onTouchBegan = [=](Touch* touch, Event* event){



Point locationInNode = this->convertToNodeSpace(touch->getLocation());

Size s = this->getContentSize();

Rect rect = Rect(0, 0, s.width, s.height);



if (rect.containsPoint(locationInNode))

{

this->setColor(Color3B::RED);

return true;

}

return false;

};



listener->onTouchMoved = [=](Touch* touch, Event* event){

//this->setPosition(this->getPosition() + touch->getDelta());

};



listener->onTouchEnded = [=](Touch* touch, Event* event){

this->setColor(Color3B::WHITE);

};



if (_useNodePriority)

{

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

}

else

{

_eventDispatcher->addEventListenerWithFixedPriority(listener, _fixedPriority);

}

_listener = listener;

}



void onExit() override

{

_eventDispatcher->removeEventListener(_listener);



Sprite::onExit();

}



private:

EventListener* _listener;

int _fixedPriority;

bool _useNodePriority;

};

(2)分别创建三个精灵,可以自定义设置每一个精灵的触摸优先级。注意:优先级值小的,接受触摸优先。

[cpp] view
plaincopy





auto sprite1 = TouchableSpriteWithFixedPriority::create();

sprite1->setTexture("Images/CyanSquare.png");

sprite1->setPriority(30);

sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 40));

addChild(sprite1, 10);



auto sprite2 = TouchableSpriteWithFixedPriority::create();

sprite2->setTexture("Images/MagentaSquare.png");

sprite2->setPriority(20);

sprite2->setPosition(origin+Point(size.width/2, size.height/2));

addChild(sprite2, 20);



auto sprite3 = TouchableSpriteWithFixedPriority::create();

sprite3->setTexture("Images/YellowSquare.png");

sprite3->setPriority(10);

sprite3->setPosition(Point(0, 0));

sprite2->addChild(sprite3, 1);

三、删除触摸监听器的方法:

[cpp] view
plaincopy





/** Remove a listener

* @param listener The specified event listener which needs to be removed.

*/

void removeEventListener(EventListener* listener);



/** Removes all listeners with the same event listener type */

void removeEventListeners(EventListener::Type listenerType);

前者只是删除某一个事件监听器,而后者是删除某一类事件监听器(使用了 clone 克隆)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: