002,LuaFramework对资源的管理

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

资源管理

资源可以分为两个部分,美术资源和代码资源

美术资源

  1. 美术资源(图片,预制体)等都在ResourceManager里面管理着,在程序启动后,资源从服务器更新完毕,会初始化资源管理器

    在GameManager里面完成初始化

     public void OnResourceInited()
     {
         ResManager.Initialize();
         this.OnInitialize();
     }
    

    Initialize函数会创建bundles字典存储要加载的资源

     public void Initialize() {
         byte[] stream = null;
         string uri = string.Empty;
         bundles = new Dictionary();
    
     	 //这里加载的是StreamingAssets这个AssetBundle
         //但是这个bundle并没有使用
         uri = Util.DataPath + AppConst.AssetDir;
         if (!File.Exists(uri)) return;
         stream = File.ReadAllBytes(uri);
         assetbundle = AssetBundle.LoadFromMemory(stream);
         manifest = assetbundle.LoadAsset("AssetBundleManifest");
     }
    
  2. 资源的使用

    资源是通过ResourceManager提供的接口LoadAsset,LoadPrefab和LoadAssetBundle获取的
    LoadAsset和LoadPrefab最终是要调用LoadAssetBundle函数获取AssetBundle
    LoadAssetBundle首先检查资源有没有被加载过,没有加载过,是要首先从文件读取资源

     public AssetBundle LoadAssetBundle(string abname)
     {
         //加上扩展名,读取文件时用
         if (!abname.EndsWith(AppConst.ExtName))
         {
             abname += AppConst.ExtName;
         }
    
         //如果不在已加载字典里面,则从文件读取并记录到已加载字典里面
         AssetBundle bundle = null;
         if (!bundles.ContainsKey(abname))
         {
             byte[] stream = null;
             string uri = Util.DataPath + abname;
             Debug.LogWarning("LoadFile::>> " + uri);
             LoadDependencies(abname);
    
             stream = File.ReadAllBytes(uri);
             bundle = AssetBundle.LoadFromMemory(stream); //关联数据的素材绑定
             bundles.Add(abname, bundle);
         }
         else
         {
             bundles.TryGetValue(abname, out bundle);
         }
         return bundle;
     }
    

代码资源

  1. 代码主要指可更新的lua脚本,资源打包在AppConst里面有两个参数选项

     //Lua字节码模式-默认关闭
     //字节码会把lua脚本转成二进制字节进行存储
     //否则,lua脚本是以文本的方式存在
     public const bool LuaByteMode = false;
    
     //Lua代码AssetBundle模式
     //如果开启,代码以目录为单位打包在AssetBundle里面
     //否则是以单个文件的形式存在
     //具体效果可以看StreamingAssets文件夹里面的输出
     public const bool LuaBundleMode = true;
    
  2. 代码的管理主要在LuaManager里面完成,LuaManager在GameManager的OnInitialize里面首先初始化

     void OnInitialize()
     {
         LuaManager.InitStart();
     	//Game脚本作为lua代码的入口使用,会对很多东西做初始化工作
         LuaManager.DoFile("Logic/Game");
    
     	...
     }
    

    InitStart函数执行lua脚本执行前的环境初始化工作

     public void InitStart()
     {
         //设置lua脚本的搜索路径
         InitLuaPath();
         //加载lua脚本的AssetBundle,并记录在字典里面
         InitLuaBundle();
         //启动lua虚拟机
         this.lua.Start();
         //开始执行lua函数
         this.StartMain();
         //lua的主循环开始执行
         this.StartLooper();
     }
    

    InitLuaPath函数添加一些lua脚本的搜索路径,这些路径只在LuaBundleMode=false是才有用

     void InitLuaPath()
     {
         if (AppConst.DebugMode)
         {
             string rootPath = AppConst.FrameworkRoot;
             lua.AddSearchPath(rootPath + "/Lua");
             lua.AddSearchPath(rootPath + "/ToLua/Lua");
         }
         else
         {
             lua.AddSearchPath(Util.DataPath + "lua");
         }
     }
    

    InitLuaBundle函数在LuaBundleMode=true的时候才有用,用来添加打包好的资源

     void InitLuaBundle()
     {
     	//beZip的值等于AppConst.LuaBundleMode的值
     	//也就是在Bundle模式下才要加载资源
         if (loader.beZip)
         {
             loader.AddBundle("lua/lua.unity3d");
             loader.AddBundle("lua/lua_math.unity3d");
             loader.AddBundle("lua/lua_system.unity3d");
             loader.AddBundle("lua/lua_system_reflection.unity3d");
             loader.AddBundle("lua/lua_unityengine.unity3d");
             loader.AddBundle("lua/lua_common.unity3d");
             loader.AddBundle("lua/lua_logic.unity3d");
             loader.AddBundle("lua/lua_view.unity3d");
             loader.AddBundle("lua/lua_controller.unity3d");
             loader.AddBundle("lua/lua_misc.unity3d");
    
             loader.AddBundle("lua/lua_protobuf.unity3d");
             loader.AddBundle("lua/lua_3rd_cjson.unity3d");
             loader.AddBundle("lua/lua_3rd_luabitop.unity3d");
             loader.AddBundle("lua/lua_3rd_pbc.unity3d");
             loader.AddBundle("lua/lua_3rd_pblua.unity3d");
             loader.AddBundle("lua/lua_3rd_sproto.unity3d");
    
     		...
         }
     }
    

    loader的AddBundle函数通过bundleName检索文件,并保存一个AssetBundle实例

     public void AddBundle(string bundleName) {
         string url = Util.DataPath + bundleName.ToLower();
         if (File.Exists(url)) {
             var bytes = File.ReadAllBytes(url);
             AssetBundle bundle = AssetBundle.LoadFromMemory(bytes);
             if (bundle != null)
             {
                 bundleName = bundleName.Replace("lua/", "").Replace(".unity3d", "");
                 base.AddSearchBundle(bundleName.ToLower(), bundle);
             }
         }
     }
    

    AddSearchBundle方法就是把AssetBundle实例保存在一个字典里面

     public void AddSearchBundle(string name, AssetBundle bundle)
     {
         zipMap[name] = bundle;
     }
    

    通过上述的内容,脚本资源就被加载到内存中,并保存在一个字典里面

  3. lua脚本资源的使用

    脚本的使用是通过DoFile和lua的require实现的
    LuaState里面的DoFile函数实现对lua文件的使用

     public void DoFile(string fileName)
     {
         //读取文件
         byte[] buffer = LoadFileBuffer(fileName);
         fileName = LuaChunkName(fileName);
         LuaLoadBuffer(buffer, fileName);
     }
    

    LoadFileBuffer调用LuaFileUtils里面的ReadFile函数读取资源文件

     byte[] LoadFileBuffer(string fileName)
     {
     	//读取文件
         byte[] buffer = LuaFileUtils.Instance.ReadFile(fileName);
    
         if (buffer == null)
         {
             string error = string.Format("cannot open {0}: No such file or directory", fileName);
             error += LuaFileUtils.Instance.FindFileError(fileName);
             throw new LuaException(error);
         }
    
         return buffer;
     }
    

    ReadFile函数实现对文件的读取,这里用到了beZip变量,就和LuaManager的InitLuaBundle对应上了

     public virtual byte[] ReadFile(string fileName)
     {
         if (!beZip)
         {
             string path = FindFile(fileName);
             byte[] str = null;
    
             if (!string.IsNullOrEmpty(path) && File.Exists(path))
             {
     			#if !UNITY_WEBPLAYER
                 str = File.ReadAllBytes(path);
     			#else
                 throw new LuaException("can't run in web platform, please switch to other platform");
     			#endif
             }
    
             return str;
         }
         else
         {
             return ReadZipFile(fileName);
         }
     }
    
     如果beZip为false,就是不是打包的资源,FindFile函数会从搜索目录列表里面查找文件
    
     public string FindFile(string fileName)
     {
         if (fileName == string.Empty)
         {
             return string.Empty;
         }
    
         if (Path.IsPathRooted(fileName))
         {
             if (!fileName.EndsWith(".lua"))
             {
                 fileName += ".lua";
             }
    
             return fileName;
         }
    
         if (fileName.EndsWith(".lua"))
         {
             fileName = fileName.Substring(0, fileName.Length - 4);
         }
    
         string fullPath = null;
    
         for (int i = 0; i < searchPaths.Count; i++)
         {
             fullPath = searchPaths[i].Replace("?", fileName);
    
             if (File.Exists(fullPath))
             {
                 return fullPath;
             }
         }
    
         return null;
     }
    
     searchPaths在LuaManager的InitLuaPath函数里面设置
    
     如果beZip为true,ReadZipFile函数从AssetBundle里面读取文件,  
     LuaManager的InitLuaBundle函数里添加的AssetBundle就是用在这里的  
     下面第4节还有具体说明
    
     byte[] ReadZipFile(string fileName)
     {
         AssetBundle zipFile = null;
         byte[] buffer = null;
         string zipName = null;
    
         using (CString.Block())
         {
     		//注意这里的操作,如果在lua脚本里面使用require引用模块
             //一定要加上路径,这个路径相对于AppConst里面的LuaTempDir目录
             //例如require "3rd/pbc/protobuf"
             CString sb = CString.Alloc(256);
             sb.Append("lua");
             int pos = fileName.LastIndexOf('/');
    
             if (pos > 0)
             {
                 sb.Append("_");
                 sb.Append(fileName, 0, pos).ToLower().Replace('/', '_');
                 fileName = fileName.Substring(pos + 1);
             }
    
             if (!fileName.EndsWith(".lua"))
             {
                 fileName += ".lua";
             }
    
     		#if UNITY_5 || UNITY_2017
             fileName += ".bytes";
     		#endif
             zipName = sb.ToString();
             zipMap.TryGetValue(zipName, out zipFile);
         }
    
         if (zipFile != null)
         {
     		#if UNITY_5 || UNITY_2017
             TextAsset luaCode = zipFile.LoadAsset(fileName);
     		#else
             TextAsset luaCode = zipFile.Load(fileName, typeof(TextAsset)) as TextAsset;
     		#endif
             if (luaCode != null)
             {
                 buffer = luaCode.bytes;
                 Resources.UnloadAsset(luaCode);
             }
         }
    
         return buffer;
     }
    

    DoFile里面的LoadFileBuffer读取文件保存在byte[]数组里面,
    然后通过LuaLoadBuffer函数加载数据作为lua的模块使用

     protected void LuaLoadBuffer(byte[] buffer, string chunkName)
     {
         LuaDLL.tolua_pushtraceback(L);
         int oldTop = LuaGetTop();
    
         //这个地方是调用的LuaStatePtr里面的LuaLoadBuffer函数
         //最终调用LuaDLL.luaL_loadbuffer完成加载
         if (LuaLoadBuffer(buffer, buffer.Length, chunkName) == 0)
         {
             if (LuaPCall(0, LuaDLL.LUA_MULTRET, oldTop) == 0)
             {
                 LuaSetTop(oldTop - 1);
                 return;
             }
         }
    
         string err = LuaToString(-1);
         LuaSetTop(oldTop - 1);
         throw new LuaException(err, LuaException.GetLastError());
     }
    
  4. 关于LuaLoader的beZip为true时的一些说明
    LuaLoader通过AddBundle把打包的记录在zipMap这个字典里面
    这个字典的key是文件名称的全小写形式 例如资源包lua/lua_logic.unity3d,最终得到的key是lua_logic
    AddBundle函数里面是通过string的两次Replace实现的

    bundleName = bundleName.Replace("lua/", "").Replace(".unity3d", "");
    base.AddSearchBundle(bundleName.ToLower(), bundle);
    

    通过上面的操作,zipMap里面就存有了lua_logic.unity3d的AssetBundle实例

    zipMap["lua_logic"] = bundle;
    

    有了资源,下面看下使用的时候怎么获得资源的,以下面的require为例:

    require "Logic/CtrlManager"
    

    下面看ReadZipFile函数的执行过程,例如调用函数的参数fileName传入的是"Logic/CtrlManager"
    首先创建一个字符串,并添加'lua'

    CString sb = CString.Alloc(256);
    sb.Append("lua");
    

    然后拆分fileName,找到最后出现'/'的位置,把'/'之前的字符串赋值给创建的新字符串
    同时fileName只记录'/'后面的部分

    int pos = fileName.LastIndexOf('/');
    if (pos > 0)
    {
        sb.Append("_");
        sb.Append(fileName, 0, pos).ToLower().Replace('/', '_');
        fileName = fileName.Substring(pos + 1);
    }
    

    通过上面的操作

    sb = "lua_logic";
    fileName = "CtrlManager";
    

    接下来fileName字符串加上".lua"和".bytes"后缀

    if (!fileName.EndsWith(".lua"))
    {
        fileName += ".lua";
    }
    #if UNITY_5 || UNITY_2017
    fileName += ".bytes";
    #endif
    

    接下来通过zipName获取zipMap里面的资源

    zipName = sb.ToString();//"lua_logic"
    zipMap.TryGetValue(zipName, out zipFile);
    

    获取到资源保存在zipFile里面,通过LoadAsset方法读取指定的文件

    TextAsset luaCode = zipFile.LoadAsset(fileName);
    

以上就是LuaFramwork对资源的管理

转载于:https://my.oschina.net/jacky0525/blog/1789117

你可能感兴趣的:(002,LuaFramework对资源的管理)