Cocos2d-X研究之v3.x 事件分发机制详解

事件分发机制

新事件分发机制:在2.x 版本事件处理时,将要触发的事件交给代理(delegate)处理,再通过实现代理里面的onTouchBegan等方法接收事件,最后完成事件的响应。而在新的事件分发机制中,只需通过创建一个事件监听器-用来实现各种触发后的逻辑,然后添加到事件分发器_eventDispatcher,所有事件监听器由这个分发器统一管理,即可完成事件响应。请参考更多3.0资料。。。

事件监听器有以下几种:

  • 触摸事件 (EventListenerTouch)
  • 键盘响应事件 (EventListenerKeyboard)
  • 鼠标响应事件 (EventListenerMouse)
  • 自定义事件 (EventListenerCustom)
  • 加速记录事件 (EventListenerAcceleration)

_eventDispatcher的工作由三部分组成:

  • 事件分发器 EventDispatcher
  • 事件类型 EventTouch, EventKeyboard 等
  • 事件监听器 EventListenerTouch, EventListenerKeyboard 等

监听器实现了各种触发后的逻辑,在适当时候由事件分发器分发事件类型,然后调用相应类型的监听器。

用户输入事件

触摸事件

在处理触摸事件时,既可以重写三个方法onTouchBegan,onTouchMoved和onTouchEnded,也可以直接通过Lambda表达式完成响应逻辑。

在2.x版本中,开启多点触摸需要在AppController.mm中的application didFinishLaunchingWithOptions:launchOptions中添加[__glView setMultipleTouchEnabled: YES],另外还需重载5个相应函数:

  • virtual void registerWithTouchDispatcher(void);
  • virtual void ccTouchesBegan(cocos2d::CCSet pTouches, cocos2d::CCEvent
    pEvent);
  • virtual void ccTouchesMoved(cocos2d::CCSet pTouches, cocos2d::CCEvent
    pEvent);
  • virtual void ccTouchesEnded(cocos2d::CCSet pTouches, cocos2d::CCEvent
    pEvent);
  • virtual void ccTouchesCancelled(cocos2d::CCSet pTouches, cocos2d::CCEvent
    pEvent);

而在3.0中,只需创建多点触摸事件监听器,并将其添加到事件分发器中即可。

以下代码在一个界面中添加三个按钮,三个按钮相互遮挡,并且都能触发触摸事件:

1

2

3

4

5

6

7

8

// 创建按钮精灵

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

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

addChild(sprite1,
10);

// sprite2

...

// sprite3

...

创建好按钮精灵后,创建单点触摸事件监听器,并完成相应逻辑处理

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

// 创建一个事件监听器类型为 OneByOne 的单点触摸

auto listener1
= EventListenerTouchOneByOne::create();

// 设置是否吞没事件,在 onTouchBegan 方法返回 true 时吞没

listener1->setSwallowTouches(true);

// 使用 lambda 实现 onTouchBegan 事件回调函数

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

// 获取事件所绑定的 target

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){...};

// 点击事件结束处理

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

最后需要将事件监听器添加到事件分发器

1

2

3

4

// 添加监听器

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,
sprite1);

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

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

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

注意:当再次使用 listener1 的时候,需要使用clone()方法创建一个新的克隆,因为在使用addEventListenerWithSceneGraphPriority或者addEventListenerWithFixedPriority方法时,会对当前使用的事件监听器添加一个已注册的标记,这使得它不能够被添加多次。另外,有一点非常重要,FixedPriority listener添加完之后需要手动remove,而SceneGraphPriority listener是跟Node绑定的,在Node的析构函数中会被移除。具体的示例用法可以参考引擎自带的tests。

我们可以通过以下方法移除一个已经被添加了的监听器。

_eventDispatcher->removeEventListener(listener);

也可以使用如下方法,移除当前事件分发器中所有监听器。

_eventDispatcher->removeAllEventListeners();

当使用removeAll的时候,此节点的所有的监听将被移除,推荐使用 指定删除的方式。removeAll之后菜单也不能响应。因为它也需要接受触摸事件。

键盘响应事件

键盘响应事件和处理触摸事件使用了相同的处理方式,一下代码演示如何处理键盘响应事件:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// 初始化并绑定

auto listener
= EventListenerKeyboard::create();

listener->onKeyPressed
= CC_CALLBACK_2(KeyboardTest::onKeyPressed,
this);

listener->onKeyReleased
= CC_CALLBACK_2(KeyboardTest::onKeyReleased,
this);

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,
this);

// 键位响应函数原型

void
KeyboardTest::onKeyPressed(EventKeyboard::KeyCode
keyCode,
Event* event)

{

log("Key with keycode %d pressed",
keyCode);

}

void
KeyboardTest::onKeyReleased(EventKeyboard::KeyCode
keyCode,
Event* event)

{

log("Key with keycode %d released",
keyCode);

}

鼠标响应事件

在 3.0 中多了鼠标捕获事件派发,这可以在不同的平台上,丰富我们游戏的用户体验。

下面代码实现鼠标响应事件的实现步骤:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// 创建监听器

_mouseListener
= EventListenerMouse::create();

// 时间响应逻辑

_mouseListener->onMouseMove
= [=](Event *event){

EventMouse*
e =
(EventMouse*)event;

string
str =
"Mouse Down detected, Key: ";

str
+= tostr(e->getMouseButton());

// ...

};

_mouseListener->onMouseUp
= [=](Event *event){...};

_mouseListener->onMouseDown
= [=](Event *event){...};

_mouseListener->onMouseScroll
= [=](Event *event){...};

// 添加到事件分发器

_eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener,
this);

自定义事件

以上是系统自带的事件类型,事件由系统内部自动触发,如 触摸屏幕,键盘响应等,除此之外,还提供了一种 自定义事件,简而言之,它不是由系统自动触发,而是人为的干涉,如下:

1

2

3

4

5

6

7

8

9

_listener =
EventListenerCustom::create("game_custom_event1",
[=](EventCustom*
event){

std::string
str("Custom event 1 received, ");

char*
buf =
static_cast<char*>(event->getUserData());

str
+= buf;

str
+= " times";

statusLabel->setString(str.c_str());

});

_eventDispatcher->addEventListenerWithFixedPriority(_listener,
1);

以上定义了一个 “自定义事件监听器”,实现了相关逻辑,并且添加到事件分发器。上面的自定义事件将由以下代码触发:

1

2

3

4

5

6

7

8

9

10

11

static
int count
= 0;

++count;

char*
buf =
new char[10];

sprintf(buf,
"%d",
count);

EventCustom
event("game_custom_event1");

event.setUserData(buf);

if(...)

{

_eventDispatcher->dispatchEvent(&event);

}

CC_SAFE_DELETE_ARRAY(buf);

定义一个 EventCustom,并且设置了其 UserData 数据,手动的通过 _eventDispatcher->dispatchEvent(&event); 将此事件分发出去,从而触发之前所实现的逻辑。

加速计事件

除了触摸,移动设备上一个很重要的输入源是设备的方向,因此大多数设备都配备了加速计,用于测量设备静止或匀速运动时所受到的重力方向。

重力感应来自移动设备的加速计,通常支持X,Y和Z三个方向的加速度感应,所以又称为三向加速计。在实际应用中,可以根据3个方向的力度大小来计算手机倾斜的角度或方向。

3.0中,新的事件机制下,我们需要通过创建一个加速计监听器EventListenerAcceleration,其静态create方法中有个Acceleration的参数需要注意。Acceleration是一个类,包含了加速计获得的3个方向的加速度,相关代码如下:

1

2

3

4

5

6

7

8

9

10

11

class
Acceleration

{

public:

double
x;

double
y;

double
z;

double
timestamp;

Acceleration():
x(0),
y(0),
z(0),
timestamp(0)
{}

};

该类中每个方向的加速度大小都为一个重力加速度大小。

在使用加速计事件监听器之前,需要先启用此硬件设备:

Device::setAccelerometerEnabled(true);

然后创建对应的监听器,在创建回调函数时,可以使用 lambda 表达式创建匿名函数,也可以绑定已有的函数逻辑实现,如下:

1

2

3

4

auto
listener =
EventListenerAcceleration::create([=](Acceleration*
acc,
Event* event){

//逻辑代码段

});

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,
this);

Cocos2d-X研究之v3.x 事件分发机制详解

时间: 2024-08-05 07:08:30

Cocos2d-X研究之v3.x 事件分发机制详解的相关文章

Android事件分发机制详解(1)----探究View的事件分发

探究View的事件分发 在Activity中,只有一个按钮,注册一个点击事件 [java] view plaincopy button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d("TAG", "onClick execute"); } }); 如果在需要一个触摸事件 [java] view plaincopy button.setO

Android事件分发机制详解(2)----分析ViewGruop的事件分发

首先,我们需要 知道什么是ViewGroup,它和普通的View有什么区别? ViewGroup就是一组View的集合,它包含很多子View和ViewGroup,是Android 所有布局的父类或间接父类. 但ViewGroup也是一个View,只不过比起View,它可以包含子View和定义布局参数的功能. 现在,通过一个Demo演示Android中ViewGroup的事件分发机制. 首先我们来自定义一个布局,命名为MyLayout,继承自LinearLayout,如下 所示: public c

Cocos2d-X研究之v3.x 事件分发机制具体解释

事件分发机制 " src="http://www.cgzhw.com/wp-content/uploads/2014/07/inherent3.png" style=""> 新事件分发机制:在2.x 版本号事件处理时.将要触发的事件交给代理(delegate)处理,再通过实现代理里面的onTouchBegan等方法接收事件.最后完毕事件的响应.而在新的事件分发机制中.仅仅需通过创建一个事件监听器-用来实现各种触发后的逻辑.然后加入到事件分发器_eve

Cocos2d-X 3.x 事件分发机制详解

事件分发机制 新事件分发机制:在2.x 版本事件处理时,将要触发的事件交给代理(delegate)处理,再通过实现代理里面的onTouchBegan等方法接收事件,最后完成事件的响应.而在新的事件分发机制中,只需通过创建一个事件监听器-用来实现各种触发后的逻辑,然后添加到事件分发器_eventDispatcher,所有事件监听器由这个分发器统一管理,即可完成事件响应.请参考更多3.0资料... 事件监听器有以下几种: 触摸事件 (EventListenerTouch) 键盘响应事件 (Event

android 事件分发机制详解(OnTouchListener,OnClick)

昨天做东西做到触摸事件冲突,以前也经常碰到事件冲突,想到要研究一下Android的事件冲突机制,于是从昨天开始到今天整整一天时间都要了解这方面的知识,这才懂了安卓的触摸和点击事件的机制.探究如下: 首先重写三个View布局,用来做测试: package com.example.yzj.android_8_10; import android.content.Context; import android.util.AttributeSet; import android.util.Log; im

Android事件分发机制详解:史上最全面、最易懂

前言 Android事件分发机制是每个Android开发者必须了解的基础知识 网上有大量关于Android事件分发机制的文章,但存在一些问题:内容不全.思路不清晰.无源码分析.简单问题复杂化等等 今天,我将全面总结Android的事件分发机制,我能保证这是市面上的最全面.最清晰.最易懂的 本文秉着"结论先行.详细分析在后"的原则,即先让大家感性认识,再通过理性分析从而理解问题: 所以,请各位读者先记住结论,再往下继续看分析: 文章较长,阅读需要较长时间,建议收藏等充足时间再进行阅读 目

Android6.0 ViewGroup/View 事件分发机制详解

之前自认为对于Android的事件分发机制还算比较了解,直到前一阵偶然跟人探讨该问题,才发现自己以前的理解有误,惭愧之余遂决定研习源码,彻底弄明白Android的事件分发机制,好了废话少说,直接开干. 首先,我们对Android中的touch事件做一下总结,主要分为以下几类: 1.Action_Down  用户手指触碰到屏幕的那一刻,会触发该事件: 2.Action_Move   在触碰到屏幕之后,手指开始在屏幕上滑动,会触发Action_Move事件: 3.Action_Up       在用

Android事件分发机制详解

我们通过一个示例来分析Touch事件的分发过程. 示例: 布局文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id=&

Android 事件分发机制详解

更多内容请参照我的个人站点: http://stackvoid.com/ 网上很多关于Android事件分发机制的解释,大多数描述的都不够清晰,没有吧来龙去脉搞清楚,本文将带你从Touch事件产生到Touch事件被消费这一全过程作全面的剖析. 产生Touch事件 这部分牵扯到硬件和Linux内核部分:我们简单讲述一下这部分内容,如果有兴趣的话可以参考这篇文章. 传递Touch事件 触摸事件是由Linux内核的一个Input子系统来管理的(InputManager),Linux子系统会在/dev/