cocos2d-x学习之二:HelloWorld运行原理浅析
HelloWorld
工程目录结构
在上文
中,我们使用create_project.py
脚本创建了一个HelloWorld
工程,项目的目录结构如下:
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
中,可以看到其又继承于CCApplicationProtocol
,CCApplicationProtocol
是一个纯虚类,也就是接口,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.cpp
里 run()
函数的调用:
//创建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
对这些场景进行切换,这样就构成了一个完整的游戏。