zoukankan      html  css  js  c++  java
  • 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方法,是现在cocos2dcocosplatformandroidjniTouchesJni.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)----启动渲染流程 博文分析中可以看到的.

  • 相关阅读:
    “北斗人”梦想成真!星间链路新技术探索,ThingJS 3D 可视化 效果图
    如何从道路BIM模型中提取数据?分三步超简单!ThingJS 3D 可视化
    ThingJS: 做一个数据可视化项目的难点在什么地方?3D 可视化 three.js
    震撼来袭 | ThingJS 3D城市应用双11钜惠,低至688元>>官方优惠
    ThingJS 和three.js开发有何不同,让开发早点下班回家!3D 可视化
    NASA样本实验室之3D渲染图,和宇航员一样的上帝视角!3D 可视化 ThingJS
    三维建模国内外技术对比:和SketchUp、Revit建模软件相媲美 3D 地图 可视化 ThingJS
    CSS两列及三列自适应布局方法整理
    前端进阶-每日一练(3)
    前端进阶-每日一练(2)
  • 原文地址:https://www.cnblogs.com/yyxt/p/5516419.html
Copyright © 2011-2022 走看看