cocos2d-x游戏引擎核心(3.x)----事件分发机制之事件从(android,ios,desktop)系统传到cocos2dx的过程浅析

(一) Android平台下:

cocos2dx 版本3.2,先导入一个android工程,然后看下AndroidManifest.xml

    <application android:label="@string/app_name"
                 android:icon="@drawable/icon">
                     
        <!-- Tell Cocos2dxActivity the name of our .so -->
        <meta-data android:name="android.app.lib_name"
                  android:value="cocos2dcpp" />

        <activity android:name="org.cocos2dx.cpp.AppActivity"
                  android:label="@string/app_name"
                  android:screenOrientation="landscape"
                  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
                  android:configChanges="orientation">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

由此得知启动窗口类为 org.cocos2dx.cpp.AppActivity,并继承之 Cocos2dxActivity.

package org.cocos2dx.cpp;

import org.cocos2dx.lib.Cocos2dxActivity;

public class AppActivity extends Cocos2dxActivity {
}
public abstract class Cocos2dxActivity extends Activity implements Cocos2dxHelperListener 

看下 Cocos2dxActivity 的 onCreate

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        Log.i(TAG, "------onCreate----");
        super.onCreate(savedInstanceState);
        CocosPlayClient.init(this, false);

        onLoadNativeLibraries();//加载了一些静态库

        sContext = this;
        this.mHandler = new Cocos2dxHandler(this);
        
        Cocos2dxHelper.init(this);
        
        this.mGLContextAttrs = getGLContextAttrs();
        this.init();//初始化

        if (mVideoHelper == null) {
            mVideoHelper = new Cocos2dxVideoHelper(this, mFrameLayout);
        }
        
        if(mWebViewHelper == null){
            mWebViewHelper = new Cocos2dxWebViewHelper(mFrameLayout);
        }
    }

onCreate 调用了  init()  初始化

 public void init() {
        
        // FrameLayout
        ViewGroup.LayoutParams framelayout_params =
            new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                       ViewGroup.LayoutParams.MATCH_PARENT);
        mFrameLayout = new FrameLayout(this);
        mFrameLayout.setLayoutParams(framelayout_params);

        // Cocos2dxEditText layout
        ViewGroup.LayoutParams edittext_layout_params =
            new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                       ViewGroup.LayoutParams.WRAP_CONTENT);
        Cocos2dxEditText edittext = new Cocos2dxEditText(this);
        edittext.setLayoutParams(edittext_layout_params);

        // ...add to FrameLayout
        mFrameLayout.addView(edittext);

        // Cocos2dxGLSurfaceView
        this.mGLSurfaceView = this.onCreateView();

        // ...add to FrameLayout
        mFrameLayout.addView(this.mGLSurfaceView);

        // Switch to supported OpenGL (ARGB888) mode on emulator
        if (isAndroidEmulator())
           this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);

        this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
        this.mGLSurfaceView.setCocos2dxEditText(edittext);

        // Set framelayout as the content view
        setContentView(mFrameLayout);
    }

最终显示的视图为 this.mGLSurfaceView = this.onCreateView();

 public Cocos2dxGLSurfaceView onCreateView() {
        Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
        //this line is need on some device if we specify an alpha bits
        if(this.mGLContextAttrs[3] > 0) glSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);

        Cocos2dxEGLConfigChooser chooser = new Cocos2dxEGLConfigChooser(this.mGLContextAttrs);
        glSurfaceView.setEGLConfigChooser(chooser);

        return glSurfaceView;
    }

Cocos2dxGLSurfaceView 就是最终显示的视图,事件处理也在这个类中,包括 onResume,onPause,onSizeChanged,onKeyDown,onTouchEvent等 主要看下onTouchEvent事件的处理过程.

@Override
    public boolean onTouchEvent(final MotionEvent pMotionEvent) {
        
        //Log.d(TAG, "------onTouchEvent action=----"+pMotionEvent.getAction());
        
        // these data are used in ACTION_MOVE and ACTION_CANCEL
        final int pointerNumber = pMotionEvent.getPointerCount();
        final int[] ids = new int[pointerNumber];
        final float[] xs = new float[pointerNumber];
        final float[] ys = new float[pointerNumber];

        for (int i = 0; i < pointerNumber; i++) {
            ids[i] = pMotionEvent.getPointerId(i);
            xs[i] = pMotionEvent.getX(i);
            ys[i] = pMotionEvent.getY(i);
        }

        switch (pMotionEvent.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_POINTER_DOWN:
                final int indexPointerDown = pMotionEvent.getAction() >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final int idPointerDown = pMotionEvent.getPointerId(indexPointerDown);
                final float xPointerDown = pMotionEvent.getX(indexPointerDown);
                final float yPointerDown = pMotionEvent.getY(indexPointerDown);

                this.queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionDown(idPointerDown, xPointerDown, yPointerDown);
                    }
                });
                break;

            case MotionEvent.ACTION_DOWN:
                // there are only one finger on the screen
                final int idDown = pMotionEvent.getPointerId(0);
                final float xDown = xs[0];
                final float yDown = ys[0];

                this.queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionDown(idDown, xDown, yDown);
                    }
                });
                break;

            case MotionEvent.ACTION_MOVE:
                this.queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionMove(ids, xs, ys);
                    }
                });
                break;

            case MotionEvent.ACTION_POINTER_UP:
                final int indexPointUp = pMotionEvent.getAction() >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final int idPointerUp = pMotionEvent.getPointerId(indexPointUp);
                final float xPointerUp = pMotionEvent.getX(indexPointUp);
                final float yPointerUp = pMotionEvent.getY(indexPointUp);

                this.queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionUp(idPointerUp, xPointerUp, yPointerUp);
                    }
                });
                break;

            case MotionEvent.ACTION_UP:
                // there are only one finger on the screen
                final int idUp = pMotionEvent.getPointerId(0);
                final float xUp = xs[0];
                final float yUp = ys[0];

                this.queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionUp(idUp, xUp, yUp);
                    }
                });
                break;

            case MotionEvent.ACTION_CANCEL:
                this.queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionCancel(ids, xs, ys);
                    }
                });
                break;
        }

        /*
        if (BuildConfig.DEBUG) {
            Cocos2dxGLSurfaceView.dumpMotionEvent(pMotionEvent);
        }
        */
        return true;
    }

看下 MotionEvent.ACTION_DOWN,调用了Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionDown.

public void handleActionDown(final int id, final float x, final float y) {
        Log.i("Cocos2dxRenderer","-----handleActionDown--");
        Cocos2dxRenderer.nativeTouchesBegin(id, x, y);
    }

这里的nativeTouchesBegin 是一个jni方法,是现在cocos2d\cocos\platform\android\jni\TouchesJni.cpp里,

private static native void nativeTouchesBegin(final int id, final float x, final float y);
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeTouchesBegin(JNIEnv * env, jobject thiz, jint id, jfloat x, jfloat y) {
        intptr_t idlong = id;
        log("----Info:nativeTouchesBegin id = %d, x=%f, y=%f",id,x,y);
        cocos2d::Director::getInstance()->getOpenGLView()->handleTouchesBegin(1, &idlong, &x, &y);
    }

最后调用GLView::handleTouchesBegin (其实GLView并没有重写父类的handleTouchesBegin方法,所以android下的触发事件,最后是通过GLViewProtocol类的handleTouchesBegin方法进行处理的). 通过 EventDispatcher::dispatchEvent进行事件的分发,调用事件响应函数,都是C++里完成的,就不再往下分析了。

附注:

C++里对事件的分发机制是通过Event,EventListener和EventDispatcher三个主要类完成的,具体详见cocos2d-x 源码分析 : EventDispatcher、EventListener、Event 源码分析 (新触摸机制,新的NotificationCenter机制) .

[转自]cocos2dx android平台事件系统解析

(二) 上面针对的是Android平台下,其他系统事件通知到cocos2dx的流程,在desktop和ios下的流程类似,都有相应平台下的处理文件接收系统事件,如下:

/home/yangxt/document/cocos2d-x-3.2/cocos/platform/CCGLViewProtocol.cpp:
  237: void GLViewProtocol::handleTouchesBegin(int num, intptr_t ids[], float xs[], float ys[])

/home/yangxt/document/cocos2d-x-3.2/cocos/platform/CCGLViewProtocol.h:
  163:     virtual void handleTouchesBegin(int num, intptr_t ids[], float xs[], float ys[]);

/home/yangxt/document/cocos2d-x-3.2/cocos/platform/android/jni/TouchesJni.cpp:
   35:         cocos2d::Director::getInstance()->getOpenGLView()->handleTouchesBegin(1, &id, &x, &y);

/home/yangxt/document/cocos2d-x-3.2/cocos/platform/desktop/CCGLView.cpp:
  555:                 this->handleTouchesBegin(1, &id, &_mouseX, &_mouseY);

/home/yangxt/document/cocos2d-x-3.2/cocos/platform/ios/CCEAGLView.mm:
  411:     glview->handleTouchesBegin(i, (intptr_t*)ids, xs, ys);

从上面可以看出,android下的系统调用最后是调用到了TouchesJni.cpp文件下的触摸事件.也即本文第(一)部分所分析的结果.如果在ios平台下,可以看到是通过CCEAGLView.mm文件处理的.ios下这里不做过多分析. 而desktop平台下,直接通过CCGLView.cpp处理.下面看看desktop平台下的处理过程:

我们可以在GLView.cpp下找到下面的方法:

bool GLView::initWithRect(const std::string& viewName, Rect rect, float frameZoomFactor)
{
    setViewName(viewName);

    _frameZoomFactor = frameZoomFactor;

    glfwWindowHint(GLFW_RESIZABLE,GL_FALSE);

    _mainWindow = glfwCreateWindow(rect.size.width * _frameZoomFactor,
                                   rect.size.height * _frameZoomFactor,
                                   _viewName.c_str(),
                                   _monitor,
                                   nullptr);
    glfwMakeContextCurrent(_mainWindow);

    glfwSetMouseButtonCallback(_mainWindow, GLFWEventHandler::onGLFWMouseCallBack);
    glfwSetCursorPosCallback(_mainWindow, GLFWEventHandler::onGLFWMouseMoveCallBack);
    glfwSetScrollCallback(_mainWindow, GLFWEventHandler::onGLFWMouseScrollCallback);
    glfwSetCharCallback(_mainWindow, GLFWEventHandler::onGLFWCharCallback);
    glfwSetKeyCallback(_mainWindow, GLFWEventHandler::onGLFWKeyCallback);
    glfwSetWindowPosCallback(_mainWindow, GLFWEventHandler::onGLFWWindowPosCallback);
    glfwSetFramebufferSizeCallback(_mainWindow, GLFWEventHandler::onGLFWframebuffersize);
    glfwSetWindowSizeCallback(_mainWindow, GLFWEventHandler::onGLFWWindowSizeFunCallback);

    setFrameSize(rect.size.width, rect.size.height);

    // check OpenGL version at first
    const GLubyte* glVersion = glGetString(GL_VERSION);

    if ( utils::atof((const char*)glVersion) < 1.5 )
    {
        char strComplain[256] = {0};
        sprintf(strComplain,
                "OpenGL 1.5 or higher is required (your version is %s). Please upgrade the driver of your video card.",
                glVersion);
        MessageBox(strComplain, "OpenGL version too old");
        return false;
    }

    initGlew();

    // Enable point size by default.
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

    return true;
}

glfwCreateWindow方法里面就完成了窗口的创建以及其他的一些初始化工作,并且也设置了鼠标,键盘等一下响应回调函数,这里,我们看看glfwSetMouseButtonCallback(_mainWindow, GLFWEventHandler::onGLFWMouseCallBack);的回调函数:

void GLView::onGLFWMouseCallBack(GLFWwindow* window, int button, int action, int modify)
{
    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);
            }
        }
        else if(GLFW_RELEASE == action)
        {
            if (_captured)
            {
                _captured = false;
                intptr_t id = 0;
                this->handleTouchesEnd(1, &id, &_mouseX, &_mouseY);
            }
        }
    }
    
    //Because OpenGL and cocos2d-x uses different Y axis, we need to convert the coordinate here
    float cursorX = (_mouseX - _viewPortRect.origin.x) / _scaleX;
    float cursorY = (_viewPortRect.origin.y + _viewPortRect.size.height - _mouseY) / _scaleY;

    if(GLFW_PRESS == action)
    {
        EventMouse event(EventMouse::MouseEventType::MOUSE_DOWN);
        event.setCursorPosition(cursorX, cursorY);
        event.setMouseButton(button);
        Director::getInstance()->getEventDispatcher()->dispatchEvent(&event);
    }
    else if(GLFW_RELEASE == action)
    {
        EventMouse event(EventMouse::MouseEventType::MOUSE_UP);
        event.setCursorPosition(cursorX, cursorY);
        event.setMouseButton(button);
        Director::getInstance()->getEventDispatcher()->dispatchEvent(&event);
    }
}

可以看到,在GLView里面直接处理窗口鼠标事件,并通过dispatchEvent将事件分发给cocos2dx. cocos2dx里面的事件分发这里也不作过多阐述.

附注:

上面对事件分发机制的分析中,我们可以看到,GLViewProtocol类实际负责了窗口级别的功能管理和实现, 包括:坐标和缩放管理, 画图工具,按键事件,而这些正是cocos2d-x游戏引擎核心(3.x)----启动渲染流程 博文分析中可以看到的.

posted @ 2016-05-22 11:37  小天_y  阅读(1510)  评论(0编辑  收藏  举报