cocos2dx触屏事件详细教程

2015年03月18日 10:21 0 点赞 0 评论 更新于 2025-11-21 14:03

本教程将详细介绍Cocos2d-x 2.x版本在iOS平台上的触屏事件处理机制。

1. 应用启动流程

1.1 main.m文件

在iOS系统中,应用启动时会执行main.m文件中的代码,以下是创建iOS应用的关键代码:

// 创建一个iOS应用
int retVal = UIApplicationMain(argc, argv, nil, @"AppController");

iOS系统会调用AppControllerdidFinishLaunchingWithOptions函数,该函数内部会进行一些界面创建的操作,其中包含如下代码:

cocos2d::CCApplication::sharedApplication()->run();

需要注意的是,.mm文件是Objective-C与C++混编文件的命名方式。在AppController.mm文件中,会创建一个AppDelegate对象,AppDelegate继承于CCApplicationcocos2d::CCApplication::sharedApplication()获取的就是该对象:

static AppDelegate s_sharedApplication;

1.2 CCApplication::run()函数

进入CCApplication::run()函数,其代码如下:

int CCApplication::run()
{
if (applicationDidFinishLaunching())
{
[[CCDirectorCaller sharedDirectorCaller] startMainLoop];
}
return 0;
}

1.3 AppDelegate::applicationDidFinishLaunching函数

接着进入AppDelegate::applicationDidFinishLaunching函数,部分代码省略:

bool AppDelegate::applicationDidFinishLaunching()
{
// 初始化导演
CCDirector *pDirector = CCDirector::sharedDirector();
pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
return true;
}

1.4 CCDirector::setOpenGLView函数

进入CCDirector::setOpenGLView函数:

void CCDirector::setOpenGLView(CCEGLView *pobOpenGLView)
{
// ...
m_pobOpenGLView->setTouchDelegate(m_pTouchDispatcher); // 设置触摸代理,只是对CCEGLViewProtocol中EGLTouchDelegate* m_pDelegate;变量初始化
m_pTouchDispatcher->setDispatchEvents(true); // 设置接受派发事件
}

CCDirector::init()函数中,已经对Cocos2d-x引擎用到的变量进行了一些初始化,其中包括CCTouchDispatcher的创建和初始化:

m_pTouchDispatcher = new CCTouchDispatcher();
m_pTouchDispatcher->init();

2. 从iOS系统获取触摸事件

为了便于针对OpenGL ES进行编程,苹果公司提供了派生于类UIView的类EAGLView来实现OpenGL的输出支持。在EAGLView.mm中,以下函数会接收到iOS系统发送过来的触屏事件:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

这些函数内部分别调用了:

cocos2d::CCEGLView::sharedOpenGLView()->handleTouchesBegin(i, ids, xs, ys);
cocos2d::CCEGLView::sharedOpenGLView()->handleTouchesMove(i, ids, xs, ys);
cocos2d::CCEGLView::sharedOpenGLView()->handleTouchesEnd(i, ids, xs, ys);
cocos2d::CCEGLView::sharedOpenGLView()->handleTouchesCancel(i, ids, xs, ys);

进入CCEGLViewProtocol的这些函数,里面分别调用了:

m_pDelegate->touchesBegan(&set, NULL);
m_pDelegate->touchesMoved(&set, NULL);
m_pDelegate->touchesEnded(&set, NULL);
m_pDelegate->touchesCancelled(&set, NULL);

之前已经对m_pDelegate进行了赋值:

m_pTouchDispatcher = new CCTouchDispatcher();
m_pobOpenGLView->setTouchDelegate(m_pTouchDispatcher);

这样,CCTouchDispatcher就能接收到iOS系统发送过来的触屏事件了。

3. CCTouchDispatcher处理触摸事件

3.1 事件分发函数

CCTouchDispatcher的以下函数会根据m_bDispatchEvents的值来调用touches函数:

void CCTouchDispatcher::touchesBegan(CCSet *touches, CCEvent *pEvent)
{
if (m_bDispatchEvents)
{
this->touches(touches, pEvent, CCTOUCHBEGAN);
}
}

void CCTouchDispatcher::touchesMoved(CCSet *touches, CCEvent *pEvent)
{
if (m_bDispatchEvents)
{
this->touches(touches, pEvent, CCTOUCHMOVED);
}
}

void CCTouchDispatcher::touchesEnded(CCSet *touches, CCEvent *pEvent)
{
if (m_bDispatchEvents)
{
this->touches(touches, pEvent, CCTOUCHENDED);
}
}

void CCTouchDispatcher::touchesCancelled(CCSet *touches, CCEvent *pEvent)
{
if (m_bDispatchEvents)
{
this->touches(touches, pEvent, CCTOUCHCANCELLED);
}
}

3.2 touches函数

void CCTouchDispatcher::touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex)
{
CCAssert(uIndex >= 0 && uIndex < 4, "");
CCSet *pMutableTouches;
m_bLocked = true;

// 优化,避免不必要的可变副本
unsigned int uTargetedHandlersCount = m_pTargetedHandlers->count();
unsigned int uStandardHandlersCount = m_pStandardHandlers->count();
bool bNeedsMutableSet = (uTargetedHandlersCount && uStandardHandlersCount);
pMutableTouches = (bNeedsMutableSet ? pTouches->mutableCopy() : pTouches);

struct ccTouchHandlerHelperData sHelper = m_sHandlerHelperData[uIndex];

// 处理单点触摸
if (uTargetedHandlersCount > 0)
{
CCTouch *pTouch;
CCSetIterator setIter;
for (setIter = pTouches->begin(); setIter != pTouches->end(); ++setIter)
{
pTouch = (CCTouch *)(*setIter);
CCTargetedTouchHandler *pHandler = NULL;
CCObject* pObj = NULL;
// 从CCTargetedTouchHandler的数组中取出单个CCTargetedTouchHandler对象
CCARRAY_FOREACH(m_pTargetedHandlers, pObj)
{
pHandler = (CCTargetedTouchHandler *)(pObj);
if (!pHandler)
{
break;
}
bool bClaimed = false;
if (uIndex == CCTOUCHBEGAN)
{
// 这里调用CCTargetedTouchHandler对象的ccTouchBegin方法,我们知道Cocos2d-x中layer为接受触屏事件的最小单位
bClaimed = pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent);
// 如果ccTouchBegin方法返回true,会把这个CCTargetedTouchHandler对象加入到处理move、end、canceled的数组中
// 意思就是如果ccTouchBegin方法返回true,该CCTargetedTouchHandler对象才会继续接受到move、end、canceled这三个事件
if (bClaimed)
{
pHandler->getClaimedTouches()->addObject(pTouch);
}
}
else if (pHandler->getClaimedTouches()->containsObject(pTouch))
{
// moved、ended、canceled
bClaimed = true;
switch (sHelper.m_type)
{
case CCTOUCHMOVED:
pHandler->getDelegate()->ccTouchMoved(pTouch, pEvent);
break;
case CCTOUCHENDED:
pHandler->getDelegate()->ccTouchEnded(pTouch, pEvent);
pHandler->getClaimedTouches()->removeObject(pTouch);
break;
case CCTOUCHCANCELLED:
pHandler->getDelegate()->ccTouchCancelled(pTouch, pEvent);
pHandler->getClaimedTouches()->removeObject(pTouch);
break;
}
}
// 如果有CCTargetedTouchHandler对象接受到这个触摸事件了,如果设置为吞并事件,不向下传递了,这里就会把触屏事件删除
if (bClaimed && pHandler->isSwallowsTouches())
{
if (bNeedsMutableSet)
{
pMutableTouches->removeObject(pTouch);
}
break;
}
}
}
}

// 处理多点触摸
if (uStandardHandlersCount > 0 && pMutableTouches->count() > 0)
{
CCStandardTouchHandler *pHandler = NULL;
CCObject* pObj = NULL;
CCARRAY_FOREACH(m_pStandardHandlers, pObj)
{
pHandler = (CCStandardTouchHandler*)(pObj);
if (!pHandler)
{
break;
}
switch (sHelper.m_type)
{
case CCTOUCHBEGAN:
pHandler->getDelegate()->ccTouchesBegan(pMutableTouches, pEvent);
break;
case CCTOUCHMOVED:
pHandler->getDelegate()->ccTouchesMoved(pMutableTouches, pEvent);
break;
case CCTOUCHENDED:
pHandler->getDelegate()->ccTouchesEnded(pMutableTouches, pEvent);
break;
case CCTOUCHCANCELLED:
pHandler->getDelegate()->ccTouchesCancelled(pMutableTouches, pEvent);
break;
}
}
}

// 多点触摸是否释放
if (bNeedsMutableSet)
{
pMutableTouches->release();
}

// 优化,避免昂贵的[handlers copy],add/removes/quit操作在迭代之后进行
m_bLocked = false;
if (m_bToRemove)
{
m_bToRemove = false;
for (unsigned int i = 0; i < m_pHandlersToRemove->num; ++i)
{
forceRemoveDelegate((CCTouchDelegate*)m_pHandlersToRemove->arr[i]);
}
ccCArrayRemoveAllValues(m_pHandlersToRemove);
}
if (m_bToAdd)
{
m_bToAdd = false;
CCTouchHandler* pHandler = NULL;
CCObject* pObj = NULL;
CCARRAY_FOREACH(m_pHandlersToAdd, pObj)
{
pHandler = (CCTouchHandler*)pObj;
if (!pHandler)
{
break;
}
if (dynamic_cast<CCTargetedTouchHandler*>(pHandler) != NULL)
{
forceAddHandler(pHandler, m_pTargetedHandlers);
}
else
{
forceAddHandler(pHandler, m_pStandardHandlers);
}
}
m_pHandlersToAdd->removeAllObjects();
}
if (m_bToQuit)
{
m_bToQuit = false;
forceRemoveAllDelegates();
}
}

4. m_pTargetedHandlers变量添加对象

4.1 CCLayer::setTouchEnabled函数

CCLayer函数中,setTouchEnabled函数的代码如下:

void CCLayer::setTouchEnabled(bool enabled)
{
if (enabled)
{
// true,注册
this->registerWithTouchDispatcher();
}
else
{
// 传进来的是false,删除
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
}
}

4.2 CCLayer::registerWithTouchDispatcher函数

void CCLayer::registerWithTouchDispatcher()
{
CCTouchDispatcher* pDispatcher = CCDirector::sharedDirector()->getTouchDispatcher();
// Using LuaBindings用于lua的
if (m_pScriptTouchHandlerEntry)
{
if (m_pScriptTouchHandlerEntry->isMultiTouches())
{
pDispatcher->addStandardDelegate(this, 0);
LUALOG("[LUA] Add multi-touches event handler: %d", m_pScriptTouchHandlerEntry->getHandler());
}
else
{
pDispatcher->addTargetedDelegate(this,
m_pScriptTouchHandlerEntry->getPriority(),
m_pScriptTouchHandlerEntry->getSwallowsTouches());
LUALOG("[LUA] Add touch event handler: %d", m_pScriptTouchHandlerEntry->getHandler());
}
}
else
{
if (m_eTouchMode == kCCTouchesAllAtOnce)
{
pDispatcher->addStandardDelegate(this, 0);
}
else
{
pDispatcher->addTargetedDelegate(this, m_nTouchPriority, true); // 把当前layer或layer的子类加入,触摸优先级,接受到触摸事件是否吞并,不向该优先级以下传递
}
}
}

4.3 CCTouchDispatcher::addTargetedDelegate函数

void CCTouchDispatcher::addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches)
{
CCTouchHandler *pHandler = CCTargetedTouchHandler::handlerWithDelegate(pDelegate, nPriority, bSwallowsTouches);
if (!m_bLocked)
{
forceAddHandler(pHandler, m_pTargetedHandlers);
}
}

4.4 CCTouchDispatcher::forceAddHandler函数

void CCTouchDispatcher::forceAddHandler(CCTouchHandler *pHandler, CCArray *pArray)
{
unsigned int u = 0;
CCObject* pObj = NULL;
// 遍历数组
CCARRAY_FOREACH(pArray, pObj)
{
CCTouchHandler *h = (CCTouchHandler *)pObj;
if (h)
{
// 比较优先级,设置插入的位置
if (h->getPriority() < pHandler->getPriority())
{
++u;
}
if (h->getDelegate() == pHandler->getDelegate())
{
CCAssert(0, "");
return;
}
}
}
// 向该数组中加入该CCTouchHandler *pHandler
pArray->insertObject(pHandler, u);
}

5. 实现自定义触摸函数

以下是一个自定义UILayer类的示例,用于实现自定义的触摸函数:

class UILayer : public CCLayer
{
public:
UILayer();
~UILayer();

virtual bool init();
virtual void onEnter();
virtual void onExit();
virtual void registerWithTouchDispatcher();
virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
virtual void ccTouchCancelled(CCTouch* touch, CCEvent* event);
virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);

CREATE_FUNC(UILayer);
};

UILayer::UILayer()
{
}

UILayer::~UILayer()
{
}

bool UILayer::init()
{
if (!CCLayer::init())
{
return false;
}
return true;
}

void UILayer::onEnter()
{
CCLayer::onEnter();
setTouchEnabled(true);
}

void UILayer::onExit()
{
CCLayer::onExit();
}

void UILayer::registerWithTouchDispatcher()
{
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, ROR::TOUCH_UI_PRIORITY, true); // 默认吞噬触摸事件
}

bool UILayer::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
CCLOG("ccTouchBegan");
return true;
}

void UILayer::ccTouchEnded(CCTouch* touch, CCEvent* event)
{
CCLOG("ccTouchEnded");
}

void UILayer::ccTouchCancelled(CCTouch* touch, CCEvent* event)
{
CCLOG("ccTouchCancelled");
}

void UILayer::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
CCLOG("ccTouchMoved");
}

通过以上步骤,我们详细了解了Cocos2d-x 2.x版本在iOS平台上的触屏事件调用流程,以及如何实现自定义的触摸函数。

作者信息

feifeila

feifeila

共发布了 3994 篇文章