cocos2d-x启动过程

做游戏~

1.建立cocos2d-x-3.5的项目

进入cocos.py所在的文件夹,使用python cocos.py new HelloCpp3 -p com.cocos2dx.org -l cpp -d ~/destination

2.打开HelloCpp3的文件夹,看一下文件结构:

可以看到proj.android,proj.ios_mac,proj.linux,proj.win8.1-universal,proj.win32,proj.wp8-xaml是与平台相关,而Classes和Resources是与游戏逻辑相关的,其中Classes是放置的代码,而Resources里面则是图片和字体文件。

3.程序结构如下:

cocos2d-x启动过程_第1张图片

Src文件夹下是Classes的文件,分别是平台入口,以及HelloWorldScene的场景类

Win32下main.cpp是Win32的入口类启动程序

4.打开入口程序

cocos2d-x启动过程_第2张图片

5.打开Application类,这个是平台相关的类

看一下run函数

applicationDidFinishLauning()是由Appdelegate实现的,代码如下:

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    //获得导演类
    auto director = Director::getInstance();
   //获得OpenGL实例
    auto glview = director->getOpenGLView();
    if(!glview) {
        glview = GLViewImpl::create("My Game");
	//设置OpenGL视图
        director->setOpenGLView(glview);
    }
    // turn on display FPS
    //显示每秒帧速率,即每秒重绘的帧数
    director->setDisplayStats(true);
    // set FPS. the default value is 1.0/60 if you don't call this
    //<span class="comment"> 设置绘制间隔</span>
    director->setAnimationInterval(1.0 / 60);
    register_all_packages();
    // create a scene. it's an autorelease object
    //绘制场景
    auto scene = HelloWorld::createScene();
    // run
    //设置下一帧的scene
    director->runWithScene(scene);
    return true;
}


6.进入到createScene()里看一下

<pre class="html" name="code">Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    //创建场景
    auto scene = Scene::create();
    //创建layer
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();
    // add layer as a child to scene
    scene->addChild(layer);
    // return the scene
    return scene;
}

 
 

7进入create()里看里面发生了什么

// implement the "static create()" method manually    CREATE_FUNC(HelloWorld);

点击F12进入这个函数,这个函数的具体实现是

#define CREATE_FUNC(__TYPE__) \static __TYPE__* create() \{ \    __TYPE__ *pRet = new(std::nothrow) __TYPE__(); \    if (pRet && pRet->init()) \    { \        pRet->autorelease(); \        return pRet; \    } \    else \    { \        delete pRet; \        pRet = NULL; \        return NULL; \    } \}

这个函数最终会调用HelloWorld里的init(),并返回指向HelloWorld的指针

8.进入HelloWorld类看一下此类的init()的函数

<pre class="html" name="code">bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
	closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label
    //创建字体
    auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
    
    // position the label on the center of the screen
    //设置字体位置
    label->setPosition(Vec2(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - label->getContentSize().height));

    // add the label as a child to this layer
    //设置字体在此层中的位置
    this->addChild(label, 1);

    // add "HelloWorld" splash screen"
    //创建精灵
    auto sprite = Sprite::create("HelloWorld.png");
    //设置精灵位置	
    // position the sprite on the center of the screen
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer
    //设置精灵层次
    this->addChild(sprite, 0);
    
    return true;
}

 
 

9.创建完此帧的显示元素后,run进入mainLoop()

在run中用此 while(!glview->windowShouldClose())控制mainLoop的调用

void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (_restartDirectorInNextLoop)
    {
        _restartDirectorInNextLoop = false;
        restartDirector();
    }
    else if (! _invalid)
    {
        drawScene();
     
        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}
在mainLoop里(转自 http://www.cnblogs.com/liangzhimy/p/4425442.html)

Director::drawScene()  主要做了哪些事呢? 其中省去了一些我觉得无关紧要的小东西,最好你看看源码, 看哪里我没说到。
 
1 计算时间差, 2帧之前的时间差,calculateDeltaTime()
2 定时任务调用 _scheduler->update(_deltaTime);
3 事件处理 EventAfterUpdate
4 设置当前的下一个场景, 主要就是把当前场景释放掉, 之后把_nextScene 设置为当前的场景
5 调用当前场景的渲染方法        _runningScene->render(_renderer);
6 事件处理意思是Visit调用完了, _eventAfterVisit
7 调用渲染引擎进行渲染  _renderer->render();
8 事件处理 _eventAfterDraw
9 调用 openGLView 平台提供的屏幕显示方法, _openGLView->swapBuffers();
 
下面我一个一个的展开, 简单的介绍一下, 比较复杂的留在后面进行分析说明:
void Director::drawScene()
{
    // calculate "global" dt
    calculateDeltaTime();
    
    if (_openGLView)
    {
        _openGLView->pollEvents();
    }

    //tick before glClear: issue #533
    if (! _paused)
    {
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    }

    _renderer->clear();

    /* to avoid flickr, nextScene MUST be here: after tick and before draw.
     * FIXME: Which bug is this one. It seems that it can't be reproduced with v0.9
     */
    if (_nextScene)
    {
        setNextScene();
    }

    pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    
    if (_runningScene)
    {
	#if CC_USE_PHYSICS
        auto physicsWorld = _runningScene->getPhysicsWorld();
        if (physicsWorld && physicsWorld->isAutoStep())
        {
            physicsWorld->update(_deltaTime, false);
        }
	#endif
        //clear draw stats
        _renderer->clearDrawStats();
        
        //render the scene
        _runningScene->render(_renderer);
        
        _eventDispatcher->dispatchEvent(_eventAfterVisit);
    }

    // draw the notifications node
    if (_notificationNode)
    {
        _notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
    }

    if (_displayStats)
    {
        showStats();
    }
    _renderer->render();

    _eventDispatcher->dispatchEvent(_eventAfterDraw);

    popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    _totalFrames++;

    // swap buffers
    if (_openGLView)
    {
        _openGLView->swapBuffers();
    }

    if (_displayStats)
    {
        calculateMPF();
    }
}
1 计算时间差, 2帧之前的时间差 calculateDeltaTime()
 
我们在update(float dt) 中得到的float dt.  就是这里计算的,计算了, 上次调用和这次调用之前的差。 
 
2 定时任务调用 _scheduler->update(_deltaTime);
 
Scheduler 本身就是用来处理更新函数调用的。 我们在自己的场景中经常会使用schedulerUpdate(), 然后实现一个update(float dt) 方法,其实就是在内部把当前的调用请求注册到了 scheudler 中, 之后在这个主循环每帧时进行统一的调用update() 函数. 
这里可以看到, 这个update调用的时机还是蛮早的呢, 啥都没干呢, 就先调用它了。 稍等我要把这点记住。

3 事件处理 EventAfterUpdate

_eventDispatcher->dispatchEvent(_eventAfterUpdate);

触发事件“更新调用完了”, 这里大家也不用纠结, 我们可以简单的认为这是一个通知,发送了一个 “更新调用完了“的通知,之后由事件管理器EventDispatcher 来调用注册了这个事件的函数。 
 
事件处理器Dispatcher 典型的观察者模式, 细节这里不说, 以后单出一章说明。 
 
4 设置当前的下一个场景,其实就场景的处理, 场景的用处理就在这里体现了。 
setNextScene();

主要就是把当前场景释放掉, 之后把_nextScene 设置为当前的场景,还记得我们在调用场景时要调用一个 director->runWithScene(scene);
这里的scene 可以被认为是下一个场景了。 下面是它的几个过程
 
1> 清理正在运行的Scene
_runningScene->release();
2> 设置当前Scene
 _runningScene = _nextScene;
_nextScene->retain();
_nextScene = nullptr;
3> 调用当前场景onEnter
_runningScene->onEnter();

5 调用当前场景的渲染方法        _runningScene->render(_renderer);
 
1> 加载当前的Camera
Camera::_visitingCamera = defaultCamera;
2> 装载当前Camera的投影矩阵  director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION,Camera::_visitingCamera->getViewProjectionMatrix());
3> 调用visit; 这个函数可重要了,这个函数主要是分别调用当前节点下的子节点的visit(), 之后调用了自身的draw将相应的Command命令把渲染请求加入的当前readerer的渲染队列中。   visit(renderer, Mat4::IDENTITY, 0);
3.1 对当前子节点排序 sortAllChildren
3.2 递归调用当前localZOrder 小于当前元素的子元素的visit() 
       
复制代码
// draw children zOrder < 0
        for( ; i < _children.size(); i++ )
        {
            auto node = _children.at(i);

            if ( node && node->_localZOrder < 0 )
                node->visit(renderer, _modelViewTransform, flags);
            else
                break;
        }
复制代码
3.3 调用自身的draw()把指定的渲染命令添加到当前的渲染队列
复制代码
   if (visibleByCamera)
    this->draw(renderer, _modelViewTransform, flags);
 
 下面是一个标准的Sprite的Draw() 
void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    // Don't do calculate the culling if the transform was not updated
    _insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform,_contentSize) : _insideBounds;
    if(_insideBounds)
    {
        _quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(),_blendFunc, &_quad, 1, transform);
        renderer->addCommand(&_quadCommand);
    }
}
3.4 递归调用localZorder大于0的子节点的visit() 
 
 for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
            (*it)->visit(renderer, _modelViewTransform, flags);
 

 

4> 调用渲染引擎进行渲染 renderer->render();
这块渲染相关的, 我不展开了,回头会详细说, 大家知道引擎就是使用它来把元素显示的屏幕上的,它封装了大部分的OpenGL的指令。 
 
6 事件处理意思是Visit调用完了, _eventAfterVisit
 
通知Visit调用完了, 这个visit就是把渲染请求封装成命令,压入Render的渲染队列。 
 
7 调用渲染引擎进行渲染  _renderer->render();
 
其它渲染操作。 就是画东西
 
8 事件处理 _eventAfterDraw
 
通知我画完了。 
 
9 调用 openGLView 平台提供的屏幕显示方法, _openGLView->swapBuffers();
 
所有操作做完了, 这里我们的操作都还只在 OpenGL 里帧缓存中, 我们要把它显示在屏幕上 就要调用 不同平台提供的对OpenGLES 的支持函数, iOS上调用的是 [context_presentRenderbuffer:GL_RENDERBUFFER], 主要就是把帧缓存中的内容真正的
总结:
 
可以看到, 主循环里做了很多工作, 更新,事件处理, 场景切换,渲染, 是不是全了。 我看差不多。 后面继续。。 交换或者说是显示在屏幕上。 










你可能感兴趣的:(cocos2d-x启动过程)