cocos2d-x学习之二:HelloWorld运行原理浅析

HelloWorld 工程目录结构

上文 中,我们使用create_project.py脚本创建了一个HelloWorld工程,项目的目录结构如下:

helloworld_explorer.png

main.cpp

main.cpp很简单,主要工作是设置窗口名和窗口大小,然后进入HelloWorld的消息循环,其中2句代码需要关注

//创建HelloWorld实例
AppDelegate app;

//此处省略...

//进入HelloWorld的消息循环
return CCApplication::sharedApplication()->run();

这里的2句代码是如何关联起来的? 先看下文

AppDelegate类

  • AppDelegate.h中,可以看出AppDelegate类私有继承于CCApplication,然后实现了以下3个虚函数:
//程序启动后调用的初始化函数,如果返回false,则初始化失败,运行中止
virtual bool applicationDidFinishLaunching();

//程序进入后台时调用的函数,比如最小化
virtual void applicationDidEnterBackground();

//程序进入前台前调用的函数,比如从最小化恢复
virtual void applicationWillEnterForeground();
  • 在父类CCApplication中,可以看到其又继承于CCApplicationProtocolCCApplicationProtocol是一个纯虚类,也就是接口,AppDelegate类实现的那3个虚函数其实就是从这里继承的

  • 父类CCApplication中有一个很重要的静态对象:

static CCApplication * sm_pSharedApplication;

这个对象是在CCApplication的构造函数中用this指针进行再赋值的,也就是当我们实例化一个对象AppDelegate app;的时候,在初始化父类时,会将app对象赋值给这个静态对象。CCApplication提供了一个静态方法来访问这个对象:

//CCApplication.h
static CCApplication* sharedApplication();

//CCApplication.cpp
CCApplication* CCApplication::sharedApplication()
{
    CC_ASSERT(sm_pSharedApplication);
    return sm_pSharedApplication;
}

也就是我们在 main.cpp 里看到的, CCApplication::sharedApplication()->run(); 这句就是调用的app这个对象的 run() ,其实可以稍做修改让逻辑更清晰一点,将AppDelegate.h中的私有继承改为公有继承:

//AppDelegate.h
class  AppDelegate : public cocos2d::CCApplication
{
    //...
};

修改 main.cpprun() 函数的调用:

//创建HelloWorld实例
AppDelegate app;

//...

//进入HelloWorld的消息循环
return app.run();
  • run()函数里其实就是封装了win32的窗口类注册、窗口创建和消息循环,我们只需要关注3个地方:
//CCApplication.cpp
        
int CCApplication::run()
{
    //...
            
    // Initialize instance and cocos2d.
    // 这个地方就是会调用到我们的AppDelegate类里实现的的3个虚函数之一的初始化函数
    if(!applicationDidFinishLaunching())
    {
        return 0;
    }
            
    //这里进行窗口的创建,sharedOpenGLView会创建一个静态实例,这个实例初始化的时候会进行窗口的注册和创建
    CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView();
    pMainWnd->centerWindow();
    ShowWindow(pMainWnd->getHWnd(), SW_SHOW);

    //win32消息循环
    while(1)
    {
        //...
               
        //这里进行游戏画面的渲染
        CCDirectory::sharedDirector()->mainLoop();
    }
}
  • applicationDidFinishLaunching() 函数里进行初始化的设置,是游戏渲染的入口点
bool AppDeleaget::applicationDidFinishLaunching()
{
    //initialize director
    CCDirector* pDirector = CCDirector::sharedDirector();
    CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();

    //将场景控制器(导演)与窗口关联起来
    pDirector->setOpenGLView(pEGLView);

    //turn on display FPS
    //设置显示帧数
    pDirector->setDisplayStats(true);
        
    //set FPS. the default value is 1.0/60 if you don't call this
    //设置默认帧率,有3种(60帧,30帧,15帧/每秒)
    pDirector->setAnimationInterval(1.0 / 60);
        
    //create a secne. it's an autorelease object
    //创建HelloWorld场景
    CCScene* pScene = HelloWorld()::scene();

    //run
    //游戏渲染从此入口
    pDirector->runWithScene(pScene);

    return true;
}
  • 总结

    总的来说,CCApplication 类就是对各个平台下的窗口创建、消息循环等平台相关的细节进行了封装,使得我们不用关心实现的细节,只需要关心暴露出来的CCApplicationProtocol接口,我们的cocos2d-x程序实例需要实现这个接口,然后cocos2d-x底层就会在适当的时候回调这些接口。

HelloWorld类

  • 其实HelloWorldScene.h并不是声明了一个场景类,而是一个图层类,一个场景中可能包含很多图层。

  • HelloWorld类的头文件用一个宏生成了create函数

class HelloWorld : public cocos2d::CCLayer
{
    //...
        
    //implement the "static node()" method manually
    CREATE_FUNC(HelloWorld);
}

CREATE_FUNC 宏用来生成create函数,具体如下:

#define CREATE_FUNC(__TYPE__)\
static __TYPE__* create()\
{\
    __TYPE__* pRet = new __TYPE__();\
    //创建成功时进行初始化
    if(pRet && pRet->init())\
    {\
        //加入自动释放队列
        pRet->autorelease();\
        return pRet;\
    }\
    else\
    {\
        //此处建议先判断指针是否为空再删除,或者直接使用CC_SAFE_DELETE这个宏
        delete pRet;\
        pRet = NULL;\
        return NULL;\
    }\
}
  • HelloWorld类的 scene 函数创建了一个场景,并将自己加入这个场景中
CCScene* HelloWorld::scene()
{
    //'scene' is an autorelease object
    //创建场景
    CCScene* scene = CCScene::create();

    //'layer' is an autorelease object
    //创建HelloWorld图层
    HelloWorld *layer = HelloWorld::create();

    //add layer as a child to scene
    //向场景中添加HelloWorld图层
    scene->addChild(layer);

    //return the scene
    return scene;
}
  • HelloWorld 类的 init 函数对图层进行了初始化,如创建各种显示对象,这个函数是在create 函数中调用的
bool HelloWorld::init()
{
    //1. super init first
    // 调用父类init进行初始化
    if(!CCLayer::init())
    {
        return false;
    }

    //创建菜单对象...
    //创建文本,图片图像...
    //将这些对象按z序添加到图层中...
        
    return true;
}
  • 总结

    cocos2d-x 中,每个应用都有且只有一个导演 CCDirector 负责对场景进行显示和切换,有若干个场景对象,每个场景中又有若干个图层,每个图层中又有若干个显示对象,例如文本,图片等,这样就构成了 cocos2d-x 的渲染体系。一般情况我们都是实现不同的场景,然后根据游戏的状态,通过 CCDirector 对这些场景进行切换,这样就构成了一个完整的游戏。

参考阅读