本文从主要介绍点击事件的处理过程,分别从win32、Android、IOS系统介绍Cocos2dx点击事件处理过程。
1、Win32系统
AppDelegate::applicationDidFinishLaunching()->GLView::create(…)->GLView::initWithRect(…) bool GLView::initWithRect(conststd::string& viewName, Rect rect, float frameZoomFactor){ ……… glfwSetMouseButtonCallback(_mainWindow, GLFWEventHandler::onGLFWMouseCallBack); ……… }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
在win32系统中点击事件为鼠标点击事件,使用glfwSetMouseButtonCallback绑定鼠标点击事件的时间处理函数;
static voidonGLFWMouseCallBack(GLFWwindow* window, int button, int action, int modify){ if (_view) _view->onGLFWMouseCallBack(window,button, action, modify); } voidGLView::onGLFWMouseCallBack(GLFWwindow* window, int button, int action, intmodify){ if(GLFW_MOUSE_BUTTON_LEFT == button) { //鼠标左键时 if(GLFW_PRESS == action) { //鼠标左键按下时 _captured = true; if(this->getViewPortRect().equals(Rect::ZERO) || this->getViewPortRect().containsPoint(Vec2(_mouseX,_mouseY))){ intptr_t id = 0; this->handleTouchesBegin(1, &id, &_mouseX, &_mouseY); } }elseif(GLFW_RELEASE == action) { //鼠标左键松开时 if(_captured) { _captured = false; intptr_t id = 0; this->handleTouchesEnd(1, &id, &_mouseX, &_mouseY); } } } ………//处理鼠标事件 }
上面的处理方法代码位于cocos2d\cocos\platform\win32\ CCGLView.cpp中;
2、Android系统
public class Cocos2dxGLSurfaceView extends GLSurfaceView { …… public booleanonTouchEvent(final MotionEvent pMotionEvent) { ………. switch(pMotionEvent.getAction() & MotionEvent.ACTION_MASK) { …… caseMotionEvent.ACTION_DOWN: //按下事件 this.queueEvent(newRunnable() { //新起一个线程处理该事件 publicvoid run() { Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionDown (idDown, xDown,yDown); } }); break; ……………. caseMotionEvent.ACTION_UP: //抬起事件 this.queueEvent(newRunnable() { publicvoid run() { Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionUp (idUp, xUp, yUp); } }); break; ………… } return true; } }
Cocos2dxGLSurfaceView继承View类,实现View中关于触摸屏事件处理函数onTouchEvent,当有触摸屏事件发生时会调用Cocos2dxGLSurfaceView中onTouchEvent方法,在onTouchEvent方法中会区分Down&Up事件,并使用新的线程执行Cocos2dxRenderer类中handleActionDown&handleActionUp方法;
public class Cocos2dxRenderer implements GLSurfaceView.Renderer{ private static native void nativeTouchesBegin(final int pID, final float pX, final float pY); private static native void nativeTouchesEnd(final int pID, final float pX, final float pY); public void handleActionDown(finalint pID, final float pX, final float pY) { Cocos2dxRenderer.nativeTouchesBegin(pID,pX, pY); } public voidhandleActionUp(final int pID, final float pX, final float pY) { Cocos2dxRenderer.nativeTouchesEnd(pID,pX, pY); } }
在Cocos2dxRenderer类中的handleActionDown&handleActionUp方法会通过JNI调用C++实现的nativeTouchesBegin&nativeTouchesEnd方法;
nativeTouchesBegin&nativeTouchesEnd分别对应
cocos2d\cocos\platform\android\jni\TouchesJni.cpp文件中:
JNIEXPORT voidJNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeTouchesBegin(JNIEnv * env, jobject thiz, jint id, jfloatx, jfloat y) { cocos2d::Director::getInstance()->getOpenGLView()->handleTouchesBegin(1,&id, &x, &y); } JNIEXPORT voidJNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeTouchesEnd(JNIEnv * env, jobject thiz, jint id, jfloatx, jfloat y) { cocos2d::Director::getInstance()->getOpenGLView()->handleTouchesEnd(1,&id, &x, &y); }
3、IOS系统
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { …… glview->handleTouchesBegin(i,(intptr_t*)ids, xs, ys); } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { …… glview->handleTouchesEnd(i,(intptr_t*)ids, xs, ys); }
以上方法在cocos2d\cocos\platform\ios\CCEAGLView.mm中实现;
4、Cocs2dx事件处理
上文中handleTouchesBegin和handleTouchesEnd处理在Win32、Android、IOS中是相同的实现,接下来会继续分析;
voidGLViewProtocol::handleTouchesBegin(int num, intptr_t ids[], float xs[], floatys[]){ //点击按下时执行 intptr_t id = 0; float x = 0.0f; float y = 0.0f; int unusedIndex = 0; EventTouch touchEvent; for (int i = 0; i < num; ++i)//遍历所有的触摸点,若多点触摸则需要 { id = ids[i]; x = xs[i]; y = ys[i]; //在本地Touch字典中查询该touch是否已经存在 auto iter = g_touchIdReorderMap.find(id); // 如果是一个新Touch if (iter == g_touchIdReorderMap.end()) { unusedIndex = getUnUsedIndex();//获取新Touch标记符 …… //收集Touch信息 Touch* touch =g_touches[unusedIndex] = new Touch(); touch->setTouchInfo(unusedIndex,(x - _viewPortRect.origin.x) / _scaleX, (y -_viewPortRect.origin.y) / _scaleY); CCLOGINFO("x = %f y =%f", touch->getLocationInView().x, touch->getLocationInView().y); //将当前Touch添加入Touch查询字典中 g_touchIdReorderMap.insert(std::make_pair(id, unusedIndex)); touchEvent._touches.push_back(touch); } } …… touchEvent._eventCode =EventTouch::EventCode::BEGAN; auto dispatcher =Director::getInstance()->getEventDispatcher(); dispatcher->dispatchEvent(&touchEvent);//分发Touch事件 }
在handleTouchesBegin中:
(1)查询点击是否已经存在
(2)若点击记录中不存在当前点击,则收集点击信息,分发TouchBegin事件
在过程(1)中单点点击中传入的ids={0},点击事件是存储在touchIdReorderMap[0]位置,这么做事防止出现点击同一个位置时未释放前,出现点击其他位置从而调用handleTouchesBegin;
voidGLViewProtocol::handleTouchesEnd(int num, intptr_t ids[], float xs[], floatys[]){ //点击释放时执行 handleTouchesOfEndOrCancel(EventTouch::EventCode::ENDED, num, ids, xs,ys); } void GLViewProtocol::handleTouchesOfEndOrCancel(EventTouch::EventCodeeventCode, int num, intptr_t ids[], float xs[], float ys[]) { intptr_t id = 0; float x = 0.0f; float y = 0.0f; EventTouch touchEvent; for (int i = 0; i < num; ++i) //遍历所有的触摸点,若多点触摸则需要 { id = ids[i]; x = xs[i]; y = ys[i]; //查询该释放事件对应的点击事件是否存在 auto iter =g_touchIdReorderMap.find(id); …… Touch* touch =g_touches[iter->second]; if (touch) { touch->setTouchInfo(iter->second,(x - _viewPortRect.origin.x) / _scaleX, (y -_viewPortRect.origin.y) / _scaleY); touchEvent._touches.push_back(touch); g_touches[iter->second] =nullptr; removeUsedIndexBit(iter->second); g_touchIdReorderMap.erase(id);//移除已经释放点击事件 } …… } …… touchEvent._eventCode = eventCode; auto dispatcher =Director::getInstance()->getEventDispatcher(); dispatcher->dispatchEvent(&touchEvent);//派发点击释放事件 for(auto& touch : touchEvent._touches) {//释放点击 touch->release(); } }
在handleTouchesOfEndOrCancel中:
(1)查询点击是否已经存在
(2)若点击记录存在点击,则收集点击信息,分发TouchEnd事件
因为在单点点击中传入的ids={0},只有查询到该点击事件存在才会执行释放操作,最后会将点击事件从g_touchIdReorderMap移除,保证在调用ToucheEnd之前一定有TouchBegin。