2dx流程 cocos_cocos2dx渲染流程 - CSDN
  • Cocos2Dx之渲染流程

    2015-03-31 16:09:33
    渲染时一个游戏引擎最重要的部分。...Cocos2Dx是一个2D框架,可以简单地看做z轴在一个平面上,Cocos2Dx采用的OpenGL技术决定了往3D渲染上面走也不是不行的。最新3.2版本已经支持3D骨骼动画的CCSprite。

    渲染时一个游戏引擎最重要的部分。渲染的效率决定了游戏的流畅度清晰度,跟前面的介绍的内容相比,渲染是最具技术含量的事情,也是一个需要很多专业知识的事情。这里我们有这个机会,来学习下一个游戏引擎的渲染是怎么做的。Cocos2Dx是一个2D框架,可以简单地看做z轴在一个平面上,Cocos2Dx采用的OpenGL技术决定了往3D渲染上面走也不是不行的。最新3.2版本已经支持3D骨骼动画的CCSprite。3.2版本还有很多其他的变化,可谓是一次伤筋动骨的大版本,现在还不跳跃,继续基于2.2.3进行分析。

    这篇文章先分析简单的CCSprite和CCLayer是怎么渲染出来的,过一下整个渲染的流程。后续我们再分析一些高级功能。

    要能够使用OpenGL,肯定第一步是要先初始化OpenGL,并且创建一个可以在上面绘制的场景的窗口。OpenGL本身是跟操作系统,窗口系统无关的图形接口。因此初始化肯定也是平台相关。这就是为什么CCEGLView在每个平台下面都有自己实现的原因。继续使用Windows平台进行分析,其他平台大同小异。

    回忆前面讲到游戏启动流程的时候,我们会在AppDelegate::applicationDidFinishLaunching调用pDirector->setOpenGLView(CCEGLView::sharedOpenGLView())来设置OpenGL使用的View。sharedOpenGLView是CCEGLView的一个单例访问接口。CCEGLView::sharedOpenGLView第一次被调用的时候会首先创建一个平台相关的CCEGLView对象,然后调用其Create()函数。

    Windows上面的Create函数我们已经看过,它会设置窗口样式,然后创建一个窗口。窗口创建完毕后会调用CCEGLView::initGL。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //file:\cocos2dx\platform\win32\CCEGLView.cpp
    bool CCEGLView::initGL()
    {
        m_hDC = GetDC(m_hWnd);
        SetupPixelFormat(m_hDC);
        m_hRC = wglCreateContext(m_hDC);
        wglMakeCurrent(m_hDC, m_hRC);
        const GLubyte* glVersion = glGetString(GL_VERSION);
        if atof((const char*)glVersion) < 1.5 )
        {
            return false;
        }
        GLenum GlewInitResult = glewInit();
        if (GLEW_OK != GlewInitResult)
        {
            return false;
        }
        if(glew_dynamic_binding() == false)
        {
            return false;
        }
        glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
        return true;
    }

    CCEGLView::initGL首先设置像素格式。像素格式是OpenGL窗口的重要属性,它包括是否使用双缓冲,颜色位数和类型以及深度位数等。像素格式可由Windows系统定义的所谓像素格式描述子结构来定义(PIXELFORMATDESCRIPTOR)。应用程序通过调用ChoosePixelFormat函数来寻找最接近应用程序所设置的象素格式,然后调用SetPixelFormat来设置使用的像素格式。WglCreateContext函数创建一个新的OpenGL渲染描述表,此描述表必须适用于绘制到由m_hDC返回的设备。这个渲染描述表将有和设备上下文(DC)一样的像素格式。wglMakeCurrent将刚刚创建的渲染描述符表设置为当前线程使用的渲染环境。完成这些操作之后,OpenGL的基本环境已经准备好了。后续的处理是为了使用OpenGL扩展。glewInit初始化GLEW。GLEW(OpenGL Extension Wrangler Library)是一个跨平台的C/C++库,用来查询和加载OpenGL扩展。OpenGL并存的版本比较多,有些特性并没有存在于OpenGL核心当中,是以扩展形式存在。作为一个跨平台的游戏框架,处理OpenGL的版本差异是必须要做的。glew_dynamic_binding启用帧缓冲对象(FBO)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    static void SetupPixelFormat(HDC hDC)
    {
        int pixelFormat;
        PIXELFORMATDESCRIPTOR pfd =
        {
            sizeof(PIXELFORMATDESCRIPTOR), // 大小
            1, // 版本
            PFD_SUPPORT_OPENGL | // 像素格式必须支持OpenGL
            PFD_DRAW_TO_WINDOW | // 渲染到窗口中
            PFD_DOUBLEBUFFER, // 支持双缓冲
            PFD_TYPE_RGBA, // 申请RGBA颜色格式
            32, // 选定的颜色深度
            0, 0, 0, 0, 0, 0, // 忽略的色彩位
            0, // 无Alpha缓存
            0, // 忽略Shift Bit
            0, // 无累加缓存
            0, 0, 0, 0, // 忽略聚集位
            24, // 24位的深度缓存
            8, // 蒙版缓存
            0, // 无辅助缓存
            PFD_MAIN_PLANE, // 主绘图层
            0, // 保留
            0, 0, 0, // 忽略层遮罩
        };
        pixelFormat = ChoosePixelFormat(hDC, &pfd);
        SetPixelFormat(hDC, pixelFormat, &pfd);
    }

    从设置的像素格式上可以看出,Windows上启用了双缓冲,使用RGBA颜色,32位颜色深度。

    不同平台上面,OpenGL的初始化流程不完全一样。详细的区别可以查看平台相关的CCEGLView类。

    OpenGL初始化完成后,就可以进行绘制操作了。CCDisplayLinkDirector::mainLoop会调用drawScene()来绘制场景。drawScene本身做的工作还比较多,比如前面介绍的调度器处理。这里我们抛开渲染无关的内容,只看跟渲染紧密相关的代码实现。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    void CCDirector::drawScene(void)
    {
        //清除颜色缓冲区和深度缓冲去
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        //该节点的转换矩阵入栈
        kmGLPushMatrix();
        // draw the scene
        if (m_pRunningScene)
        {
            m_pRunningScene->visit();
        }
        // 该节点的转换矩阵出栈
        kmGLPopMatrix();
        // 交换缓冲区
        if (m_pobOpenGLView)
        {
            m_pobOpenGLView->swapBuffers();
        }
    }

    一个场景包含当前窗口可以看到的所有内容。由于OpenGL是基于状态的,比如设置的颜色,如果不去清楚或者修改就一直会使用下去。我们当然不希望上一次绘制场景产生的缓冲区数据被绘制到现在的场景当中。所以drawScene()首先清楚颜色缓冲区和深度缓冲区。

    关于kmGLPushMatrix和kmGLPopMatrix,有必要先介绍下背景。Cocos2Dx使用了模型视图矩阵、投影矩阵和纹理矩阵。模型视图矩阵完成模型和视图的变换,包括平移、旋转和缩放。投影矩阵完成三维空间的顶点映射到二维的屏幕上,有两种投影:正射投影和透视投影。纹理矩阵用来对纹理进行变换。关于矩阵变换参考注1的资料。

    OpenGL通过使用齐次坐标,平移、旋转和缩放变换都可以通过矩阵乘法完成。但是矩阵乘法是有顺序的,左乘和右乘的结果是不一样的,因此应该变换的顺序很重要。由于存在多个矩阵,并且为了简化复杂模型变换,OpenGL使用栈来存放矩阵。kmGLPushMatrix()函数将当前栈的栈顶矩阵复制后压栈,对应的kmGLPopMatrix()用于出栈,还有kmGLLoadIdentity()用于将栈顶矩阵初始化成单位矩阵。需要注意的是,OpenGL使用的矩阵是列优先的矩阵。

    kmGLPushMatrix()内部调用lazyInitialize()来延迟初始化Cocos2Dx使用的三类矩阵:模型视图矩阵、投影矩阵和纹理矩阵。初始化的过程中,不但为三个类型的矩阵都创建了一个堆栈(内部通过数组实现的),还在每个堆栈中都压入一个单位矩阵。单位矩阵左乘任何顶点,顶点不会变化。单位矩阵不会影响后续的变换。current_stack表示了当前使用的堆栈,可以通过kmGLMatrixMode(mode)来切换当前使用的矩阵堆栈。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void lazyInitialize()
    {
        if (!initialized) {
            kmMat4 identity; 
            km_mat4_stack_initialize(&modelview_matrix_stack);
            km_mat4_stack_initialize(&projection_matrix_stack);
            km_mat4_stack_initialize(&texture_matrix_stack);
            current_stack = &modelview_matrix_stack;
            initialized = 1;
            kmMat4Identity(&identity);
            km_mat4_stack_push(&modelview_matrix_stack, &identity);
            km_mat4_stack_push(&projection_matrix_stack, &identity);
            km_mat4_stack_push(&texture_matrix_stack, &identity);
        }
    }

    CCDirector::drawScene()第一次被调用的时候,会初始化模型视图矩阵、投影矩阵和纹理矩阵分别对应的矩阵栈。然后在每个栈中压入一个单位矩阵。随后kmGLPushMatrix()复制一个模式视图矩阵栈的单位矩阵,然后放到栈顶上。然后渲染当前的场景,最后将栈顶的矩阵出栈。为什么要这样做呢?设想当前的节点需要做一个平移操作,对应的变换矩阵是T1(压栈),假设当前节点的子节点Z序比当前节点大,那么当前节点的所有顶点执行T1p以后,对于它的子节点也需要做平移,使用的同样是变换矩阵T1。这个节点绘制完以后,就该绘制下一个节点了(出栈),下一个节点的变换矩阵为T2(压栈),再用T2做该节点及其子节点的变换。可以看出,这样的处理方式非常自然、简单。

    CCScene的visit继承的是CCNode的visit,它的实现我们前面已经分析过了。这里再看看跟渲染相关的内容。CCNode::visit()先复制一个栈顶元素并压栈,然后计算出一个此节点相对于父节点的变换矩阵,然后把它转换为OpenGL格式的矩阵并右乘在当前绘图矩阵之上,从而得到此节点的世界变换矩阵。随后,根据子节点的Z序确定渲染顺序,然后根据渲染顺序依次渲染子节点或自身。如果是渲染自身,直接调用draw函数。CCSprite的draw函数为空,什么都不做。当前节点及其子节点绘制完成之后,再执行出栈操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    void CCNode::visit()
    {
        kmGLPushMatrix();
        this->transform();
        CCNode* pNode = NULL;
        unsigned int i = 0;
        if(m_pChildren && m_pChildren->count() > 0)
        {
            sortAllChildren();
            // draw children zOrder < 0
            ccArray *arrayData = m_pChildren->data;
            for( ; i < arrayData->num; i++ )
            {
                pNode = (CCNode*) arrayData->arr[i];
                if ( pNode && pNode->m_nZOrder < 0 )
                {
                    pNode->visit();
                }
                else
                {
                    break;
                }
            }
            // self draw
            this->draw();
            for( ; i < arrayData->num; i++ )
            {
                pNode = (CCNode*) arrayData->arr[i];
                if (pNode)
                {
                    pNode->visit();
                }
            }
        }
        else
        {
            this->draw();
        }
        kmGLPopMatrix();
    }

    CCNode::visit的核心是transform()。transform()根据当前节点的位置、旋转角度和缩放比例等属性计算出一个此节点相对于父节点的变换矩阵,然后把它转换为OpenGL格式的矩阵并右乘在当前绘图矩阵之上。transform()是负责CCSprite处理的各种变换的核心函数。下一篇文章继续讨论。

    现在继续分析渲染的流程。前面提到了,CCScrene的draw()函数不做任何事情。下面我们依次看CCSprite和CCLayer是如何绘制出来的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    void CCSprite::draw(void)
    {
        CC_PROFILER_START_CATEGORY(kCCProfilerCategorySprite, "CCSprite - draw");
        CC_NODE_DRAW_SETUP();
        ccGLBlendFunc( m_sBlendFunc.src, m_sBlendFunc.dst );
        ccGLBindTexture2D( m_pobTexture->getName() );
        ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex );
        long offset = (long)&m_sQuad;
        // vertex
        int diff = offsetof( ccV3F_C4B_T2F, vertices);
        glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));
        // texCoods
        diff = offsetof( ccV3F_C4B_T2F, texCoords);
        glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));
        // color
        diff = offsetof( ccV3F_C4B_T2F, colors);
        glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        CHECK_GL_ERROR_DEBUG();
        CC_INCREMENT_GL_DRAWS(1);
        CC_PROFILER_STOP_CATEGORY(kCCProfilerCategorySprite, "CCSprite - draw");
    }

    CCSprite::draw()首先设置使用的OpenGL状态。然后设置混合函数,绑定纹理。随后设置顶点数据,包括顶点坐标、顶点颜色和纹理坐标。因为使用的是顶点数组的方式绘图,所以调用glDrawArrays绘制。GL_TRIANGLE_STRIP参数的意义见注2。代码里面用了很多EMSCRIPTEN宏,他是一个Javscript的LLVM后端处理库,应该是跟JS相关的支持代码。

    CC_NODE_DRAW_SETUP设置绘图环境。实际上设置了OpenGL服务器端的状态和着色器。OpenGL是C/S架构,因此客户端和服务器端都有状态需要维护。ccGLEnable在当前版本上什么都没有做,使用服务器端默认状态。着色器的设置,首先选择当前顶点或片段使用的着色器,然后检查同一着色器使用的Uniform数据是否有变化,有变化的话,重新设置着色器使用的Uniform数据。关于Uniform,参见注4。

    1
    2
    3
    4
    5
    6
    7
    8
    #define CC_NODE_DRAW_SETUP() \
    do { \
        ccGLEnable(m_eGLServerState); \
        { \
            getShaderProgram()->use(); \
            getShaderProgram()->setUniformsForBuiltins(); \
        } \
    while(0)

    混合的设置在渲染的过程中也非常重要。它决定了在视景框内不同深度的模型颜色如何显示出来,透明效果就依靠它实现。渲染的过程是有顺序的,glBlendFunc函数决定后画上去的颜色与已经存在的颜色如何混合到一起。把将要画上去的颜色称为“源颜色”,把原来的颜色称为“目标颜色”。OpenGL 会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘以的系数称为“源因子”,目标颜色乘以的系数称为“目标因子”),然后相加,这样就得到了新的颜色。关于OpenGL混合的详细计算模型,参考注5。

    CCSprite混合使用的源因子和目标因子,以及使用的着色器在CCSprite::initWithTexture里面指定的。可以看出CCSprite的源因子是GL_ONE(CC_BLEND_SRC),目标因子是GL_ONE_MINUS_SRC_ALPHA(CC_BLEND_DST)。这样的参数组合,表示使用1.0作为源因子,实际上相当于完全的使用了这种源颜色参与混合运算,目标颜色乘以1.0减去源颜色的alpha值。CCSprite使用的着色器是kCCShader_PositionTextureColor。

    CCSprite::draw使用glVertexAttribPointer来设置定点数据。它的原型:

    1
    void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,const GLvoid * pointer);
    • index是要修改的定点属性的索引值。实际上就是指定,现在设置的顶点数据是顶点坐标,还是纹理坐标,还是顶点颜色。

    • size是顶点数据的大小。

    • type是顶点数据的类型。CCSprite::draw设置的顶点坐标是3个浮点数表示的;纹理坐标是两个浮点数表示的;颜色是4个无符号数表示的。

    • normalized是否进行归一化。颜色需要归一化操作,也就是讲[0-255]整数的颜色,归一化为[0-1]浮点数的颜色。

    • stride是设置同一类型的数据之间的间隔。这个标志可以让我们把颜色数据、顶点坐标数据和纹理坐标数据放到一个数组里面。

    • pointer指向数据地址。

    CCSprite使用了一个四元组ccV3F_C4B_T2F_Quad来存放四个顶点ccV3F_C4B_T2F,ccV3F_C4B_T2F包含了顶点的坐标、颜色和纹理坐标。因此对于顶点坐标数据,第一个坐标与第二个坐标之间,还有一个大小为ccV3F_C4B_T2F的数据间隔。这就是为什么glVertexAttribPointer的stride参数设置kQuadSize。纹理坐标和颜色放在顶点坐标之后,在通过glVertexAttribPointer设置它们的时候需要计算偏移,offsetof就是完成这个计算的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    typedef struct _ccV3F_C4B_T2F_Quad
    {
        ccV3F_C4B_T2F tl; //! top left
        ccV3F_C4B_T2F bl; //! bottom left
        ccV3F_C4B_T2F tr; //! top right
        ccV3F_C4B_T2F br; //! bottom right
    } ccV3F_C4B_T2F_Quad;
    typedef struct _ccV3F_C4B_T2F
    {
        ccVertex3F vertices; //! vertices (3F)
        ccColor4B colors; //! colors (4B)
        ccTex2F texCoords; // tex coords (2F)
    } ccV3F_C4B_T2F;

    由于CCSprite继承了CCNodeRGBA,它可以设置绘制使用的颜色和不透明度。默认颜色是黑色(255, 255, 255, 255)。

    CCLayer并没有draw()的缺省实现,使用的是它继承的CCNode的draw()函数,什么都不做。但它的子类CCLayerColor可以自己绘制出来,它继承了CCLayerRGBA和CCBlendProtocol(CCSprite通过CCTextureProtocol间接继承CCBlendProtocol)。我们看看CCLayerColor是如何绘制自身的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void CCLayerColor::draw()
    {
        CC_NODE_DRAW_SETUP();
        ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_Color );
        // vertex
        glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, m_pSquareVertices);
        // color
        glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_FALSE, 0, m_pSquareColors);
        ccGLBlendFunc( m_tBlendFunc.src, m_tBlendFunc.dst );
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        CC_INCREMENT_GL_DRAWS(1);
    }

    绘制的步骤与CCSprite类似。只是顶点没有开启纹理坐标,CCLayerColor只是显示设置的颜色,并不支持纹理贴图。因此,glVertexAttribPointer设置顶点数据的时候,没有绑定和设置纹理坐标。

    CCLayerColor::initWithColor里面设置了CCLayerColor混合方式和着色器。混合使用的源因子是GL_SRC_ALPHA,目标因子是GL_ONE_MINUS_SRC_ALPHA。这个混合组合是比较常见的组合。表示源颜色乘以自身的alpha值,目标颜色乘以1.0减去源颜色的alpha值。这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减小。CCLayerColor使用的着色器是kCCShader_PositionColor。

    现在已经看完了怎么渲染出CCSprite和CCLayerColor。自己派生的CCSprite渲染也是一样的方式。游戏退出的时候需要做OpenGL的清理工作。清理工作放在CCEGLView::destroyGL()里面。Windows上面依次调用:

    1
    2
    wglMakeCurrent(m_hDC, NULL);
    wglDeleteContext(m_hRC);

    设置设备描述符表使用的图形描述符为空,然后删除图形描述符。

    注1:模式视图矩阵和投影矩阵http://www.songho.ca/opengl/gl_transform.html。该网站上还有模拟矩阵变换的小程序,非常直观。

    注2:GL_TRIANGLES是以每三个顶点绘制一个三角形。第一个三角形使用顶点v0,v1,v2,第二个使用v3,v4,v5,以此类推。如果顶点的个数n不是3的倍数,那么最后的1个或者2个顶点会被忽略。GL_TRIANGLE_STRIP则稍微有点复杂,其规律是:

    1. 构建当前三角形的顶点的连接顺序依赖于要和前面已经出现过的2个顶点组成三角形的当前顶点的序号的奇偶性(如果从0开始):

    2. 如果当前顶点是奇数:组成三角形的顶点排列顺序:T = [n-1 n-2 n]。

    3. 如果当前顶点是偶数:组成三角形的顶点排列顺序:T = [n-2 n-21 n]。

    以上图为例,第一个三角形,顶点v2序号是2,是偶数,则顶点排列顺序是v0,v1,v2。第二个三角形,顶点v3序号是3,是奇数,则顶点排列顺序是v2,v1,v3,第三个三角形,顶点v4序号是4,是偶数,则顶点排列顺序是v2,v3,v4,以此类推。这个顺序是为了保证所有的三角形都是按照相同的方向绘制的,使这个三角形串能够正确形成表面的一部分。对于某些操作,维持方向是很重要的,比如剔除。注意:顶点个数n至少要大于3,否则不能绘制任何三角形。GL_TRIANGLE_FAN与GL_TRIANGLE_STRIP类似,不过它的三角形的顶点排列顺序是T = [n-1 n-2 n]。各三角形形成一个扇形序列。

    注3:EMSCRIPTEN:Emscripten 是 Mozilla 的 Alon Zakai 开发的一个独特 LLVM 后端,可以将任意 LLVM 中间码编译成 JavaScript,大大简化了现有代码在 Web 时代的重用。Emscripten 并非通常的 LLVM 后端,本身使用 JavaScript 写成。它可以将任何通过 LLVM 前端(比如 C/C++ Clang )生成的 LLVMIR 中间码编译成 JavaScript,从而显著降低移植现有代码库到 Web 环境的损耗。

    注4:uniform变量是外部application程序传递给(vertex和fragment)shader的变量。因此它是application通过函数glUniform**()函数赋值的。在(vertex和fragment)shader程序内部,uniform变量就像是C语言里面的常量(const ),它不能被shader程序修改。(shader只能用,不能改)。如果uniform变量在vertex和fragment两者之间声明方式完全一样,则它可以在vertex和fragment共享使用。(相当于一个被vertex和fragment shader共享的全局变量)。uniform变量一般用来表示:变换矩阵,材质,光照参数和颜色等信息。
    注5:OpenGL可以设置混合运算方式,包括加、减、取两者中较大的、取两者中较小的、逻辑运算等。下面用数学公式来表达一下这个运算方式。假设源颜色的四个分量(指红色,绿色,蓝色,alpha值)是(Rs, Gs, Bs,  As),目标颜色的四个分量是(Rd, Gd, Bd, Ad),又设源因子为(Sr, Sg, Sb, Sa),目标因子为(Dr, Dg, Db,  Da)。则混合产生的新颜色可以表示为:(Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da)。源因子和目标因子是可以通过glBlendFunc函数来进行设置的。glBlendFunc有两个参数,前者表示源因子,后者表示目标因子。这两个参数可以是多种值,下面介绍比较常用的几种。

    • GL_ZERO:     表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。

    • GL_ONE:      表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。

    • GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。

    • GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。

    • GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。

    • GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。


    转自:http://my.oschina.net/sulliy/blog/299437

    展开全文
  • cocos2dx 安装流程

    2019-02-12 11:59:22
    1.安装cocos2dx 下载安装cocos2dx 安装目录如下 cocos2dx是使用Python脚本编写的,cocos工具的运行需要安装Python环境 Python下载地址 https://www.python.org/ 需要注意的是它目前有Python3和Python2可以下载,...

    1.安装cocos2dx

    下载安装cocos2dx 安装目录如下
    在这里插入图片描述
    cocos2dx是使用Python脚本编写的,cocos工具的运行需要安装Python环境
    Python下载地址
    https://www.python.org/
    需要注意的是它目前有Python3和Python2可以下载,我们选择Python 2,不要下载3,因为2和3语法有很大差别,cocos这个工具是使用2编写的,如果你使用了3,会在脚本编译就出问题
    (我的源代码中有window的Python安装包)
    下载->安装->配置环境变量(Path中添加Python根目录)
    在这里插入图片描述

    点击确定设置好。

    接下来还需要安装jdk和ant 并配置 配置流程百度。

    接下来安装visual studio 2017
    文件下载完毕后,双击安装文件,在弹出来的安装窗口里,选择C++桌面开发,然后在左下角选择安装位置。最后点击右下角的安装按钮开始安装。
    在这里插入图片描述在这里插入图片描述

    接下来配置cocos2dx
    在cocos2dx安装目录位置打开命令行界面,
    在当前文件夹打开命令行

    方法一:
    在当前目录下,按下shift + 鼠标右键,会出现“在此处打开命令窗口”的字样,然后点击即可。

    方法二:
    在该文件夹上,按下shift + 鼠标右键,会出现“在此处打开命令窗口”的字样,然后点击即可。

    然后执行命令:
    python setup.py
    如下所示,执行过程中会提示有一些路径未找到,因为是用于安卓开发的,所以可以不用管,敲回车跳过即可。

    然后关闭执行命令完毕的命令行界面,重新打开一个,执行下面这条命令,创建一个测试工程:

    cocos new -l cpp -p com.gamefromscratch.gamename -d e:\testgame

    这个命令new表示新建,-l表示使用cpp语言来创建,然后-p表示使用哪个包,-d表示项目的目录,我把目录放在e盘的testgame目录下。创建完成后目录内容如下所示,里面有各个版本的项目文件,windows平台就是win32,mac平台就是mac。在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 最近要做一个手游项目,打算用cocos2dx来做,我也是初学者,网上很多书籍和资料都是采用cocos2dx的c++版本来做开发示例的 很少关于cocos2dx和lua怎么结合在实际项目中的教程,这使得我很痛苦,所以我写下这一系列...

    最近要做一个手游项目,打算用cocos2dx来做,我也是初学者,网上很多书籍和资料都是采用cocos2dx的c++版本来做开发示例的

    很少关于cocos2dx和lua怎么结合在实际项目中的教程,这使得我很痛苦,所以我写下这一系列教程,希望跟我一样的同学能得到小需帮助


    首先,我讲讲我为什么要用cocos2dx+lua来开发吧

    我个人觉得,对于开发一些休闲类、性能要求不太高的游戏,应该采用cocos2dx+lua、cocos2dx+html5、cocos2dx+javascript等来会使开发的效率提高

    原因:

    1、我们不用考虑很多c++中最烦的指针没师范而导致的内存泄漏问题,虽然说cocos2dx已经有一套内存管理机制,但是对于我这种渣渣程序员来说

    我绝对保证,我写的程序一旦大了,肯定会有内存泄漏的发生,而且写c++程序还有很多坑,比如以后如果要做游戏热更,那肯定是绝对的麻烦,甚至可以说无法完成

    2、用lua等脚本语言来开发游戏逻辑部分代码将变得非常简便,cocos2dx(我使用的是3.4版本)目前提供了一个马上刷新加载程序的功能,使得我们一旦修改了代码,可以不用像C++那样编译半天之后才看效果,只需按一下F5就可以了,几乎达到了“修改即看”的效果,方便我们调整ui布局等等

    3、用lua脚本之后,我们就可以少考虑内存泄漏问题了,我个人对于这东西很是害怕,所以如果不用考虑那么多,将利于我把精力放在其他思考地方上去,而且使用lua脚本可以执行热更,目前很多手游都的热更都是游戏的逻辑代码,其中很多就是用lua写的,C++的要重新编译,估计实现不容易吧(未探究,知道的同学告诉我一下哈,谢谢!)


    至于说为什么用cocos2dx-lua而不用cocos2dx-html5或者cocos2dx-javascript,我认为这个就要根据你们项目组情况咯,总之走C++和脚本语言开发游戏的方向肯定是对的

    好了,说了这么多废话,现在来开始讲一讲我最新学到的成果,有什么不对的地方请大神们多多指教,也欢迎大家与我交流,QQ:1609097487(不过我比较少上Q)


    首先有些cocos2dx-lua的基础教程,在cocos2dx的官网上已经有了,这部分的东西我就不多说了,直接拉个链接给你们,打打基础再看我写的东西吧:

    Quick-Cocos2d-x v3.3小白书系列教程

    Quick-Cocos2d-x初学者游戏教程


    看完这两个教程,基本上你对cocos2dx-quick就有个基础了解

    好接下来我要说教程上没说,但是有可能同学们会比较困惑的一些地方啦

    首先,我们说的代码结构都以qucik-cocos2dx建立的代码结构来进行说明(其实用cocos命令建立的结构也是差不多)


    疑惑一:有很多直接addTo、move等方法,我在cocos2dx的API中没找到啊,它们在哪里?

    答:在src/cocos/framework/extends里面,有很多类似NodeEx.lua的文件,就是在这里啦,quick在这里对一些常见的类进行了一些方法的扩充,使得我们的代码简化了


    疑惑二:有个什么class的用来创建类的,我在哪里可以看到它的源码?

    答:在src/cocos/cocos2d/functions.lua文件里

    lua没有类的语法,但是cocos2dx利用类的概念跟lua的特性模拟了类的实现,

    实现你们都看到了,在一开始定义一个本地变量:

    local cc = class("Test")

    然后实现方法:

    function cc:method()

    ...

    end

    最后返回最开始的变量:

    return cc


    疑惑三:你怎么知道这些东西的?

    答:首先出错时看编译器的提示,里面的debug.strace会说哪个lua脚本出错的,然后再看看quick中的lua源代码,最后唯有百度咯(很多我也是最后百度= =)


    好,先写到这里,写本文的时候我也是边学边写的状态,所以写出来的都不是经验之谈,但是我仍然希望能对你有帮助,下次我将写一些开发中遇到的问题,以及我的解决方法跟大家共享,谢谢各位



    展开全文
  • cocos2dx 引擎渲染流程

    2018-04-10 18:49:18
    cocos3.x版本的渲染是将所有需要渲染的...渲染前根据ID对RenderCommand进行排序,然后再进行渲染,整个渲染流程就是在CCApplication类文件run()方法中开始的cocos2dx整个工程是运行在一个单线程里的,也就是我们通...

    cocos3.x版本的渲染是将所有需要渲染的node先通过各种RenderCommand封装起来,然后引擎将这些RenderCommand添加到一个队列中存了起来,这个队列叫CommandQueue;渲染前根据ID对RenderCommand进行排序,然后再进行渲染,整个渲染流程就是在CCApplication类文件run()方法中开始的

    cocos2dx整个工程是运行在一个单线程里的,也就是我们通常说的主线程,在主线程里完成渲染、相关的定时器等处理;主循环mainLoop()由导演负责维护,主线程mainLoop()会不停地执行,理想状态下每秒会调用60次。


    展开全文
  • 一、游戏开发流程。 ①准备游戏引擎 ②准备各类美术资源 ③编写demo,逻辑 ④适配手机,屏幕分辨率,以iPhone4的分辨率为经典。 内存、系统版本。 ⑤优化,性能优化,操作系统优化 ...Cocos2dx

    一、游戏开发流程。

    ①准备游戏引擎
    ②准备各类美术资源
    ③编写demo,逻辑
    ④适配手机,屏幕分辨率,以iPhone4的分辨率为经典。内存、系统版本。
    ⑤优化,性能优化,操作系统优化
    ⑦发布游戏

    二、游戏的构成

    图形系统之shader,动画,输入控制系统、音频系统、物理系统、粒子系统、配置系统、资源管理系统、

    三、Cocos2d与Cocos2dx

    ①cocos2d只能在ios下运行,cocos2dx是跨平台的,ios和android平台都可以运行
    ②cocos2dx是cocos2d的C++写法,但是游戏架构是一样的,都包含了精灵,导演,场景,动作等概念,他们是一脉相承的东西。你可以直接研究cocos2dx,没有什么障碍。虽然是有了cocos2d才有的cocos2dx,但是cocos2dx包含了cocos2d的主要思想,因此可以直接研究cocos2dx。
    ③Cocos2dx支持的开发语言,Lua,C++,JavaScript.
    ④Cocos2d图形特性,精灵基本绘制,旋转,位移,缩放,横切。动画序列帧,连续的多张图片来组成动画序列帧。
    ⑤Action系统,骨骼动画。
    ⑥其他支持,Box2D物理引擎,音频系统支持mp3等多种格式,Http网络模块。
    展开全文
  • Cocos2dx--使用Shader

    2018-06-23 16:43:16
    Cocos2dx的Shader架构 Cocos2dx的Shader由GLProgram、GLProgramState、GLProgramCache、GLProgramStateCache组成。 GLProgram是Cocos2dx对Program的封装,一般提供一些静态方法,被多个节点复用。而GLProgramState...
  • Cocos2dx 中的主要概念包括:应用、导演、场景、图层、精灵、动画、动作。层次关系如下: CCDirector(导演) 在cocos2d-x引擎中,CCDirector类是整个游戏的组织和控制核心,游戏的运行规则,游戏内的...
  • 渲染时一个游戏引擎最重要的部分。...Cocos2Dx是一个2D框架,可以简单地看做z轴在一个平面上,Cocos2Dx采用的OpenGL技术决定了往3D渲染上面走也不是不行的。最新3.2版本已经支持3D骨骼动画的CCSprite。
  • cocos2dx网络模块

    2019-10-11 09:54:09
    title: cocos2dx网络模块 comments: true date: 2019-01-04 18:55:15 tags: cocos2dx categories: cocos2dx cocos2dx除了curllib和websocket其他的没有封装好用的网络库,尤其是使用socket的时候得自己分装网络层...
  • Cocos2dx lua 启动流程

    2016-06-21 00:32:38
    cocos2dx 版本 3.x,工具 VS2013 + babelua插件
  • cocos2dx schedule流程

    2020-03-23 15:11:24
  • cocos2dx socket 通信

    2014-07-25 13:13:31
    本文由qinning199原创,转载请注明:http://www.cocos2dx.net/?p=167 最近做一个联网实时交互的游戏,游戏中需要使用socket保持长连接,来保证客户端与服务器端进行实时交互。以下贴出自己的一些代码: 因为socket...
  • cocos2dx渲染机制

    2019-07-16 17:24:49
    cocos2dx渲染原理是cocos引擎工作原理的核心部分,也是面试cocos职位经常考的内容,通过理解渲染机制可以掌握一部分cocos源码 接下来通过源码介绍渲染流程,代码摘自3.10引擎版本 1.main.cpp中调用Application::...
  • cocos引擎是跨平台,如何实现跨平台呢,其实就是用到了OpenGL标准,然后再在各平台上实现某些功能,我习惯上叫为平台层。Android平台游戏打包,一般是游戏本身的工程,Android平台层库,cocos引擎库,然后根据项目...
  • 我们下载的是cocos2dx-js的精简版本,主要是为了分析简单明了,能更清楚的看到架构流程。 下载地址: http://cocos2d-x.org/filecenter/jsbuilder/ 下载轻量版。    ---------------------- 服务器模拟使用...
  • cocos2dx的渲染机制

    2015-08-29 23:27:17
    cocos2dx 渲染机制
  • 怀着一颗忐忑的心情上了班,也是在这个背景下,小编也有缘接触Cocos2dx,这个我以前听都没听过,也从不会去想到自己也会开始使用这样的工具去开发2D游戏。本着一种随遇而安的心态干了起来,很开心,在初次听到cocos...
  • 想要了解cocos2dx的启动流程可以从hellocpp示例项目的源码看起,因为这是cocos的入门项目,就如同我们以前学习第一个程序的hello world一样。 在hellocpp工程中,win32目录下只有一个main文件,而Classes目录下则...
  • 第一,渲染流程2.x到3.x的变化。  在2.x中,渲染过程是通过递归渲染树(Rendering tree)这种图关系来渲染关系图。递归调用visit()函数,并且在visit()函数中调用该节点的draw函数渲染各个节点,此时draw函数的作用...
  • 键盘输入在Cocos2Dx分为两个部分。第一部分是一些功能键的处理:后退键和菜单键。第二部分是处理字符输入。功能键相关的代码位于cocos2dx\keypad_dispatcher。字符输入的代码位于\cocos2dx\text_input_node。 ...
1 2 3 4 5 ... 20
收藏数 2,708
精华内容 1,083
关键字:

2dx流程 cocos