cocos2d x 3.3 015 程序入口main - cheyiliu/All-in-One GitHub Wiki

程序入口main

  • 到目前为止,该仔细看看我们的main函数了

proj.linux

  • cpp-empty-test/proj.linux/main.cpp
int main(int argc, char **argv)
{
    // create the application instance
//栈变量
    AppDelegate app;
//看情形是单例,并执行run,和上面的变量啥关系? 继续走起
    return Application::getInstance()->run();
}
  • cocos/cocos2d.h
该文件中有这样一段,在这里开始区分不同平台的实现方式了

#if (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
    #include "platform/linux/CCApplication-linux.h"
    #include "platform/desktop/CCGLViewImpl-desktop.h"
    #include "platform/linux/CCGL-linux.h"
    #include "platform/linux/CCStdC-linux.h"
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_LINUX

CC_TARGET_PLATFORM的定义是在platform/CCPlatformConfig.h
  • platform/linux/CCApplication-linux.h
继承自ApplicationProtocol,单例
class Application : public ApplicationProtocol
{
public:
//构造析构
	Application();
	virtual ~Application();

//导演用来设置fps上限,每一帧的最大间隔,在导演章节中再看
	void setAnimationInterval(double interval);

//开始消息循环
	int run();

//单例
	static Application* getInstance();

//获取当前系统语言
	virtual LanguageType getCurrentLanguage();

//获取当前语言编码iso 639-1
    virtual const char * getCurrentLanguageCode();
    
//在默认浏览器打开指定的url
  virtual bool openURL(const std::string &url);


//  @deprecated Please use FileUtils::getInstance()->setSearchPaths() instead.
    CC_DEPRECATED_ATTRIBUTE void setResourceRootPath(const std::string& rootResDir);
    

//  @deprecated Please use FileUtils::getInstance()->getSearchPaths() instead. 
    CC_DEPRECATED_ATTRIBUTE const std::string& getResourceRootPath(void);
    
//获取目标平台的枚举值,Platform在父类中定义
    virtual Platform getTargetPlatform();
protected:
    long       _animationInterval;  //micro second
    std::string _resourceRootPath;
    
	static Application * sm_pSharedApplication;
};
  • CCApplication-linux.cpp
//静态变量初始化
// sharedApplication pointer
Application * Application::sm_pSharedApplication = 0;

//本地方法,获取当前毫秒
static long getCurrentMillSecond() {
    long lLastTime;
    struct timeval stCurrentTime;

    gettimeofday(&stCurrentTime,NULL);//man gettimeofday 查看详细说明
    lLastTime = stCurrentTime.tv_sec*1000+stCurrentTime.tv_usec*0.001; //millseconds
    return lLastTime;
}

//构造
Application::Application()
: _animationInterval(1.0f/60.0f*1000.0f)
{
    CC_ASSERT(! sm_pSharedApplication);
    sm_pSharedApplication = this;//单例
}

//析构
Application::~Application()
{
    CC_ASSERT(this == sm_pSharedApplication);
    sm_pSharedApplication = NULL;
}

//重点,整个程序的大循环开始了
int Application::run()
{
    initGLContextAttrs();
    // Initialize instance and cocos2d.
//子类实现的相关初始化操作,最重要的是调用导演director->runWithScene(scene);
    if (! applicationDidFinishLaunching())
    {
        return 0;
    }

    long lastTime = 0L;
    long curTime = 0L;

    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();

    // Retain glview to avoid glview being released in the while loop
    glview->retain();

//大循环开启
    while (!glview->windowShouldClose())
    {
        lastTime = getCurrentMillSecond();

//每一次循环调用导演的mainLoop
        director->mainLoop();
        glview->pollEvents();

//fps上限判断,curTime - lastTime是mainLoop的一次执行的时间开销,理解为一帧
//这一帧的开销小于了设定的上限值,sleep
        curTime = getCurrentMillSecond();
        if (curTime - lastTime < _animationInterval)
        {
            usleep((_animationInterval - curTime + lastTime)*1000);
        }
    }
    /* Only work on Desktop
    *  Director::mainLoop is really one frame logic
    *  when we want to close the window, we should call Director::end();
    *  then call Director::mainLoop to do release of internal resources
    */
    if (glview->isOpenGLReady())
    {
//退出循环了,调用导演的end, 收工了
        director->end();
//看原来的comments,貌似是个特殊处理,漂过
        director->mainLoop();
        director = nullptr;
    }
//和前面retain对应
    glview->release();
    return -1;
}

void Application::setAnimationInterval(double interval)
{
    //TODO do something else
    _animationInterval = interval*1000.0f;
}

void Application::setResourceRootPath(const std::string& rootResDir)
{
    _resourceRootPath = rootResDir;
    if (_resourceRootPath[_resourceRootPath.length() - 1] != '/')
    {
        _resourceRootPath += '/';
    }
    FileUtils* pFileUtils = FileUtils::getInstance();
    std::vector<std::string> searchPaths = pFileUtils->getSearchPaths();
    searchPaths.insert(searchPaths.begin(), _resourceRootPath);
    pFileUtils->setSearchPaths(searchPaths);
}

const std::string& Application::getResourceRootPath(void)
{
    return _resourceRootPath;
}

Application::Platform Application::getTargetPlatform()
{
    return Platform::OS_LINUX;
}

bool Application::openURL(const std::string &url)
{
    std::string op = std::string("open ").append(url);
    return system(op.c_str())!=-1;
}

//////////////////////////////////////////////////////////////////////////
// static member function
//////////////////////////////////////////////////////////////////////////
Application* Application::getInstance()
{
    CC_ASSERT(sm_pSharedApplication);
    return sm_pSharedApplication;
}

// @deprecated Use getInstance() instead
Application* Application::sharedApplication()
{
    return Application::getInstance();
}

const char * Application::getCurrentLanguageCode()
{
    static char code[3]={0};
    char *pLanguageName = getenv("LANG");
    if (!pLanguageName)
        return "en";
    strtok(pLanguageName, "_");
    if (!pLanguageName)
        return "en";
    strncpy(code,pLanguageName,2);
    code[2]='\0';
    return code;
}

LanguageType Application::getCurrentLanguage()
{
	//略了
}

小结

  • 这几个类的关系(proj.linux)
ApplicationProtocol 负责定义接口
Application继承接口ApplicationProtocol,实现部分具体平台相关的接口
AppDelegate继承具体平台的Application,实现平台无关的游戏逻辑了

回到文首的问题
    AppDelegate app;
    return Application::getInstance()->run();//这个run方法的本体是app了。。。

  • proj.win32的几个类,大致和上面类似
  • proj.android留着单独讲解
  • 其他的不知了
  • 从这个实现我们可以学到点什么或者看出呢? 比如如何跨平台,首先要提供好的抽象,找出各个平台公共的部分实现代码共享,提供各个平台具体相关的默认实现让用户不用关注这些平台相关的细节。java的跨平台性本质也是这样,语言级的是公共的各个平台可共享的,虚拟机部分则是具体平台相关的实现了。
  • AppDelegate中的3个重要的方法在上面代码中出现了一个applicationDidFinishLaunching,另外两个关于前后台切换的呢?(applicationDidEnterBackground applicationWillEnterForeground)通过搜索代码可以看到这两个方法的调用受各个平台的实现影响了,比如android是在onpause onresume中调用的,具体代码细节这里不跟了

扩展阅读