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

cocos2dx_logo.jpg

HelloWorld 工程目录结构

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

helloworld_explorer.png

main.cpp

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

1
2
3
4
5
6
7
//创建HelloWorld实例
AppDelegate app;

//此处省略...

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

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

AppDelegate类

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

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

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

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

1
static CCApplication * sm_pSharedApplication;

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

1
2
3
4
5
6
7
8
9
//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中的私有继承改为公有继承:

1
2
3
4
5
//AppDelegate.h
class AppDelegate : public cocos2d::CCApplication
{
//...
};

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

1
2
3
4
5
6
7
//创建HelloWorld实例
AppDelegate app;

//...

//进入HelloWorld的消息循环
return app.run();
  • run()函数里其实就是封装了win32的窗口类注册、窗口创建和消息循环,我们只需要关注3个地方:
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
//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() 函数里进行初始化的设置,是游戏渲染的入口点
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
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函数

1
2
3
4
5
6
7
class HelloWorld : public cocos2d::CCLayer
{
//...

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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 函数创建了一个场景,并将自己加入这个场景中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 函数中调用的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool HelloWorld::init()
{
//1. super init first
// 调用父类init进行初始化
if(!CCLayer::init())
{
return false;
}

//创建菜单对象...
//创建文本,图片图像...
//将这些对象按z序添加到图层中...

return true;
}
  • 总结

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

参考阅读