Cocos2dx学习笔记(12)——触摸事件2.x

这里呢,我们来讲讲2.x版本如何让游戏与人进行交互。

本文仅供个人记录和复习,不用于其他用途

2.x与3.x

由于2.x版本和3.x版本的用法不一样,这里我就分开的讲一下,3.x版本可以去看后面的事件分发机制。注意,3.x版本是不能使用2.x的方式来完成触摸。

什么是触摸

说到底,Cocos2dx还是用来做手机游戏的,既然使用手机玩游戏,就少不了在屏幕上点来点去。无论是界面上的按钮,还是操控人物的移动,我们都需要用到触摸事件。在2.x版本事件处理时,将要触发的事件交给代理delegate处理,再通过实现代理里面的onTouchBegan等方法接收事件,最后完成事件的响应。触摸事件主要分为单点触摸多点触摸,Cocos2dx默认只有CCLayer和其派生类才有触摸功能。

CCTouch类

CCTouch类是用来存储用户触摸屏幕的过程中,其触摸点的相关信息。也就是保存了触碰的整个过程中,手指所在的位置坐标。常用操作如下:

1
2
3
4
5
6
7
class CCTouch : public CCObject
{
CCPoint getLocation(); // 返回当前触点的坐标
CCPoint getPreviousLocation(); // 返回前一个触点的坐标
CCPoint getStartLocation(); // 返回开始触碰时的坐标
CCPoint getDelta(); // 返回最近两个触点的偏移量坐标
};

单点触摸

首先,我们需要注册触碰委托,单点触碰的注册委托函数为addTargetedDelegate()

1
2
3
4
5
6
7
8
9
10
11
// 开启单点触碰TargetedDelegate
// 注册触碰:addTargetedDelegate("触碰事件委托的对象","优先级","是否拦截触屏事件");
// 优先级的值越小,就越高越早被响应
// 当第3个参数为true时,表示对本次触屏事件进行拦截,也就是说当触屏事件响应了本次触屏委托后,将不会再继续响应其他触碰委托
void HelloWorld::onEnter()
{
// 注册触碰响应事件
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,false);
CCLayer::onEnter();// 一定不要忘了调用父类的onEnter
}

除了开启触碰,我们还要编写关闭触摸:

1
2
3
4
5
6
void HelloWorld::onExit()
{
// 注销触屏响应事件
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
CCLayer::onExit();// 一定不要忘了调用父类的onExit
}

接下来呢,就是写最重要的部分,用于响应触摸事件:

1
2
3
4
virtual bool ccTouchBegan(CCTouch* touch,CCEvent* event); // 手指碰到屏幕时调用
virtual void ccTouchMoved(CCTouch* touch,CCEvent* event); // 手指在屏幕上滑动时调用
virtual void ccTouchEnded(CCTouch* touch,CCEvent* event); // 手指离开屏幕是调用
virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent); // 取消触碰

需要注意的是,ccTouchBegan的返回值若为true,表示继续响应ccTouchMovedccTouchEnded事件,返回false则反之。

单点触摸代码实现

我使用的是3.x版本,没有办法使用2.x的方法,这里便借用别人的代码。首先声明触碰响应函数,以及类的生命周期:

1
2
3
4
5
6
7
8
// 触屏事件
virtual bool ccTouchBegan(CCTouch* touch,CCEvent* event);
virtual void ccTouchMoved(CCTouch* touch,CCEvent* event);
virtual void ccTouchEnded(CCTouch* touch,CCEvent* event);
// 生命周期
virtual void onEnter();
virtual void onExit();

编写开启和关闭触碰事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 开启触屏监听
void HelloWorld::onEnter()
{
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,false);
CCLayer::onEnter(); // 一定不要忘了
}
// 关闭触屏监听
void HelloWorld::onExit()
{
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
CCLayer::onExit(); // 一定不要忘了
}

init()中创建一个CCSprite精灵,用于测试触碰:

1
2
3
CCSprite* sp = CCSprite::create("Icon.png");
sp->setPosition( midPos );
this->addChild(sp, 0, 1); // tag标记为1

实现触碰响应函数:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 触屏开始ccTouchBegan
bool HelloWorld::ccTouchBegan(CCTouch* touch,CCEvent * event)
{
CCLOG("ccTouchBegan");
// 获取CCSprite精灵
CCSprite* sp = (CCSprite*)this->getChildByTag(1);
// 设置精灵的坐标为: 当前触点位置
CCPoint pTouch = touch->getLocation();
sp->setPosition( pTouch );
return true;
}
// 触屏移动ccTouchMoved
void HelloWorld::ccTouchMoved(CCTouch* touch,CCEvent* event)
{
CCLOG("ccTouchMoved");
// 获取可视区域尺寸大小
CCSize mysize = CCDirector::sharedDirector()->getVisibleSize();
// 获取CCSprite精灵
CCSprite* sp = (CCSprite*)this->getChildByTag(1);
// 实现精灵的触屏移动
CCPoint pos = touch->getDelta(); // 获得触屏滑动的偏移量
CCPoint currentPos = sp->getPosition(); // 获得精灵的当前坐标
currentPos = ccpAdd(currentPos, pos); // 精灵+偏移量 后的坐标
sp->setPosition(currentPos); // 设置触屏移动后的坐标
}
// 触屏结束ccTouchEnded
void HelloWorld::ccTouchEnded(CCTouch* touch,CCEvent* event)
{
CCLOG("ccTouchEnded");
// 获取CCSprite精灵
CCSprite* sp = (CCSprite*)this->getChildByTag(1);
// 设置精灵的坐标为: 触屏开始时的触点位置
CCPoint touchStartPos = touch->getStartLocation();
sp->setPosition( touchStartPos );
}

多点触摸

注意,多点触摸的注册不能放在onEnter()中,应该重写registerWithTouchDispatcher()方法:

1
2
3
4
void HelloWorld::registerWithTouchDispatcher()
{
CCDirector::sharedDirector()->getTouchDispatcher()->addStandardDelegate(this,0);
}

默认情况下CCLayer是没有开启触摸事件的,需要init()方法中开启:

1
setTouchEnabled(true);

同样的,多点触摸的关闭也是onExit()方法:

1
2
3
4
5
6
void HelloWorld::onExit()
{
// 关闭触碰
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
CCLayer::onExit();
}

至于多点触摸的响应函数,除了onTouchBegan()没有返回值以外,都是一样的:

1
2
3
4
virtual void ccTouchesBegan(CCSet* touches, CCEvent* event); // 触碰开始
virtual void ccTouchesMoved(CCSet* touches, CCEvent* event); // 触碰移动
virtual void ccTouchesEnded(CCSet* touches, CCEvent* event); // 触碰结束
virtual void ccTouchesCancelled(CCSet *pTouches, CCEvent *pEvent); // 取消多点触屏

CCSet类

CCSet类用于存储多点触碰的触摸点CCTouch集合,使用迭代器CCSetIterator进行CCSet集合中多个触点CCTouch的遍历:

1
2
3
4
5
6
for (CCSetIterator iter = touches->begin(); iter != touches->end(); iter++)
{
// 获取触点后就和单点一样的处理
CCTouch* touch = (CCTouch*)(*iter);
// 填写自己的代码
}

多点触摸代码实现

这个只能在手机上面显示,电脑只有一个鼠标,所以无法看到效果:

1
2
3
4
5
6
7
8
// 触屏事件
virtual void registerWithTouchDispatcher(void);
virtual void ccTouchesBegan(CCSet* touches,CCEvent* event);
virtual void ccTouchesMoved(CCSet* touches,CCEvent* event);
virtual void ccTouchesEnded(CCSet* touches,CCEvent* event);
// 生命周期
virtual void onExit();

编写开启和关闭多点触碰事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 在init中开启多点触碰
bool HelloWorld::init()
{
// 填写自己的代码
// 开启多点触屏。注意这句话必须要写,否则无法多点触屏
this->setTouchEnabled(true);
}
// 注册多点触屏
void HelloWorld::registerWithTouchDispatcher()
{
CCDirector::sharedDirector()->getTouchDispatcher()->addStandardDelegate(this,0);
}
// 注销多点触屏
void HelloWorld::onExit()
{
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
CCLayer::onExit();
}

创建两个CCSprite,用于测试触碰:

1
2
3
4
5
6
7
8
// 添加两个CCSprite精灵,用于多点触屏
CCSprite* sp1 = CCSprite::create("Icon.png");
sp1->setPosition( ccp( 100, 160 ) );
this->addChild(sp1, 0, 1);
CCSprite* sp2 = CCSprite::create("Icon_gray.png");
sp2->setPosition( ccp( 200, 160) );
this->addChild(sp2, 0, 2);

将精灵的位置设置到触点位置,sp1精灵设置到第0个触点位置;sp2精灵设置到第1个触点位置:

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
31
32
33
34
35
36
37
38
// 触屏开始,返回值是void
void HelloWorld::ccTouchesBegan(CCSet* touches,CCEvent* event)
{
CCLOG("ccTouchesBegan");
}
// 触屏移动
void HelloWorld::ccTouchesMoved(CCSet* touches,CCEvent* event)
{
CCLOG("ccTouchesMoved");
// 创建CCSet的迭代器CCSetIterator
CCSetIterator iter = touches->begin();
// 遍历多点触点集合touches
for( ;iter != touches->end(); iter++)
{
// 获取触点后,就和单点一样的处理了
CCTouch* touch = (CCTouch*)(*iter);
if( touch->getID() == 0) // 控制精灵sp1
{
CCSprite* sp1 = (CCSprite*)this->getChildByTag(1);
sp1->setPosition( touch->getLocation() );
}
else if( touch->getID() == 1) // 控制精灵sp2
{
CCSprite* sp2 = (CCSprite*)this->getChildByTag(2);
sp2->setPosition( touch->getLocation() );
}
}
}
// 触屏结束
void HelloWorld::ccTouchesEnded(CCSet* touches,CCEvent* event)
{
CCLOG("ccTouchesEnded");
}