cocos2dx学习笔记-启动流程
引子
按照官方给出的命令
./cocos.py new HelloWorldDemo -p com.coco2dx.org -l cpp -d ~/Desktop
即可创建工程,运行起来,在HelloWorldScene.cpp可以添加自己的层、精灵等。但我们并不知道cocos是如何处理不同平台运行、如何渲染
本文将通过HelloWorld的例子来跟踪一下程序的启动流程。
首先找到程序的入口,在IOS工程,cocos的入口在AppController类的 didFinishLaunchingWithOptions方法中。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
cocos2d::Application *app = cocos2d::Application::getInstance();
app->initGLContextAttrs();
cocos2d::GLViewImpl::convertAttrs();
// Override point for customization after application launch.
// Add the view controller's view to the window and display.
window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
// Init the CCEAGLView
CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds]
pixelFormat: (NSString*)cocos2d::GLViewImpl::_pixelFormat
depthFormat: cocos2d::GLViewImpl::_depthFormat
preserveBackbuffer: NO
sharegroup: nil
multiSampling: NO
numberOfSamples: 0 ];
// Enable or disable multiple touches
[eaglView setMultipleTouchEnabled:NO];
// Use RootViewController manage CCEAGLView
_viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
_viewController.wantsFullScreenLayout = YES;
_viewController.view = eaglView;
// Set RootViewController to window
if ( [[UIDevice currentDevice].systemVersion floatValue] < 6.0)
{
// warning: addSubView doesn't work on iOS6
[window addSubview: _viewController.view];
}
else
{
// use this method on ios6
[window setRootViewController:_viewController];
}
[window makeKeyAndVisible];
[[UIApplication sharedApplication] setStatusBarHidden:true];
// IMPORTANT: Setting the GLView should be done after creating the RootViewController
cocos2d::GLView *glview = cocos2d::GLViewImpl::createWithEAGLView(eaglView);
cocos2d::Director::getInstance()->setOpenGLView(glview);
app->run();
return YES;
}
在上面代码中,可以看出在完成初始化、设置OpenGL的属性、创建CCEAGView(这是OpenGL最终显示的画布)等操作以后,最终是调用app->run来启动应用的,Application是一个单体类,管理着整个程序运行的逻辑。Application是分平台定义的。定义如下:
// CCApplication.h
class CC_DLL Application : public ApplicationProtocol
{
public:
/**
* @js ctor
*/
Application();
// CCApplication_ios.mm
Application* Application::sm_pSharedApplication = 0;
Application::Application()
{
CC_ASSERT(! sm_pSharedApplication);
sm_pSharedApplication = this;
}
在看了上面Application类的定义后,会发现Application并不是一个严格意义的单体类,构造函数依是public属性,并且没有创建对象,在构造函数中断言sm_pSharedApplication是非空的,否则抛出异常。
回到AppController.mm的开头,有一个静态变量的定义。
// cocos2d application instance
static AppDelegate s_sharedApplication;
查看AppDelegate类定义,它继承于Application,是平台共用的类,在app->run中完成applicationDidFinishLaunching方法的实现。接着程序进入MainLoop。
int Application::run()
{
if (applicationDidFinishLaunching())
{
[[CCDirectorCaller sharedDirectorCaller] startMainLoop];
}
return 0;
}
在applicationDidFinishLaunching中,对游戏环境进行初始化,完成了Scene的创建和运行。
bool AppDelegate::applicationDidFinishLaunching() {
// initialize director
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
if(!glview) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
glview = GLViewImpl::createWithRect("helloDemo", Rect(0, 0, designResolutionSize.width, designResolutionSize.height));
#else
glview = GLViewImpl::create("helloDemo");
#endif
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
director->setAnimationInterval(1.0 / 60);
// Set the design resolution
glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER);
Size frameSize = glview->getFrameSize();
// if the frame's height is larger than the height of medium size.
if (frameSize.height > mediumResolutionSize.height)
{
director->setContentScaleFactor(MIN(largeResolutionSize.height/designResolutionSize.height, largeResolutionSize.width/designResolutionSize.width));
}
// if the frame's height is larger than the height of small size.
else if (frameSize.height > smallResolutionSize.height)
{
director->setContentScaleFactor(MIN(mediumResolutionSize.height/designResolutionSize.height, mediumResolutionSize.width/designResolutionSize.width));
}
// if the frame's height is smaller than the height of medium size.
else
{
director->setContentScaleFactor(MIN(smallResolutionSize.height/designResolutionSize.height, smallResolutionSize.width/designResolutionSize.width));
}
register_all_packages();
// create a scene. it's an autorelease object
auto scene = HelloWorld::createScene();
// run
director->runWithScene(scene);
return true;
}
applicationDidFinishLaunching执行成功进入程序住循环MainLoop,将程序的控制权交到director手中,displayLink设置好回调函数(doCaller)和帧率,通过addToRunLoop加入到RunLoop中去。 每一帧都会回调doCaller方法,而doCaller方法调用了mainLoop方法。
-(void) startMainLoop
{
// Director::setAnimationInterval() is called, we should invalidate it first
[self stopMainLoop];
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];
[displayLink setFrameInterval: self.interval];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
-(void) doCaller: (id) sender
{
cocos2d::Director* director = cocos2d::Director::getInstance();
[EAGLContext setCurrentContext: [(CCEAGLView*)director->getOpenGLView()->getEAGLView() context]];
director->mainLoop();
}
而mainLoop方法的实现是在DisplayLinkDirector类中,进入OpenGL主循环,实现场景绘制。
void DisplayLinkDirector::mainLoop()
{
// 如果调用了director->end(),清理数据
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (_restartDirectorInNextLoop) //如果调用了restart,重置数据
{
_restartDirectorInNextLoop = false;
restartDirector();
}
else if (! _invalid)
{
drawScene();//OpenGL 主循环 绘制场景
// release the objects每一次循环清理一次内存池
PoolManager::getInstance()->getCurrentPool()->clear();
}
}