2019独角兽企业重金招聘Python工程师标准>>>
资源管理
资源可以分为两个部分,美术资源和代码资源
美术资源
-
美术资源(图片,预制体)等都在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"); } -
资源的使用
资源是通过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; }
代码资源
-
代码主要指可更新的lua脚本,资源打包在AppConst里面有两个参数选项
//Lua字节码模式-默认关闭 //字节码会把lua脚本转成二进制字节进行存储 //否则,lua脚本是以文本的方式存在 public const bool LuaByteMode = false; //Lua代码AssetBundle模式 //如果开启,代码以目录为单位打包在AssetBundle里面 //否则是以单个文件的形式存在 //具体效果可以看StreamingAssets文件夹里面的输出 public const bool LuaBundleMode = true;
-
代码的管理主要在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; }
通过上述的内容,脚本资源就被加载到内存中,并保存在一个字典里面
-
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()); }
-
关于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);