基本元素:
- 事件监听器( EventListener):负责接收事件,并执行预定义的事件处理函数
- 事件分发器(EventDispatcher):负责发起通知
- 事件对象(Event):记录事件的相关信息
流程图:
Node: //_eventDispatcher统一指向CCDirector中的_eventDispatcher
class CC_DLL Node : public Ref
{
protected:
/* 省略 */
EventDispatcher* _eventDispatcher; ///< event dispatcher used to dispatch all kinds of events
/* 省略 */
}
Node::Node(): //_eventDispatcher统一指向CCDirector中的_eventDispatcher
Node::Node()
/* 省略 */
{
// set default scheduler and actionManager
/* 省略 */
_director = Director::getInstance();
_eventDispatcher = _director->getEventDispatcher();
_eventDispatcher->retain();
}
Director:
class CC_DLL Director : public Ref
{
/* 省略 */
/* EventDispatcher associated with this director */
EventDispatcher* _eventDispatcher = nullptr;
/* 省略 */
}
void Director::mainLoop(): //更新函数,每帧被调用
void Director::mainLoop()
{
/* 省略 */
drawScene();
/* 省略 */
}
void Director::drawScene(): //调用事件
// Draw the Scene
void Director::drawScene()
{
/* 省略 */
if (_openGLView)
{
_openGLView->pollEvents();
}
/* 省略 */
}
上面代码表明,Cocos事件机制大概思路是:对对象添加的事件都会保存到CCDirector中的_eventDispatcher对象中,然后在每一帧绘制时,根据事件调用时间顺序分别调用事件包含的回掉函数。
事件根据调用时间可分为:
- _eventBeforeUpdate
- _eventAfterUpdate
- _eventBeforeDraw
- _eventAfterVisit
- _eventAfterDraw
示例:
接下来以UIButton组件的点击(Click)事件举例来跑整个流程:
Button: //Button继承Widget
class CC_GUI_DLL Button : public Widget
{
/* 省略 */
}
typedef std::function<void(Ref*)> ccWidgetClickCallback;
ccWidgetClickCallback _clickEventListener;
void Widget::addClickEventListener(const ccWidgetClickCallback &callback): // 给UIButton添加点击事件
void Widget::addClickEventListener(const ccWidgetClickCallback &callback)
{
this->_clickEventListener = callback;
}
onTouchEnded(Touch *touch, Event* /*unusedEvent*/) : //事件被保存在std::function<bool(Touch*, Event*)>的变量中
class CC_DLL EventListenerTouchOneByOne : public EventListener
{
public:
typedef std::function<void(Touch*, Event*)> ccTouchCallback;
ccTouchCallback onTouchEnded;
}
void Widget::setTouchEnabled(bool enable): // 创建监听器,然后监听事件,最后传给CCdirector的_eventDispatcher
void Widget::setTouchEnabled(bool enable)
{
/* 省略 */
_touchListener = EventListenerTouchOneByOne::create();
CC_SAFE_RETAIN(_touchListener);
_touchListener->setSwallowTouches(true);
_touchListener->onTouchEnded = CC_CALLBACK_2(Widget::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(_touchListener, this);
/* 省略 */
}
void Widget::onTouchEnded(Touch *touch, Event* /*unusedEvent*/)
{
/* 省略 */
if (highlight)
{
releaseUpEvent();
}
/* 省略 */
}
void Widget::releaseUpEvent()
{
/* 省略 */
if (_clickEventListener) {
_clickEventListener(this);
}
/* 省略 */
}
在每一帧绘制时,调用CCDirector::mainLoop(),调用 _openGLView->pollEvents()执行Touch事件
// Draw the Scene
void Director::drawScene()
{
/* 省略 */
if (_openGLView)
{
_openGLView->pollEvents();
}
/* 省略 */
}
void GLViewImpl::pollEvents():
void GLViewImpl::pollEvents()
{
glfwPollEvents();
}
class CC_DLL GLFWEventHandler
{
public:
static void onGLFWMouseCallBack(GLFWwindow* window, int button, int action, int modify)
{
if (_view)
_view->onGLFWMouseCallBack(window, button, action, modify);
}
/* 省略 */
};
void GLViewImpl::onGLFWMouseCallBack(GLFWwindow* /*window*/, int button, int action, int /*modify*/)
void GLViewImpl::onGLFWMouseCallBack(GLFWwindow* /*window*/, int button, int action, int /*modify*/)
{
/* 省略 */
this->handleTouchesEnd(1, &id, &_mouseX, &_mouseY);
/* 省略 */
}
void GLView::handleTouchesEnd(int num, intptr_t ids[], float xs[], float ys[])
{
handleTouchesOfEndOrCancel(EventTouch::EventCode::ENDED, num, ids, xs, ys);
}
void GLView::handleTouchesOfEndOrCancel(EventTouch::EventCode eventCode, int num, intptr_t ids[], float xs[], float ys[])
{
/* 省略 */
touchEvent._eventCode = eventCode;
auto dispatcher = Director::getInstance()->getEventDispatcher();
dispatcher->dispatchEvent(&touchEvent);
for (auto& touch : touchEvent._touches)
{
// release the touch object.
touch->release();
}
}
以上就是Cocos底层的事件机制,总的来说就就是添加事件时会把事件添加到CCDirector中的_eventDispatcher中,在每帧中调用对应触发的事件。