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();
    }
}
posted @ 2016-09-28 22:08  绿茵飞狼  阅读(1252)  评论(0编辑  收藏  举报