cocos2d-x3.2源码分析(一)类FileUtils--实现把资源放在Resources文件目录下达到多平台的引用

原创 2014年07月27日 20:42:39

    当你创建TMXTiledMap* tilemap=TMXTiledMap::create("test1.tmx")或Sprite *sprite=Sprite("HelloWorld.p-ng"),有没有产生这样的疑问--为什么把资源test1.tmx和HelloWorld.png放在项目目录下的Resources文件中即可直接引用而不用标明具体路径,并且可以在多个平台下引用?或许很多人就会这样说:“别人告诉我放在这个文件夹中就可以了,我自己使用确实可行,也没有出错,我就没有多去探究了”。如果你想知道这具体原因,就要阅读下面的分析了。如果你并不关心其原因,你可以关闭这个网页了。 

     我以TMXTiledMap::Create函数为讲解对象。

    首先转到TMXTiledMap::Create的定义中,其定义是在CCFastTMXTiledMap.cpp文件中,代码1如下。其目录是E:\mycoscos2d\test2\cocos2d\cocos\2d中,这就说明这是与具体平台无关的,后面我们会看到已具体平台相关的代码。
   代码1: 
<span style="font-size:18px;">TMXTiledMap * TMXTiledMap::create(const std::string& tmxFile)
{
    TMXTiledMap *ret = new TMXTiledMap();
    if (ret->initWithTMXFile(tmxFile))
    {
        ret->autorelease();
        return ret;
    }
    CC_SAFE_DELETE(ret);
    return nullptr;
}</span>
   在代码1中,我们可以看到先创建一个TMXTileMap对象,然后初始化,最后加入自动释放池。如果想了解cosco2d-x3.2内存的管理,请继续关注我的博客。在这里我们也完全没有看到关于路径相关的字符串。其中让人觉得,路径设置有可能在TMXTiledMap()::initWithTMXFile()中,于是我们继续转到TMXTiledMap()::initWithTMXFile()定义中。代码2如下。
   代码2:
<span style="font-size:18px;">bool TMXTiledMap::initWithTMXFile(const std::string& tmxFile)
{
    CCASSERT(tmxFile.size()>0, "FastTMXTiledMap: tmx file should not be empty"); 
    setContentSize(Size::ZERO);
    TMXMapInfo *mapInfo = TMXMapInfo::create(tmxFile);
    if (! mapInfo)
    {
        return false;
    }
    CCASSERT( !mapInfo->getTilesets().empty(), "FastTMXTiledMap: Map not found. Please check the filename.");
    buildWithMapInfo(mapInfo);
    return true;
}</span>
    在代码2中,我们也没有发现关于路径字符串的信息。再看看代码1中只调用了此函数,我们由此推断路径字符串设定在此函数或此函数的调用中的概率非常大。在代码2中,我们可以看到两个函数的调用,TMXMapInfo::create()和buildWithMapInfo(),显然,TMXMapInfo::create的函数名让我们觉得路径字符串的设置在其中概率更大,因此我们转到TMXMapInfo::create代码定义中,其代码在CCTMXXMLParser.cpp文件中,如代码3。其目录是E:\mycoscos2d\test2\cocos2d\cocos\2d,这就说明以平台无关。
  代码3:
<span style="font-size:18px;">TMXMapInfo * TMXMapInfo::create(const std::string& tmxFile)
{
    TMXMapInfo *ret = new TMXMapInfo();
    if(ret->initWithTMXFile(tmxFile))
    {
        ret->autorelease();
        return ret;
    }
    CC_SAFE_DELETE(ret);
    return nullptr;
}</span>
  在代码3中,如同代码1的分析,我们要转到TMXMapInfo::initWithTMXFile()的定义中,如代码4。
  代码4:
<span style="font-size:18px;">bool TMXMapInfo::initWithTMXFile(const std::string& tmxFile)
{
    internalInit(tmxFile, "");
    return parseXMLFile(_TMXFileName.c_str());
}
</span>
  在代码4中,我们还是没有看到路径字符串的设定,如同代码2的分析,我们要转到同一个类中的internalInit()函数中,如代码5。
   代码5:
<span style="font-size:18px;">void TMXMapInfo::internalInit(const std::string& tmxFileName, const std::string& resourcePath)
{
    if (tmxFileName.size() > 0)
    {
        _TMXFileName = FileUtils::getInstance()->fullPathForFilename(tmxFileName);
    }
    if (resourcePath.size() > 0)
    {
        _resources = resourcePath;
    }
    ...
}</span>
   在代码5中,我们终于看到fullpath的字样了,这就说明路径字符串的设定就在眼前了。于是我们就要转到FileUtils::getInstance()->fullPathForFilename()函数定义中,其类定义在CCFileUtils.cpp中,如代码6,由于代码有点长,只贴出关键部分。此时我们看看CCFileUtils.cpp的路径,我们会发现路径是E:\mycoscos2d\test2\cocos2d\cocos\platform,到这里我们终于看到platform这个关键字,这就说明已经到了与平台相关的代码中。
   代码6:
<span style="font-size:18px;">std::string FileUtils::fullPathForFilename(const std::string &filename)
{
    ...
    std::string fullpath; 
    for (auto searchIt = _searchPathArray.cbegin(); searchIt != _searchPathArray.cend(); ++searchIt)
    {
        for (auto resolutionIt = _searchResolutionsOrderArray.cbegin(); resolutionIt != _searchResolutionsOrderArray.cend(); ++resolutionIt)
        {
            fullpath = this->getPathForFilename(newFilename, *resolutionIt, *searchIt);
            
            if (fullpath.length() > 0)
            {
                // Using the filename passed in as key.
                _fullPathCache.insert(std::make_pair(filename, fullpath));
                return fullpath;
            }
        }
    }
   ...
}</span>
    在代码6中,我们看到了this->getPathForFilename(),你会不会觉得奇怪,其他函数的调用都没有加this,就它加了this,具体原因在后面讲解。在这里,显然我们对this->getPathForFilename()的兴趣最大,于是我们就转到其定义中如代码7,其实从后面讲解中,可以看到代码是先转到与平台一致的FileUtilsxxx::getPathForFilename()中,然后再由平台FileUtilsxxx::getPathForFileName 调用FileUtils::getPathForFilename()。平台的getPathForFillname作用是把路径格式转化为符合平台路径的格式。
    代码7:
<span style="font-size:18px;">std::string FileUtils::getPathForFilename(const std::string& filename, const std::string& resolutionDirectory, const std::string& searchPath)
{
    std::string file = filename;
    std::string file_path = "";
    size_t pos = filename.find_last_of("/");
    if (pos != std::string::npos)
    {
        file_path = filename.substr(0, pos+1);
        file = filename.substr(pos+1);
    }
    // searchPath + file_path + resourceDirectory
    std::string path = searchPath;
    path += file_path;
    path += resolutionDirectory;
    path = getFullPathForDirectoryAndFilename(path, file);
    //CCLOG("getPathForFilename, fullPath = %s", path.c_str());
    return path;
}</span>
  在代码 7中,我们看到这个函数作用是把资源路径和一开始create的文件名相连接。我们转到getFullPathFor-
DirectoryAndFilename()函数定义中,如代码8。
  代码8:
<span style="font-size:18px;">std::string FileUtils::getFullPathForDirectoryAndFilename(const std::string& directory, const std::string& filename)
{
    // get directory+filename, safely adding '/' as necessary 
    std::string ret = directory;
    if (directory.size() && directory[directory.size()-1] != '/'){
        ret += '/';
    }
    ret += filename;
    
    // if the file doesn't exist, return an empty string
    if (!isFileExistInternal(ret)) {
        ret = "";
    }
    return ret;
}</span>
   在代码8中,我们看到是字符串的连接,根本没有看到资源路径的获取。于是我们就回到代码7中。
   在代码7中,我们看到searchPath变量,从代码注释中可以看到//searchPath + file_path + resourceDirectory,就可以发现searchPath就是我们路径的名称。
   在代码7中我们看到也只是字符串的连接,而且searchPath是作为参数传入的。于是我们就回到代码6中。
   在代码6中,代码7中的searchPath只是_searchPathArray中的一个迭代器。好,这就说明路径就藏_searchPathArray中,最后我们在CCFileUtils.cpp文件中找到了FileUtils::init(),如代码9。
    代码9:
<span style="font-size:18px;">bool FileUtils::init()
{
    _searchPathArray.push_back(_defaultResRootPath);
    _searchResolutionsOrderArray.push_back("");
    return true;
}</span>
   在代码9中,我们看到了_searchPathArray.push_back(_defaultResRootPath),好的,这就是把路径放进容器中。而又是什么函数调用init()函数?当然是调用代码6中的函数的变量,也我们就回到代码5中this->getInstance()返回的变量。
   于是我们就转到this->getInstance代码中,此时的目录是E:\mycoscos2d\test2\cocos2d\cocos\platform\win32\CCFileUtilsWin32.cpp,好的,终于转到与平台相关的目录中了。注意我用的VS2012来开发,所以才转到win32这个目录中,如果你是Eclipse来开发,你就转到E:\mycoscos2d\test2\cocos2d\cocos\platform\android\CCFileUtils-Android.cpp这个目录中。如果是IOS,你就转到E:\mycoscos2d\test2\cocos2d\cocos\platform\apple\CCFileUtils-Apple.mm。这是为什么转到相关的平台的CCFileUtilsxxx.cpp中呢,这是因为在每个与平台相关的头文件中有#if CC_TARGET_PLATFORM == CC_PLATFORM_XXX的条件预处理,也这样说在那个平台就包含那个头文件。例如:在CCFileUtilsWin32.h文件中有代码10。这是很巧妙的技巧!
   代码10:
<span style="font-size:18px;">#include "base/CCPlatformConfig.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
#include "CCStdC.h"
#include "platform/CCCommon.h"
#include "platform/CCApplicationProtocol.h"
#include <string></span>
    我们来看看this->getInstance()的代码,如代码11。此时的FileUtils* FileUtils::getInstance()是在CCFileUtils-Win32.cpp中的而不是在CCFileUtils.cpp中。这是为了夸平台,s_sharedFileUtils是在FileUtils中定义的。FileUtils-Win32是继承FileUtils的。这是很巧妙的技巧!
  代码11:
<span style="font-size:18px;">FileUtils* FileUtils::getInstance()
{
    if (s_sharedFileUtils == nullptr)
    {
        s_sharedFileUtils = new FileUtilsWin32();
        if(!s_sharedFileUtils->init())
        {
          delete s_sharedFileUtils;
          s_sharedFileUtils = nullptr;
          CCLOG("ERROR: Could not init CCFileUtilsWin32");
        }
    }
    return s_sharedFileUtils;
}</span>
    在代码11中,我们看到s_sharedFileUtils->init(),于是转到定义处,由于此时s_sharedFileUtils是从FileUtilsWin32转换而来的,而且在FileUtils中init()为虚函数,所以init()会转到FileUtilsWin32::init(),而不是FileUtils->init(),这是c++的多态。FileUtilsWin32::init()如代码12。
   代码12:
<span style="font-size:18px;">bool FileUtilsWin32::init()
{
    _checkPath();
    _defaultResRootPath = s_resourcePath;
    return FileUtils::init();
}</span>
   在代码12中,我们看到_checkPath()函数,那就转到它的定义看看,如代码13。
   代码13:
<span style="font-size:18px;">static void _checkPath()
{
    if (0 == s_resourcePath.length())
    {
        WCHAR utf16Path[CC_MAX_PATH] = {0};
        GetCurrentDirectoryW(sizeof(utf16Path)-1, utf16Path);
        
        char utf8Path[CC_MAX_PATH] = {0};
        int nNum = WideCharToMultiByte(CP_UTF8, 0, utf16Path, -1, utf8Path, sizeof(utf8Path), nullptr, nullptr);

        s_resourcePath = convertPathFormatToUnixStyle(utf8Path);
        s_resourcePath.append("/");
    }
}</span>
    好吧,在这里我们终于看到win32平台获得路径的函数GetCurrentDirectoryW(sizeof(utf16Path)-1, utf16Path),这个函数就是获得资源路径的,例如路径E:\mycoscos2d\test2\Resources。到此为止,我们终于找到这个设置资源路径的函数了。在Android平台的代码如代码14,每次在用Eclipse导入项目,会先把Resource的资源复制到E:\mycoscos-2d\test2\proj.android\assets这个路径中,以保持同步。
   代码14:
<span style="font-size:18px;">bool FileUtilsAndroid::init()
{
    _defaultResRootPath = "assets/";
    return FileUtils::init();
}</span>
   回到代码12中,FileUtilsWin32::init()最后还是调用了FileUtils::init(),那我们来看看FileUtils::init()的定义,如代码15。这是很巧妙的机巧!
   代码15:
<span style="font-size:18px;">bool FileUtils::init()
{
    _searchPathArray.push_back(_defaultResRootPath);
    _searchResolutionsOrderArray.push_back("");
    return true;
}
</span>
   在代码15中,路径字符串加入了_searchPathArray容器中!
   我们现在回到代码6中,this->getPathForFilename(newFilename, *resolutionIt, *searchIt),为什么加入this?那就要看看代码6的函数调用者代码5,在代码5中有FileUtils::getInstance(),它返回的是由FileUtilsWin32转换而来的,而FileUtils中getPathForFilename为虚函数,根据C++多态,所以会调用FileUtilsWin32::getPathForFilename()。如代码16。
   代码16:
<span style="font-size:18px;">std::string FileUtilsWin32::getPathForFilename(const std::string& filename, const std::string& resolutionDirectory, const std::string& searchPath)
{
    std::string unixFileName = convertPathFormatToUnixStyle(filename);
    std::string unixResolutionDirectory = convertPathFormatToUnixStyle(resolutionDirectory);
    std::string unixSearchPath = convertPathFormatToUnixStyle(searchPath);
    return FileUtils::getPathForFilename(unixFileName, unixResolutionDirectory, unixSearchPath);
}</span>
  在代码16中,我们看到FileUtilsWin32::getPathForFilename()作用是把路径转换为符合平台的路径格式。
  到此为止,我们详细讲解了cocos2d-x3.2如何通过FileUtils类来实现把资源放在Resources文件目录下达到多平台的引用。
  最后,我们最后用一张图片作为总结。
 

    如需转载,请标明出处,http://blog.csdn.net/cbbbc/article/details/38178753






版权声明:本文为博主原创文章,未经博主允许不得转载。

COCOS2D-X 3.0 Resource下建立一个文件夹,但是文件夹里的资源无法访问

参考: http://blog.csdn.net/wzgyyh/article/details/8277163

cocos2dx win32工程如何自定义资源路径,即Resources的路径

cocos2dx win32工程如何自定义资源路径,即Resources的路径: 1、 因为不同平台下,资源的路径不同,所以cocos2dx需要实现多平台文件 管理系统,CCFileUtils就是文...

Android把res/raw的资源转化为Uri形式访问

(android.resource://) Andorid应用会在打包成Apk时把应用中使用的资源文件都打包进去了,尤其是我们熟悉的assets和res文件夹里面存放的资源文件, 一般情况下我们...

Android中资源文件夹res/raw和assets的使用

*res/raw和assets的相同点:1.两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。*res/raw和assets的不同点:1.res/raw中的文件会被映射到R.j...

Android读取assets目录下的资源 (webview加载assets下的html)

1。获取资源的输入流 资源文件 sample.txt 位于 $PROJECT_HOME/assets/ 目录下,可以在 Activity 中通过 Context.getAssets()....

Android中读取系统图库(包含相册)中的图片,显示图片与图片的路径

最近有一个需求,是类似于手机相机的功能,拍照,多次拍照(调用系统相机只能进行一次拍照),拍照页面显示一个拍照后的缩略图,点击缩略图后进行拍过的照片的筛选(有单选和多选的问题),以及后续操作(设计相关业...
  • ABC__D
  • ABC__D
  • 2016年06月30日 15:48
  • 15489

Cocos2dx 3.x的android studio项目之间.so库、资源移植

有时候,我们可能需要把Cocos2dx的proj.android-studio项目中编译好的.so库移植到我们某个android studio项目中进行使用,本文将详细介绍整个移植过程。图1是coco...

cocos2d-x项目在不同平台设置所使用的资源目录(多个Resources文件夹)

1、在Windows上(PC包) 右键工程项目,选择属性——>配置属性——>调试——>工作目录,就在这里面修改就行。...
  • wcjwdq
  • wcjwdq
  • 2015年03月23日 17:51
  • 712

Cocos2d-x中图字原理之深入分析

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:http://blog.csdn.net/honghaier]   红孩儿Cocos2d-X学习园地QQ群:24994195...

cocos2d-x tile map瓦片地图的黑线及地图抖动解决方案

BUG 1:地图的每个图块之间有一些细密的间隔,特别是场景移动时更加明显,将游戏截图放大,可以很明显看到这个细线            解决方案:通过设置ccConfo.h中的CC_FIX_ARTI...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:cocos2d-x3.2源码分析(一)类FileUtils--实现把资源放在Resources文件目录下达到多平台的引用
举报原因:
原因补充:

(最多只允许输入30个字)