Cocos2d-lua下的Mac/ios啟動流程

2020-09-23 12:00:48

Cocos2d-lua下的Mac/ios啟動流程


前言

深入原始碼,學習Application,ApplicationProtocol,AppDelegate三個類的作用,以及相互之間的關係,並且瞭解cocos2d-lua下mac/ios的啟動流程。
ios/mac下的AppController—>AppDelegate—>cocos2d::Application(ios/mac)::run()


提示:以下是本篇文章正文內容,下面案例可供參考

一、新建cocos2d-lua工程

使用命令新建工程

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

二、使用xcode開啟/MyGameLuaTest.xcodeproj工程

使用xcode開啟MyGameLuaTest/frameworks/runtime-src/proj.ios_mac/MyGameLuaTest.xcodeproj工程。左邊欄目專案程式碼中MyGameLuaTest下ios/mac就是對應平臺下的啟動原始碼:

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。

以下是ApplicationProtocol.h原始碼:

    /** 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進入到前臺的時候的回撥方法;

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的主迴圈當中。