基本元素:
- 事件監聽器( 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中,在每幀中調用對應觸發的事件。