Cocos 中脚本文件是如何被载入和执行
Cocos 是可以执行 lua 和 js 的,lua 和 js 都可以认为是解释性语言,不涉及编译和链接步骤,那么文件是如何在Cocos中被载入到内存中并且被执行呢 ?
前提,我们知道不管是IOS 系统还是 Android 系统,Cocos 的执行流程都是需要把MainLoop 执行起来。如果这个流程不太清楚,请参看我上一个帖子 Cocos 代码执行流程
以Cocos lua 为例,前面的代码部分已经解释了,在执行MainLoop 之前,AppDelegate::applicationDidFinishLaunching 函数中会先执行lua 虚拟机的初始化 和 设定 脚本执行的入口文件,我们继续跟踪代码执行,去发现Cocos 在文件操作方面是具有如何的特点。
1
| engine->executeScriptFile("src/main.lua")
|
对于main.lua 我们很熟悉,cocos-lua 中lua脚本执行入口文件。我们知道硬盘可以容纳很多的文件内容,但是这些文件内容在没有被计算机读取的时候就仅仅是存放而已,真正有效是在载入到内存以后被cpu处理的时候,话外题,先pass。我们继续回到main.lua ,同样,在入口的时候一定会先将文件载入到内存,那么文件的IO操作一定少不了。因为使用lua语言编写的,所以lua解释器需要介入和解释文本内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| if(isFileExistence){ FILE *fp = fopen(fullPath.c_str(), mode); fseek(fp,0,SEEK_END); *size = ftell(fp); fseek(fp,0,SEEK_SET); buffer = (unsigned char*)malloc(*size); *size = fread(buffer,sizeof(unsigned char), *size,fp); fclose(fp); ... }
|
如果这里没其他需求,最简单和直接的思路应该是这样的,所以我们转向看下Cocos 的代码部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| static const std::string BYTECODE_FILE_EXT = ".luac"; static const std::string NOT_BYTECODE_FILE_EXT = ".lua"; int LuaStack::executeScriptFile(const char* filename) { std::string buf(filename); ... FileUtils *utils = FileUtils::getInstance(); std::string tmpfilename = buf + NOT_BYTECODE_FILE_EXT; if (utils->isFileExist(tmpfilename)){ buf = tmpfilename; }else{ tmpfilename = buf + BYTECODE_FILE_EXT; if (utils->isFileExist(tmpfilename)){ buf = tmpfilename; } } std::string fullPath = utils->fullPathForFilename(buf); Data data = utils->getDataFromFile(fullPath); int rn = 0; if (!data.isNull()){ if (luaLoadBuffer(_state, (const char*)data.getBytes(), (int)data.getSize(), fullPath.c_str()) == 0){ rn = executeFunction(0); } } return rn; }
|
跟Cocos 这边进行对比发现基本逻辑实现好像差不多(所以我们也可以按照自己意愿封装自己的引擎了)。文本最终执行到了lua虚拟机然后进行函数执行。解释到这里似乎基本上已经完事儿了,是确定这样的吗?会有好奇心想要知道文本如何高效率精准被找到的吗?
1 2 3
| std::string fullPath = utils->fullPathForFilename(buf); Data data = utils->getDataFromFile(fullPath);
|
在我们的FileUtils 中有对外暴露的 setSearchPaths 函数,直面翻译函数名字叫做设置文件搜索路径。在我们不知道内部如何实现的时候我们可以尝试使用自己的思路进行猜测别人的逻辑如何实现,如果最后发现很接近,这样是不是很开心?如果发现不太一样,我们是不是能学到一些别人优秀的设计思想 ?继续回归代码层面,我们先做设想。设置进来的是文件路径数组,那么数组有两个目的,第一临时存储,第二方便遍历。所以从方便遍历的角度来讲进行文件查找,我们推测。
1 2 3 4 5 6 7
| if(isFileNameExistence){ for(int i =0;i < arry.length;i++){ } }
|
我们看下源码实现部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| std::string FileUtils::fullPathForFilename(const std::string &filename) const { if (filename.empty()){ return ""; } if (isAbsolutePath(filename)){ return filename; } auto cacheIter = _fullPathCache.find(filename); if(cacheIter != _fullPathCache.end()){ return cacheIter->second; } const std::string newFilename( getNewFilename(filename) ); std::string fullpath; for (const auto& searchIt : _searchPathArray) { for (const auto& resolutionIt : _searchResolutionsOrderArray){ fullpath = this->getPathForFilename(newFilename, resolutionIt, searchIt); if (!fullpath.empty()){ _fullPathCache.insert(std::make_pair(filename, fullpath)); return fullpath; } } } if(isPopupNotify()){ CCLOG("cocos2d: fullPathForFilename: No file found at %s. Possible missing file.", filename.c_str()); } return ""; }
|
所以看下执行的逻辑和顺序,思考一下别人巧妙的思想。鉴于cocos脚本和资源部分大多数都要走fileUtils ,是不是我们可以做一些更巧妙的东西加入到cocos 的源码中 ?