Cocos2d-lua下的Mac/ios启动流程

Cocos2d-lua下的Mac/ios启动流程


前言

深刻源码,学习Application,ApplicationProtocol,AppDelegate三个类的做用,以及相互之间的关系,而且了解cocos2d-lua下mac/ios的启动流程。
ios/mac下的AppController—>AppDelegate—>cocos2d::Application(ios/mac)::run()
linux


提示:如下是本篇文章正文内容,下面案例可供参考android

1、新建cocos2d-lua工程

使用命令新建工程ios

cocos new -l lua -p com.game.myluatest -d ./ MyGameLuaTestweb

2、使用xcode打开/MyGameLuaTest.xcodeproj工程

使用xcode打开MyGameLuaTest/frameworks/runtime-src/proj.ios_mac/MyGameLuaTest.xcodeproj工程。左边栏目项目代码中MyGameLuaTest下ios/mac就是对应平台下的启动源码:
xcode

1.ios下的AppController.mm的源码:

// cocos2d application instance
static AppDelegate s_sharedApplication;

建立了一个静态全局AppDegate对象,直到程序结束,自动销毁。
缓存

2.接下来看看AppDelegate.h的源码:

class  AppDelegate : private cocos2d::Application
{ 
 
  
public:
    AppDelegate();
    virtual ~AppDelegate();

    virtual void initGLContextAttrs();

    /** @brief Implement Director and Scene init code here. @return true Initialize success, app continue. @return false Initialize failed, app terminate. */
    virtual bool applicationDidFinishLaunching();

    /** @brief The function be called when the application enter background @param the pointer of the application */
    virtual void applicationDidEnterBackground();

    /** @brief The function be called when the application enter foreground @param the pointer of the application */
    virtual void applicationWillEnterForeground();
};

因而可知AppDelegate继承了Application。有4个方法是由AppDelegate类中实现。这4个方法来自于ApplicationProtocol。app

如下是ApplicationProtocol.h源码:ide

/** Subclass override the function to set OpenGL context attribution instead of use default value. * And now can only set six attributions:redBits,greenBits,blueBits,alphaBits,depthBits,stencilBits. * Default value are(5,6,5,0,16,0), usually use as follows: * void AppDelegate::initGLContextAttrs(){ * GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8}; * GLView::setGLContextAttrs(glContextAttrs); * } */
    virtual void initGLContextAttrs() { 
 
  }
    /** * @brief Implement Director and Scene init code here. * @return true Initialize success, app continue. * @return false Initialize failed, app terminate. * @js NA * @lua NA */
    virtual bool applicationDidFinishLaunching() = 0;

    /** * @brief This function will be called when the application enters background. * @js NA * @lua NA */
    virtual void applicationDidEnterBackground() = 0;

    /** * @brief This function will be called when the application enters foreground. * @js NA * @lua NA */
    virtual void applicationWillEnterForeground() = 0;

然而 cocos2d::Application 又继承了 ApplicationProtocol,而且AppDelegate中只有4个方法:
1.initGLContextAttrs:初始化glContext;
2.applicationDidFinishLaunching:app加载完成,在此方法实现Director以及Scene的初始化;
3.applicationDidEnterBackground:app进入后台时候的回调方法;
4.applicationWillEnterForeground:app进入到前台的时候的回调方法;
svg

cocos2dx经过设置app代理的方式,在只修改AppDelegate中的代码来实现跨平台功能;即若是须要在回到后台时候,实现一些功能,好比清理一些不经常使用的文理缓存,只须要在applicationDidEnterBackground的方法中实现,便可实现跨平台的效果。即各个平台(ios/mac/android/win32/winrt/linux)同时适用。为何,接下来继续看下面代码。函数

2.接下来看看CCApplication.h的源码:

#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
#include "platform/mac/CCApplication-mac.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_IOS
#include "platform/ios/CCApplication-ios.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "platform/android/CCApplication-android.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
#include "platform/win32/CCApplication-win32.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
#include "platform/winrt/CCApplication.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_LINUX
#include "platform/linux/CCApplication-linux.h"
#endif

在头文件源码中,在不一样平台下引入不一样的CCApplication的头文件

4.以CCApplication-ios.h/CCApplication-ios.mm的源码为例:

如下是CCApplication-ios.h源码:继承了ApplicationProtocol

//在CCApplication-ios.h文件中
class CC_DLL Application : public ApplicationProtocol
...省略若干
/** @brief Run the message loop. */
int run();
    
/** @brief Get the current application instance. @return Current application instance pointer. */
static Application* getInstance();

protected:
    static Application * sm_pSharedApplication;

如下是CCApplication-ios.mm源码:

Application* Application::sm_pSharedApplication = 0;

Application::Application()
{ 
 
  
    CC_ASSERT(! sm_pSharedApplication);
    sm_pSharedApplication = this;
}
/
// static member function
//

Application* Application::getInstance()
{ 
 
  
    CC_ASSERT(sm_pSharedApplication);
    return sm_pSharedApplication;
}

回到第一点中的AppController.mm的源码,初始化全局静态的AppDelegate的对象;其实默认调用了父类的CCApplication无参数构造函数。即次例CCApplication-ios.mm中的静态成员变量sm_pSharedApplication是自己CCApplication对象实例的一个指针。所以cocos2d::Application *app = cocos2d::Application::getInstance()返回的是各个平台下CCApplication对象的实例。

AppController.mm源码:

// cocos2d application instance
static AppDelegate s_sharedApplication;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 
 
  
    
    cocos2d::Application *app = cocos2d::Application::getInstance();
    ...省略若干行代码
    //run the cocos2d-x game scene
    app->run();

    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application { 
 
  
    /* Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. If your application supports background execution, called instead of applicationWillTerminate: when the user quits. */
    cocos2d::Application::getInstance()->applicationDidEnterBackground();
}

- (void)applicationWillEnterForeground:(UIApplication *)application { 
 
  
    /* Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background. */
    cocos2d::Application::getInstance()->applicationWillEnterForeground();
}

1.当AppController.mm中调用app->run();方法时候,就是调用了CCApplication其父类ApplicationProtocol中的applicationDidFinishLaunching方法,可是惟一实现就是AppDelegate文件中。
2.当ios的AppController.mm进入applicationDidEnterBackground方法时候(应用转到后台)会调用cocos2d::Application::getInstance()->applicationDidEnterBackground();
3.当iosAppController进入applicationWillEnterForegroundy应用将要前台)的时候调用cocos2d::Application::getInstance()->applicationWillEnterForeground();

CCApplication-ios.mm源码:

int Application::run()
{ 
 
  
    if (applicationDidFinishLaunching()) 
    { 
 
  
        [[CCDirectorCaller sharedDirectorCaller] startMainLoop];
    }
    return 0;
}

而后调用[[CCDirectorCaller sharedDirectorCaller] startMainLoop];
即CCDirectorCaller中的startMainLoop开启ios中屏幕刷新的定时器。CADisplayLink是一个能让咱们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。源码中建立一个新的 CADisplayLink 对象,把它添加到一个runloop中,并给它提供一个 target 和selector 在屏幕刷新的时候调用。在每次刷新屏幕的时候调用

cocos2d::Director* director = cocos2d::Director::getInstance();
director->mainLoop(dt)

CCDirectorCaller.mm源码:

-(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
{ 
 
  
    if (isAppActive) { 
 
  
        cocos2d::Director* director = cocos2d::Director::getInstance();
        EAGLContext* cocos2dxContext = [(CCEAGLView*)director->getOpenGLView()->getEAGLView() context];
        if (cocos2dxContext != [EAGLContext currentContext])
            glFlush();
        
        [EAGLContext setCurrentContext: cocos2dxContext];

        CFTimeInterval dt = ((CADisplayLink*)displayLink).timestamp - lastDisplayTime;
        lastDisplayTime = ((CADisplayLink*)displayLink).timestamp;
        director->mainLoop(dt);
    }
}

此时正式进入Cocos2dx主循环当中。

CCDirector.cpp源码:

void Director::mainLoop(float dt)
{ 
 
  
    _deltaTime = dt;
    _deltaTimePassedByCaller = true;
    mainLoop();
}
void Director::mainLoop()
{ 
 
  
    if (_purgeDirectorInNextLoop)
    { 
 
  
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (_restartDirectorInNextLoop)
    { 
 
  
        _restartDirectorInNextLoop = false;
        restartDirector();
    }
    else if (! _invalid)
    { 
 
  
        drawScene();
     
        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}

2.mac下的Info.plist

总结

以ios启动流程为例,在文件AppController.mm中,建立AppDelegate对象,AppDelegate继承CCApplication,而CCApplication则继承ApplicationProtocol;而后AppController.mm中调用了CCApplication的run方法,而run方法中调用了ApplicationProtocol的applicationDidFinishLaunching方法(实现方法在AppDelegate中),还有开启了ios中屏幕刷新的定时器CADisplayLink,每次屏幕刷新调用doCaller方法,此方法中执行了CCDirector中的mainLoop(dt)方法,从而进入了Cocos2dx的主循环当中。