商城网站建设的步骤dz论坛模板

当前位置: 首页 > news >正文

商城网站建设的步骤,dz论坛模板,企业网站首页效果图,大连城乡建设网站安装插件 下载Xlua插件#xff1a;https://github.com/Tencent/xLua 下载完成后#xff0c;把Asset文件夹下的文件拖入自己的工程Asset中#xff0c;看到Unity编辑器上多了个Xlua菜单#xff0c;说明插件导入成功 Lua启动代码 新建一个空场景#xff0c;场景中什么都不…安装插件 下载Xlua插件https://github.com/Tencent/xLua 下载完成后把Asset文件夹下的文件拖入自己的工程Asset中看到Unity编辑器上多了个Xlua菜单说明插件导入成功 Lua启动代码 新建一个空场景场景中什么都不放只有一个启动脚本所有的东西都从启动脚本中加载这样打包时才能没有依赖所有资源支持热更。 启动脚本 GameLaunch using UnityEngine;public class GameLaunch : MonoBehaviour {void Awake() { // 初始化框架this.gameObject.AddComponentshow_fps();this.gameObject.AddComponentxLuaMgr();// end xLuaMgr.Instance.Init();}void Start () {// 进入启动逻辑xLuaMgr.Instance.EnterLuaGame();// end }void Update () {} }xLua管理脚本 using UnityEngine; using System.IO; using XLua;public class xLuaMgr : UnitySingletonxLuaMgr {public const string luaScriptsFolder LuaScripts;const string gameMainScriptName main; // main.lua// Lua解释器的上下文的运行环境private LuaEnv luaEnv null;private bool HasGameStart false;public override void Awake() {base.Awake();}public void SafeDoString(string scriptContent) { // 执行脚本, scriptContent脚本代码的文本内容;if (this.luaEnv ! null) {try {luaEnv.DoString(scriptContent); // 执行我们的脚本代码;}catch (System.Exception ex) {string msg string.Format(xLua exception : {0}\n {1}, ex.Message, ex.StackTrace);Debug.LogError(msg, null);}}}public void LoadScript(string scriptName) { // require(game.game_start) scriptName game.game_startSafeDoString(string.Format(require({0}), scriptName)); // }public void ReloadScript(string scriptName) {SafeDoString(string.Format(package.loaded[{0}] nil, scriptName));LoadScript(scriptName);}public void Init() {this.luaEnv new LuaEnv(); // 添加Lua代码装载器当请求文件的时候调用require会调用对应的CustomLoader函数if (this.luaEnv ! null) {this.luaEnv.AddLoader(CustomLoader);}}public static byte[] CustomLoader(ref string filePath){string scriptPath string.Empty;// 把传递文件路径时修改的点改回斜杠加上尾缀filePath filePath.Replace(., /) .lua; #if UNITY_EDITOR// if (AssetBundleConfig.IsEditorMode){scriptPath Path.Combine(Application.dataPath, luaScriptsFolder);// Assets/LuaScriptsscriptPath Path.Combine(scriptPath, filePath); // Assets/LuaScripts/game/game_start.lua// Debug.Log(Custom Load lua script : scriptPath);return GameUtility.SafeReadAllBytes(scriptPath);} #endif/scriptPath string.Format({0}/{1}.bytes, luaAssetbundleAssetName, filePath);string assetbundleName null;string assetName null;bool status AssetBundleManager.Instance.MapAssetPath(scriptPath, out assetbundleName, out assetName);if (!status){Debug.LogError(MapAssetPath failed : scriptPath);return null;}var asset AssetBundleManager.Instance.GetAssetCache(assetName) as TextAsset;if (asset ! null){return asset.bytes;}Debug.LogError(Load lua script failed : scriptPath , You should preload lua assetbundle first!!!);return null;/}void Start () {}public void EnterLuaGame() { // 进入游戏 if (this.luaEnv ! null) {// 装载main脚本this.LoadScript(gameMainScriptName);// 执行main.start()SafeDoString(main.start());this.HasGameStart true;}}void Update () {} } Main.lua require(game.game_start)main {} – main是一个全局模块;local function start()print(game started) endmain.start start return main通用脚本单例 using UnityEngine;// 实现普通的单例模式 // where 限制模板的类型, new()指的是这个类型必须要能被实例化 public abstract class SingletonT where T : new() {private static T _instance;private static object mutex new object();public static T instance {get {if (_instance null) {lock (mutex) { // 保证我们的单例是线程安全的;if (_instance null) {_instance new T();}}}return _instance;}} }// Monobeavior: 声音, 网络 // Unity单例public class UnitySingletonT : MonoBehaviour where T : Component {private static T _instance null;public static T Instance {get {if (_instance null) {_instance FindObjectOfType(typeof(T)) as T;if (_instance null) {GameObject obj new GameObject();_instance (T)obj.AddComponent(typeof(T));obj.hideFlags HideFlags.DontSave;// obj.hideFlags HideFlags.HideAndDontSave;obj.name typeof(T).Name;}}return _instance;}}public virtual void Awake() {DontDestroyOnLoad(this.gameObject);if (_instance null) {_instance this as T;}else {GameObject.Destroy(this.gameObject);}} }把对应的Lua代码放到LuaScripts文件夹中这些程序就能正常执行了从此C#能正确调用Lua可以使用Lua代替C#进行开发了。 Lua脚本组件化开发模式 关联Update、LateUpdate等 xLuaMgr.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using XLua;public class xLuaMgr : UnitySingletonxLuaMgr {public const string luaScriptsFolder LuaScripts;const string gameMainScriptName main; // main.luaprivate LuaEnv luaEnv null;private bool HasGameStart false;public override void Awake() {base.Awake();}public void SafeDoString(string scriptContent) { // 执行脚本, scriptContent脚本代码的文本内容;if (this.luaEnv ! null) {try {luaEnv.DoString(scriptContent); // 执行我们的脚本代码;}catch (System.Exception ex) {string msg string.Format(xLua exception : {0}\n {1}, ex.Message, ex.StackTrace);Debug.LogError(msg, null);}}}public void LoadScript(string scriptName) { // require(game.game_start) scriptName game.game_startSafeDoString(string.Format(require({0}), scriptName)); // }public void ReloadScript(string scriptName) {SafeDoString(string.Format(package.loaded[{0}] nil, scriptName));LoadScript(scriptName);}public void Init() {this.luaEnv new LuaEnv(); // if (this.luaEnv ! null) {this.luaEnv.AddLoader(CustomLoader);}}// require(main); // require(game.game_start)public static byte[] CustomLoader(ref string filePath){string scriptPath string.Empty;filePath filePath.Replace(., /) .lua; // game/game_start.lua #if UNITY_EDITOR// if (AssetBundleConfig.IsEditorMode){scriptPath Path.Combine(Application.dataPath, luaScriptsFolder);// Assets/LuaScriptsscriptPath Path.Combine(scriptPath, filePath); // Assets/LuaScripts/game/game_start.lua// Debug.Log(Custom Load lua script : scriptPath);return GameUtility.SafeReadAllBytes(scriptPath);} #endif/scriptPath string.Format({0}/{1}.bytes, luaAssetbundleAssetName, filePath);string assetbundleName null;string assetName null;bool status AssetBundleManager.Instance.MapAssetPath(scriptPath, out assetbundleName, out assetName);if (!status){Debug.LogError(MapAssetPath failed : scriptPath);return null;}var asset AssetBundleManager.Instance.GetAssetCache(assetName) as TextAsset;if (asset ! null){return asset.bytes;}Debug.LogError(Load lua script failed : scriptPath , You should preload lua assetbundle first!!!);return null;/}void Start () {}public void EnterLuaGame() { // 进入游戏 if (this.luaEnv ! null) {this.LoadScript(gameMainScriptName);SafeDoString(main.start());this.HasGameStart true;}}void Update () {if (this.HasGameStart) {SafeDoString(main.Update());}}void FixedUpdate() {if (this.HasGameStart) {SafeDoString(main.FixedUpdate());}}void LateUpdate() {if (this.HasGameStart) {SafeDoString(main.LateUpdate());}} } GameLaunch脚本 require(managers.LuaGameObject) local game require(game.start)main {} – main是一个全局模块;local function start()game.init(); endlocal function OnApplicationQuit() endlocal function Update()LuaGameObject.Update() endlocal function FixedUpdate()LuaGameObject.FixedUpdate() endlocal function LateUpdate()LuaGameObject.LateUpdate() endmain.OnApplicationQuit OnApplicationQuit main.Update Update main.FixedUpdate FixedUpdate main.LateUpdate LateUpdate main.start startreturn mainLua组件的基类 所有的组件基类继承自LuaBehaviour – 返回一个基类为base的类;用于继承 function LuaExtend(base) return base:new() endlocal LuaBehaviour {} function LuaBehaviour:new(instant) if not instant then instant {} –类的实例endsetmetatable(instant, {__index self}) return instant end– obj: GameObject – transform, gameObject function LuaBehaviour:init(obj)self.transform obj.transformself.gameObject obj endreturn LuaBehaviour Lua组件化管理 管理脚本 LuaGameObject {}local GameObject CS.UnityEngine.GameObject local GameObjectMap {} – key ObjectID: {lua组件实例1, Lua组件实例2, …};local function Instantiate(prefab)GameObject.Instantiate(prefab) endlocal function Destroy(obj)local obj_id obj:GetInstanceID()if (GameObjectMap[obj_id]) then – 删除掉所有的组件实例table.remove(GameObjectMap, obj_id)endGameObject.Destroy(obj) endlocal function DestroyAfter(obj, afterTime)local obj_id obj:GetInstanceID()if (GameObjectMap[obj_id]) then – 删除掉所有的组件实例table.remove(GameObjectMap, obj_id)endGameObject.Destroy(obj, afterTime) endlocal function Find(name)return GameObject.Find(name) endlocal function AddLuaComponent(obj, lua_class)local componet lua_class:new()componet:init(obj) local obj_id obj:GetInstanceID()if (GameObjectMap[obj_id]) thentable.insert(GameObjectMap[obj_id], componet)elseGameObjectMap[obj_id] {}table.insert(GameObjectMap[obj_id], componet)endif componet.Awake ~ nil then componet:Awake()endreturn componet endlocal function GetLuaComponent(obj, lua_class)return nil endlocal function trigger_update(components_array)local key, valuefor key, value in pairs(components_array) doif value.Update ~ nil thenvalue:Update()endend endlocal function trigger_fixupdate(components_array)local key, valuefor key, value in pairs(components_array) doif value.FixedUpdate ~ nil thenvalue:FixedUpdate()endend endlocal function trigger_lateupdate(components_array)local key, valuefor key, value in pairs(components_array) doif value.LateUpdate ~ nil thenvalue:LateUpdate()endend endlocal function Update()local key, valuefor key, value in pairs(GameObjectMap) dotrigger_update(value)end endlocal function FixedUpdate()local key, valuefor key, value in pairs(GameObjectMap) dotrigger_fixupdate(value)end endlocal function LateUpdate()local key, valuefor key, value in pairs(GameObjectMap) dotrigger_lateupdate(value)end endLuaGameObject.Update Update LuaGameObject.LateUpdate LateUpdate LuaGameObject.FixedUpdate FixedUpdateLuaGameObject.Find Find LuaGameObject.Instantiate Instantiate LuaGameObject.Destroy Destroy LuaGameObject.DestroyAfter DestroyAfter LuaGameObject.AddLuaComponent AddLuaComponent LuaGameObject.GetLuaComponent GetLuaComponentreturn LuaGameObject 添加组件方法 例如有一个控制物体移动的脚本 local LuaBehaviour require(Component.LuaBehaviour) local cube_ctrl LuaExtend(LuaBehaviour)function cube_ctrl:Awake()print(Awake) end function cube_ctrl:Update()self.transform:Translate(0, 0, 5 * CS.UnityEngine.Time.deltaTime) endreturn cube_ctrl 这个脚本除了加上最上面两行和最后一行其他的写法都是C#当中的。 调用这个脚本时 local cube_ctrl require(game.cube_ctrl) local obj LuaGameObject.Find(Cube) LuaGameObject.AddLuaComponent(obj, cube_ctrl) 例子 local start {} local cube_ctrl require(game.cube_ctrl)local function enter_login_scene()print(enter_login_scene)– 放地图– end– 放怪物– end – 放玩家– end– 放UI–end– 测试local obj LuaGameObject.Find(Cube)LuaGameObject.AddLuaComponent(obj, cube_ctrl)– end endlocal function enter_game_scene() endlocal function init()enter_login_scene() endstart.init initreturn start;Unity编辑器创建Lua模板 这个脚本放在Editor目录下作为编辑器脚本 using UnityEngine; using System.Collections; using UnityEditor.ProjectWindowCallback; using System.IO; using UnityEditor; public class CreateLua {[MenuItem(Assets/Create/Lua Script,false,80)] //80是菜单的次序public static void CreateNewLua(){ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0,ScriptableObject.CreateInstanceCreateScriptAssetAction(),GetSelectedPathOrFallback() /New Lua.lua,null,Assets/Editor/Template/LuaComponent.lua);}public static string GetSelectedPathOrFallback(){string path Assets;foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets)){path AssetDatabase.GetAssetPath(obj);if (!string.IsNullOrEmpty(path) File.Exists(path)){path Path.GetDirectoryName(path);break;}}return path;} } class CreateScriptAssetAction:EndNameEditAction {public override void Action(int instanceId, string pathName, string resourceFile){//创建资源UnityEngine.Object obj CreateAssetFromTemplate(pathName, resourceFile);//高亮显示该资源ProjectWindowUtil.ShowCreatedAsset(obj);}internal static UnityEngine.Object CreateAssetFromTemplate(string pahtName, string resourceFile){//获取要创建的资源的绝对路径string fullName Path.GetFullPath(pahtName);//读取本地模板文件StreamReader reader new StreamReader(resourceFile);string content reader.ReadToEnd();reader.Close();//获取资源的文件名// string fileName Path.GetFileNameWithoutExtension(pahtName);//替换默认的文件名content content.Replace(#TIME, System.DateTime.Now.ToString(yyyy年MM月dd日 HH:mm:ss dddd));//写入新文件StreamWriter writer new StreamWriter(fullName, false, new System.Text.UTF8Encoding(false));writer.Write(content);writer.Close();//刷新本地资源AssetDatabase.ImportAsset(pahtName);AssetDatabase.Refresh();return AssetDatabase.LoadAssetAtPath(pahtName, typeof(UnityEngine.Object));} }在Editor目录下创建Template文件夹存放模板里面的文件如下 local LuaBehaviour require(Component.LuaBehaviour) local newclass LuaExtend(LuaBehaviour)function newclass:Awake() end function newclass:Update() endreturn newclass 这样在右键菜单Create中就会多出一个Lua Script的选项可以直接创建Lua的模板文件。 Lua调用Unity相关组件和接口 Unity编辑器相关CS.UnityEngine例如CS.UnityEngine.Time.deltaTime 自己定义的类CS.命名空间.类名 为了方便我们可以在lua中重新进行定义 local Time CS.UnityEngine.Time另外在上面组件化开发模式中我们可以直接才lua模块中使用obj.gameObject,obj.transform来获取对应物体和对应物体的transform的信息。 C#端创建一个脚本 using UnityEngine; using System; using XLua;[LuaCallCSharp] // Lua 能否调用到这个装饰器很重要; public class ResMgr : UnitySingletonResMgr {public override void Awake() {base.Awake();}public UnityEngine.Object GetAssetCache(string name, string type_name) {Debug.Log(UnityEngine: GetAssetCache);return null;}public void LoadAssetBundleAsync(string assetbundleName, Action end_func){end_func();// this.StartCoroutine(this.IE_LoadAssetBundleAsync(assetbundleName, end_func));} }Lua调用这个脚本 local ResMgr {}local cs_ResMgr CS.ResMgr.Instance local function GetAssetCache(name, type_name)cs_ResMgr:GetAssetCache(name, type_name) end ResMgr.GetAssetCache GetAssetCachelocal function LoadAssetBundleAsync(name, callback)cs_ResMgr:LoadAssetBundleAsync(name, callback) endResMgr.LoadAssetBundleAsync LoadAssetBundleAsync return ResMgr使用的时候 local ResMgr require(managers.ResMgr) ResMgr.GetAssetCache(test,test_type) –回调函数 ResMgr.LoadAssetBundleAsync(name, function() print(C#) end) 资源管理 编辑器内部资源启动和AssetBundle启动游戏 启动游戏脚本 using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using AssetBundles; using GameChannel;public class GameLaunch : MonoBehaviour {void Awake() { // 初始化框架this.gameObject.AddComponentshow_fps();this.gameObject.AddComponentxLuaMgr();this.gameObject.AddComponentResMgr();// end xLuaMgr.Instance.Init();}IEnumerator InitPackageName(){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){yield break;} #endif// 获取渠道名字在文件中设置var packageNameRequest AssetBundleManager.Instance.RequestAssetFileAsync(BuildUtils.PackageNameFileName);yield return packageNameRequest;var packageName packageNameRequest.text;packageNameRequest.Dispose();AssetBundleManager.ManifestBundleName packageName;// 初始化渠道ChannelManager.instance.Init(packageName);Debug.Log(string.Format(packageName {0}, packageName));yield break;}IEnumerator GameStart(){var start DateTime.Now;yield return InitPackageName();Debug.Log(string.Format(InitPackageName use {0}ms, (DateTime.Now - start).Milliseconds));// 启动资源管理模块start DateTime.Now;yield return AssetBundleManager.Instance.Initialize();Debug.Log(string.Format(AssetBundleManager Initialize use {0}ms, (DateTime.Now - start).Milliseconds));string luaAssetbundleName xLuaMgr.Instance.AssetbundleName;// lua脚本AssetBundle装载进内存AssetBundleManager.Instance.SetAssetBundleResident(luaAssetbundleName, true);var abloader AssetBundleManager.Instance.LoadAssetBundleAsync(luaAssetbundleName);yield return abloader;abloader.Dispose();xLuaMgr.Instance.EnterLuaGame();yield break;}void Start () {this.StartCoroutine(this.GameStart());}void Update () {} } XLuaManager using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using XLua; using AssetBundles;public class xLuaMgr : UnitySingletonxLuaMgr {public const string luaScriptsFolder LuaScripts;public const string luaAssetbundleAssetName Lua;const string gameMainScriptName main; // main.luaprivate LuaEnv luaEnv null;private bool HasGameStart false;public override void Awake() {base.Awake();string path AssetBundleUtility.PackagePathToAssetsPath(luaAssetbundleAssetName);AssetbundleName AssetBundleUtility.AssetBundlePathToAssetBundleName(path);}public string AssetbundleName {get;protected set;}public void SafeDoString(string scriptContent) { // 执行脚本, scriptContent脚本代码的文本内容;if (this.luaEnv ! null) {try {luaEnv.DoString(scriptContent); // 执行我们的脚本代码;}catch (System.Exception ex) {string msg string.Format(xLua exception : {0}\n {1}, ex.Message, ex.StackTrace);Debug.LogError(msg, null);}}}public void LoadScript(string scriptName) { // require(game.game_start) scriptName game.game_startSafeDoString(string.Format(require({0}), scriptName)); // }public void ReloadScript(string scriptName) {SafeDoString(string.Format(package.loaded[{0}] nil, scriptName));LoadScript(scriptName);}public void Init() {this.luaEnv new LuaEnv(); // if (this.luaEnv ! null) {this.luaEnv.AddLoader(CustomLoader);}}// require(main); // require(game.game_start)public static byte[] CustomLoader(ref string filePath){string scriptPath string.Empty;filePath filePath.Replace(., /) .lua; // game/game_start.lua// 编辑器模式直接从本地lua文件读代码 #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode) {scriptPath Path.Combine(Application.dataPath, luaScriptsFolder);// Assets/LuaScriptsscriptPath Path.Combine(scriptPath, filePath); // Assets/LuaScripts/game/game_start.luabyte[] data GameUtility.SafeReadAllBytes(scriptPath);return data;} #endif// 非编辑器模式从AssetBundle读scriptPath string.Format({0}/{1}.bytes, luaAssetbundleAssetName, filePath);string assetbundleName null;string assetName null;bool status AssetBundleManager.Instance.MapAssetPath(scriptPath, out assetbundleName, out assetName);if (!status){Debug.LogError(MapAssetPath failed : scriptPath);return null;}var asset AssetBundleManager.Instance.GetAssetCache(assetName) as TextAsset;if (asset ! null){return asset.bytes;}Debug.LogError(Load lua script failed : scriptPath , You should preload lua assetbundle first!!!);return null;}void Start () {}public void EnterLuaGame() { // 进入游戏 if (this.luaEnv ! null) {this.LoadScript(gameMainScriptName);SafeDoString(main.start());this.HasGameStart true;}}void Update () {if (this.HasGameStart) {SafeDoString(main.Update());}}void FixedUpdate() {if (this.HasGameStart) {SafeDoString(main.FixedUpdate());}}void LateUpdate() {if (this.HasGameStart) {SafeDoString(main.LateUpdate());}} }/** local ResMgr {}local cs_ResMgr CS.ResMgr.Instance local function GetAssetCache(name, type_name)cs_ResMgr:GetAssetCache(name, type_name) endreturn ResMgr*/管理器代码 渠道管理器框架 using System; using XLua;namespace GameChannel {[Hotfix][LuaCallCSharp]public class ChannelManager : SingletonChannelManager{private BaseChannel channel null;private Action initDelFun null;public Action downLoadGameSucceed null;public Action downLoadGameFail null;public Actionint downLoadGameProgress null;public string packageName{get;protected set;}public string noticeVersion{get;set;}public string resVersion{get;set;}public string appVersion{get;set;}public string svnVersion{get;set;}public void Init(string packageName){this.packageName packageName;channel CreateChannel(packageName);}public BaseChannel CreateChannel(string packageName){ChannelType platName (ChannelType)Enum.Parse(typeof(ChannelType), packageName);switch ((platName)){case ChannelType.Test:return new TestChannel();default:return new TestChannel();}}public void InitSDK(Action delFun){initDelFun delFun;channel.Init();channel.DataTrackInit();}public void InitSDKComplete(string msg){// Logger.platChannel packageName;if (initDelFun ! null){initDelFun.Invoke();initDelFun null;}}public void StartDownLoadGame(string url, Action succeed null, Action fail null, Actionint progress null, string saveName null){downLoadGameSucceed succeed;downLoadGameFail fail;downLoadGameProgress progress;channel.DownloadGame(url, saveName);}public void DownLoadGameEnd(bool succeed){if (succeed){if (downLoadGameSucceed ! null){downLoadGameSucceed.Invoke();}}else{if (downLoadGameFail ! null){downLoadGameFail.Invoke();}}downLoadGameSucceed null;downLoadGameFail null;downLoadGameProgress null;}public void DownLoadGameProgress(int progress){if (downLoadGameProgress ! null){downLoadGameProgress.Invoke(progress);}}public void InstallGame(Action succeed, Action fail){downLoadGameSucceed succeed;downLoadGameFail fail;AndroidSDKHelper.FuncCall(InstallApk);}public bool IsInternalVersion(){if (channel null){return true;}return channel.IsInternalChannel();}/public override void Dispose(){}/} } AssetBundle管理器框架 using UnityEngine; using System.Collections; using System.Collections.Generic; using XLua; using System; #if UNITY_EDITOR using UnityEditor; #endif/// summary /// added by wsh 2017-12-21 /// 功能assetbundle管理类为外部提供统一的资源加载界面、协调Assetbundle各个子系统的运行 /// 注意 /// 1、抛弃Resources目录的使用官方建议https://unity3d.com/cn/learn/tutorials/temas/best-practices/resources-folder?playlist30089 /// 2、提供Editor和Simulate模式前者不适用Assetbundle直接加载资源快速开发后者使用Assetbundle用本地服务器模拟资源更新 /// 3、场景不进行打包场景资源打包为预设 /// 4、只提供异步接口所有加载按异步进行 /// 5、采用LZMA压缩方式性能瓶颈在Assetbundle加载上ab加载异步asset加载同步ab加载后导出全部asset并卸载ab /// 6、所有公共ab包被多个ab包依赖常驻内存非公共包加载asset以后立刻卸载被依赖的公共ab包会随着资源预加载自动加载并常驻内存 /// 7、随意卸载公共ab包可能导致内存资源重复最好在切换场景时再手动清理不需要的公共ab包 /// 8、常驻包公共ab包引用计数不为0时手动清理无效正在等待加载的所有ab包不能强行终止—一旦发起创建就一定要等操作结束异步过程进行中清理无效 /// 9、切换场景时最好预加载所有可能使用到的资源所有加载器用完以后记得Dispose回收清理GC时注意先释放所有Asset缓存 /// 10、逻辑层所有Asset路径带文件类型后缀且是AssetBundleConfig.ResourcesFolderName下的相对路径注意路径区分大小写 /// TODO /// 1、区分场景常驻包和全局公共包切换场景时自动卸载场景公共包 /// 使用说明 /// 1、由Asset路径获取AssetName、AssetBundleNameParseAssetPathToNames /// 2、设置常驻(公共)ab包SetAssetBundleResident(assebundleName, true)—公共ab包已经自动设置常驻 /// 2、(预)加载资源var loader LoadAssetBundleAsync(assetbundleName)协程等待加载完毕后Disposeloader.Dispose() /// 3、加载Asset资源var loader LoadAssetAsync(assetPath, TextAsset)协程等待加载完毕后Disposeloader.Dispose() /// 4、离开场景清理所有Asset缓存ClearAssetsCache()UnloadUnusedAssetBundles(), Resources.UnloadUnusedAssets() /// 5、离开场景清理必要的(公共)ab包TryUnloadAssetBundle()注意这里只是尝试卸载所有引用计数不为0的包还正在加载不会被清理 /// /summarynamespace AssetBundles {[Hotfix][LuaCallCSharp]public class AssetBundleManager : UnitySingletonAssetBundleManager{// 最大同时进行的ab创建数量const int MAX_ASSETBUNDLE_CREATE_NUM 5;// manifest提供依赖关系查找以及hash值比对Manifest manifest null;// 资源路径相关的映射表AssetsPathMapping assetsPathMapping null;// 常驻ab包需要手动添加公共ab包进来常驻包不会自动卸载即使引用计数为0引用计数为0时可以手动卸载HashSetstring assetbundleResident new HashSetstring();// ab缓存包所有目前已经加载的ab包包括临时ab包与公共ab包Dictionarystring, AssetBundle assetbundlesCaching new Dictionarystring, AssetBundle();// ab缓存包引用计数卸载ab包时只有引用计数为0时才会真正执行卸载Dictionarystring, int assetbundleRefCount new Dictionarystring, int(); // asset缓存给非公共ab包的asset提供逻辑层的复用Dictionarystring, UnityEngine.Object assetsCaching new Dictionarystring, UnityEngine.Object();// 加载数据请求正在prosessing或者等待prosessing的资源请求Dictionarystring, ResourceWebRequester webRequesting new Dictionarystring, ResourceWebRequester();// 等待处理的资源请求QueueResourceWebRequester webRequesterQueue new QueueResourceWebRequester();// 正在处理的资源请求ListResourceWebRequester prosessingWebRequester new ListResourceWebRequester();// 逻辑层正在等待的ab加载异步句柄ListAssetBundleAsyncLoader prosessingAssetBundleAsyncLoader new ListAssetBundleAsyncLoader();// 逻辑层正在等待的asset加载异步句柄ListAssetAsyncLoader prosessingAssetAsyncLoader new ListAssetAsyncLoader();public static string ManifestBundleName{get;set;}#if UNITY_EDITOR || CLIENT_DEBUG #if !CLIENT_DEBUG[BlackList] #endif// Hotfix测试—用于侧测试资源模块的热修复public void TestHotfix(){Debug.Log(********** AssetBundleManager : Call TestHotfix in cs…);} #endifpublic IEnumerator Initialize(){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){yield break;} #endifmanifest new Manifest();assetsPathMapping new AssetsPathMapping();// 说明同时请求资源可以提高加载速度var manifestRequest RequestAssetBundleAsync(manifest.AssetbundleName);var pathMapRequest RequestAssetBundleAsync(assetsPathMapping.AssetbundleName);yield return manifestRequest;var assetbundle manifestRequest.assetbundle;manifest.LoadFromAssetbundle(assetbundle);assetbundle.Unload(false);manifestRequest.Dispose();yield return pathMapRequest;assetbundle pathMapRequest.assetbundle;var mapContent assetbundle.LoadAssetTextAsset(assetsPathMapping.AssetName);if (mapContent ! null){assetsPathMapping.Initialize(mapContent.text);}assetbundle.Unload(true);pathMapRequest.Dispose();// 设置所有公共包为常驻包var start DateTime.Now;var allAssetbundleNames manifest.GetAllAssetBundleNames();foreach (var curAssetbundleName in allAssetbundleNames){if (string.IsNullOrEmpty(curAssetbundleName)){continue;}int count 0;foreach (var checkAssetbundle in allAssetbundleNames){if (checkAssetbundle curAssetbundleName || string.IsNullOrEmpty(checkAssetbundle)){continue;}var allDependencies manifest.GetAllDependencies(checkAssetbundle);if (Array.IndexOf(allDependencies, curAssetbundleName) 0){count;if (count 2){break;}}}if (count 2){SetAssetBundleResident(curAssetbundleName, true);}}Debug.Log(string.Format(AssetBundleResident Initialize use {0}ms, (DateTime.Now - start).Milliseconds));yield break;}public IEnumerator Cleanup(){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){yield break;} #endif// 等待所有请求完成// 要是不等待Unity很多版本都有各种Bugyield return new WaitUntil(() {return prosessingWebRequester.Count 0;});yield return new WaitUntil(() {return prosessingAssetBundleAsyncLoader.Count 0;});yield return new WaitUntil(() {return prosessingAssetAsyncLoader.Count 0;});ClearAssetsCache();foreach (var assetbunle in assetbundlesCaching.Values){if (assetbunle ! null){assetbunle.Unload(false);}}assetbundlesCaching.Clear();assetbundleRefCount.Clear();assetbundleResident.Clear();yield break;}public Manifest curManifest{get{return manifest;}}public string DownloadUrl{get{// return Setting.SERVER_RESOURCE_ADDR;return null;}}public void SetAssetBundleResident(string assetbundleName, bool resident){Debug.Log(SetAssetBundleResident : assetbundleName , resident.ToString());bool exist assetbundleResident.Contains(assetbundleName);if (resident !exist){assetbundleResident.Add(assetbundleName);}else if(!resident exist){assetbundleResident.Remove(assetbundleName);}}public bool IsAssetBundleResident(string assebundleName){return assetbundleResident.Contains(assebundleName);}public bool IsAssetBundleLoaded(string assetbundleName){return assetbundlesCaching.ContainsKey(assetbundleName);}public AssetBundle GetAssetBundleCache(string assetbundleName){AssetBundle target null;assetbundlesCaching.TryGetValue(assetbundleName, out target);return target;}protected void RemoveAssetBundleCache(string assetbundleName){assetbundlesCaching.Remove(assetbundleName);}protected void AddAssetBundleCache(string assetbundleName, AssetBundle assetbundle){assetbundlesCaching[assetbundleName] assetbundle;}public bool IsAssetLoaded(string assetName){return assetsCaching.ContainsKey(assetName);}public UnityEngine.Object GetAssetCache(string assetName){UnityEngine.Object target null;assetsCaching.TryGetValue(assetName, out target);return target;}public void AddAssetCache(string assetName, UnityEngine.Object asset){assetsCaching[assetName] asset;}public void AddAssetbundleAssetsCache(string assetbundleName){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){return;} #endifif (!IsAssetBundleLoaded(assetbundleName)){Debug.LogError(Try to add assets cache from unloaded assetbundle : assetbundleName);return;}var curAssetbundle GetAssetBundleCache(assetbundleName);var allAssetNames assetsPathMapping.GetAllAssetNames(assetbundleName);for (int i 0; i allAssetNames.Count; i){var assetName allAssetNames[i];if (IsAssetLoaded(assetName)){continue;}var assetPath AssetBundleUtility.PackagePathToAssetsPath(assetName);var asset curAssetbundle null ? null : curAssetbundle.LoadAsset(assetPath);AddAssetCache(assetName, asset);#if UNITY_EDITOR// 说明在Editor模拟时Shader要重新指定var go asset as GameObject;if (go ! null){var renderers go.GetComponentsInChildrenRenderer();for (int j 0; j renderers.Length; j){var mat renderers[j].sharedMaterial;if (mat null){continue;}var shader mat.shader;if (shader ! null){var shaderName shader.name;mat.shader Shader.Find(shaderName);}}} #endif}}public void ClearAssetsCache(){assetsCaching.Clear();}public ResourceWebRequester GetAssetBundleAsyncCreater(string assetbundleName){ResourceWebRequester creater null;webRequesting.TryGetValue(assetbundleName, out creater);return creater;}protected int GetReferenceCount(string assetbundleName){int count 0;assetbundleRefCount.TryGetValue(assetbundleName, out count);return count;}protected int IncreaseReferenceCount(string assetbundleName){int count 0;assetbundleRefCount.TryGetValue(assetbundleName, out count);count;assetbundleRefCount[assetbundleName] count;return count;}protected int DecreaseReferenceCount(string assetbundleName){int count 0;assetbundleRefCount.TryGetValue(assetbundleName, out count);count–;assetbundleRefCount[assetbundleName] count;return count;}protected bool CreateAssetBundleAsync(string assetbundleName){if (IsAssetBundleLoaded(assetbundleName) || webRequesting.ContainsKey(assetbundleName)){return false;}var creater ResourceWebRequester.Get();var url AssetBundleUtility.GetAssetBundleFileUrl(assetbundleName);creater.Init(assetbundleName, url);webRequesting.Add(assetbundleName, creater);webRequesterQueue.Enqueue(creater);// 创建器持有的引用创建器对每个ab来说是全局唯一的IncreaseReferenceCount(assetbundleName);return true;}// 异步请求Assetbundle资源AB是否缓存取决于是否设置为常驻包Assets一律缓存处理依赖public BaseAssetBundleAsyncLoader LoadAssetBundleAsync(string assetbundleName){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){return new EditorAssetBundleAsyncLoader(assetbundleName);} #endifvar loader AssetBundleAsyncLoader.Get();prosessingAssetBundleAsyncLoader.Add(loader);if (manifest ! null){string[] dependancies manifest.GetAllDependencies(assetbundleName);for (int i 0; i dependancies.Length; i){var dependance dependancies[i];if (!string.IsNullOrEmpty(dependance) dependance ! assetbundleName){CreateAssetBundleAsync(dependance);// ab缓存对依赖持有的引用IncreaseReferenceCount(dependance);}}loader.Init(assetbundleName, dependancies);}else{loader.Init(assetbundleName, null);}CreateAssetBundleAsync(assetbundleName);// 加载器持有的引用同一个ab能同时存在多个加载器等待ab创建器完成IncreaseReferenceCount(assetbundleName);return loader;}// 从服务器下载网页内容需提供完整urlpublic ResourceWebRequester DownloadWebResourceAsync(string url){var creater ResourceWebRequester.Get();creater.Init(url, url, true);webRequesting.Add(url, creater);webRequesterQueue.Enqueue(creater);return creater;}// 从资源服务器下载非Assetbundle资源public ResourceWebRequester DownloadAssetFileAsync(string filePath){if (string.IsNullOrEmpty(DownloadUrl)){Debug.LogError(You should set download url first!!!);return null;}var creater ResourceWebRequester.Get();var url DownloadUrl filePath;creater.Init(filePath, url, true);webRequesting.Add(filePath, creater);webRequesterQueue.Enqueue(creater);return creater;}// 从资源服务器下载Assetbundle资源不缓存无依赖public ResourceWebRequester DownloadAssetBundleAsync(string filePath){// 如果ResourceWebRequester升级到使用UnityWebRequester那么下载AB和下载普通资源需要两个不同的DownLoadHandler// 兼容升级的可能性这里也做一下区分return DownloadAssetFileAsync(filePath);}// 本地异步请求非Assetbundle资源public ResourceWebRequester RequestAssetFileAsync(string filePath, bool streamingAssetsOnly true){var creater ResourceWebRequester.Get();string url null;if (streamingAssetsOnly){url AssetBundleUtility.GetStreamingAssetsFilePath(filePath);}else{url AssetBundleUtility.GetAssetBundleFileUrl(filePath);}creater.Init(filePath, url, true);webRequesting.Add(filePath, creater);webRequesterQueue.Enqueue(creater);return creater;}// 本地异步请求Assetbundle资源不缓存无依赖public ResourceWebRequester RequestAssetBundleAsync(string assetbundleName){var creater ResourceWebRequester.Get();var url AssetBundleUtility.GetAssetBundleFileUrl(assetbundleName);creater.Init(assetbundleName, url, true);webRequesting.Add(assetbundleName, creater);webRequesterQueue.Enqueue(creater);return creater;}public void UnloadAssetBundleDependencies(string assetbundleName){if (manifest ! null){string[] dependancies manifest.GetAllDependencies(assetbundleName);for (int i 0; i dependancies.Length; i){var dependance dependancies[i];if (!string.IsNullOrEmpty(dependance) dependance ! assetbundleName){UnloadAssetBundle(dependance);}}}}protected bool UnloadAssetBundle(string assetbundleName, bool unloadResident false, bool unloadAllLoadedObjects false){int count GetReferenceCount(assetbundleName);if (count 0){return false;}count DecreaseReferenceCount(assetbundleName);if (count 0){return false;}var assetbundle GetAssetBundleCache(assetbundleName);var isResident IsAssetBundleResident(assetbundleName);if (assetbundle ! null){if (!isResident || isResident unloadResident){assetbundle.Unload(unloadAllLoadedObjects);RemoveAssetBundleCache(assetbundleName);UnloadAssetBundleDependencies(assetbundleName);return true;}}return false;}public bool TryUnloadAssetBundle(string assetbundleName, bool unloadAllLoadedObjects false){int count GetReferenceCount(assetbundleName);if (count 0){return false;}return UnloadAssetBundle(assetbundleName, true, unloadAllLoadedObjects);}public void UnloadUnusedAssetBundles(bool unloadResident false, bool unloadAllLoadedObjects false){int unloadCount 0;bool hasDoUnload false;do{hasDoUnload false;var iter assetbundleRefCount.GetEnumerator();while (iter.MoveNext()){var assetbundleName iter.Current.Key;var referenceCount iter.Current.Value;if (referenceCount 0){var result UnloadAssetBundle(assetbundleName, unloadResident, unloadAllLoadedObjects);if (result){unloadCount;hasDoUnload true;}}}} while (hasDoUnload);}public bool MapAssetPath(string assetPath, out string assetbundleName, out string assetName){return assetsPathMapping.MapAssetPath(assetPath, out assetbundleName, out assetName);}public BaseAssetAsyncLoader LoadAssetAsync(string assetPath, System.Type assetType){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){string path AssetBundleUtility.PackagePathToAssetsPath(assetPath); UnityEngine.Object target AssetDatabase.LoadAssetAtPath(path, assetType);return new EditorAssetAsyncLoader(target);} #endifstring assetbundleName null;string assetName null;bool status MapAssetPath(assetPath, out assetbundleName, out assetName);if (!status){Debug.LogError(No assetbundle at asset path : assetPath);return null;}var loader AssetAsyncLoader.Get();prosessingAssetAsyncLoader.Add(loader);if (IsAssetLoaded(assetName)){loader.Init(assetName, GetAssetCache(assetName));return loader;}else{var assetbundleLoader LoadAssetBundleAsync(assetbundleName);loader.Init(assetName, assetbundleLoader);return loader;}}void Update(){OnProsessingWebRequester();OnProsessingAssetBundleAsyncLoader();OnProsessingAssetAsyncLoader();}void OnProsessingWebRequester(){for (int i prosessingWebRequester.Count - 1; i 0; i–){var creater prosessingWebRequester[i];creater.Update();if (creater.IsDone()){prosessingWebRequester.RemoveAt(i);webRequesting.Remove(creater.assetbundleName);UnloadAssetBundle(creater.assetbundleName);if (creater.noCache){return;}// 说明有错误也缓存下来只不过资源为空// 1、避免再次错误加载// 2、如果不存下来加载器将无法判断什么时候结束AddAssetBundleCache(creater.assetbundleName, creater.assetbundle);creater.Dispose();}}int slotCount prosessingWebRequester.Count;while (slotCount MAX_ASSETBUNDLE_CREATE_NUM webRequesterQueue.Count 0){var creater webRequesterQueue.Dequeue();creater.Start();prosessingWebRequester.Add(creater);slotCount;}}void OnProsessingAssetBundleAsyncLoader(){for (int i prosessingAssetBundleAsyncLoader.Count - 1; i 0; i–){var loader prosessingAssetBundleAsyncLoader[i];loader.Update();if (loader.IsDone()){UnloadAssetBundle(loader.assetbundleName);prosessingAssetBundleAsyncLoader.RemoveAt(i);}}}void OnProsessingAssetAsyncLoader(){for (int i prosessingAssetAsyncLoader.Count - 1; i 0; i–){var loader prosessingAssetAsyncLoader[i];loader.Update();if (loader.IsDone()){prosessingAssetAsyncLoader.RemoveAt(i);}}}#if UNITY_EDITOR[BlackList]public HashSetstring GetAssetbundleResident(){return assetbundleResident;}[BlackList]public ICollectionstring GetAssetbundleCaching(){return assetbundlesCaching.Keys;}[BlackList]public Dictionarystring, ResourceWebRequester GetWebRequesting(){return webRequesting;}[BlackList]public QueueResourceWebRequester GetWebRequestQueue(){return webRequesterQueue;}[BlackList]public ListResourceWebRequester GetProsessingWebRequester(){return prosessingWebRequester;}[BlackList]public ListAssetBundleAsyncLoader GetProsessingAssetBundleAsyncLoader(){return prosessingAssetBundleAsyncLoader;}[BlackList]public ListAssetAsyncLoader GetProsessingAssetAsyncLoader(){return prosessingAssetAsyncLoader;}[BlackList]public string GetAssetBundleName(string assetName){return assetsPathMapping.GetAssetBundleName(assetName);}[BlackList]public int GetAssetCachingCount(){return assetsCaching.Count;}[BlackList]public Dictionarystring, Liststring GetAssetCaching(){var assetbundleDic new Dictionarystring, Liststring();Liststring assetNameList null;var iter assetsCaching.GetEnumerator();while (iter.MoveNext()){var assetName iter.Current.Key;var assetbundleName assetsPathMapping.GetAssetBundleName(assetName);assetbundleDic.TryGetValue(assetbundleName, out assetNameList);if (assetNameList null){assetNameList new Liststring();}assetNameList.Add(assetName);assetbundleDic[assetbundleName] assetNameList;}return assetbundleDic;}[BlackList]public int GetAssetbundleRefrenceCount(string assetbundleName){return GetReferenceCount(assetbundleName);}[BlackList]public int GetAssetbundleDependenciesCount(string assetbundleName){string[] dependancies manifest.GetAllDependencies(assetbundleName);int count 0;for (int i 0; i dependancies.Length; i){var cur dependancies[i];if (!string.IsNullOrEmpty(cur) cur ! assetbundleName){count;}}return count;}[BlackList]public Liststring GetAssetBundleRefrences(string assetbundleName){Liststring refrences new Liststring();var cachingIter assetbundlesCaching.GetEnumerator();while (cachingIter.MoveNext()){var curAssetbundleName cachingIter.Current.Key;if (curAssetbundleName assetbundleName){continue;}string[] dependancies manifest.GetAllDependencies(curAssetbundleName);for (int i 0; i dependancies.Length; i){var dependance dependancies[i];if (dependance assetbundleName){refrences.Add(curAssetbundleName);}}}var requestingIter webRequesting.GetEnumerator();while (requestingIter.MoveNext()){var curAssetbundleName requestingIter.Current.Key;if (curAssetbundleName assetbundleName){continue;}string[] dependancies manifest.GetAllDependencies(curAssetbundleName);for (int i 0; i dependancies.Length; i){var dependance dependancies[i];if (dependance assetbundleName){refrences.Add(curAssetbundleName);}}}return refrences;}[BlackList]public Liststring GetWebRequesterRefrences(string assetbundleName){Liststring refrences new Liststring();var iter webRequesting.GetEnumerator();while (iter.MoveNext()){var curAssetbundleName iter.Current.Key;var webRequster iter.Current.Value;if (curAssetbundleName assetbundleName){refrences.Add(webRequster.Sequence.ToString());continue;}}return refrences;}[BlackList]public Liststring GetAssetBundleLoaderRefrences(string assetbundleName){Liststring refrences new Liststring();var iter prosessingAssetBundleAsyncLoader.GetEnumerator();while (iter.MoveNext()){var curAssetbundleName iter.Current.assetbundleName;var curLoader iter.Current;if (curAssetbundleName assetbundleName){refrences.Add(curLoader.Sequence.ToString());}}return refrences;} #endif} }Lua GC Lua有自己的垃圾回收系统简称GC。我们不需要自己编写GC但是要设定好什么条件下启动GC一般来说可以隔100帧启动一次。 代码 if(Time.frameCount % 100 0){this.luaEnv.Tick(); }AssetBundle菜单工具 定义菜单宏 const string kSimulateMode AssetBundles/Switch Model/Simulate Mode; const string kEditorMode AssetBundles/Switch Model/Editor Mode; const string kToolRunAllCheckers AssetBundles/Run All Checkers; const string kToolBuildForCurrentSetting AssetBundles/Build For Current Setting; const string kToolsCopyAssetbundles AssetBundles/Copy To StreamingAssets; const string kToolsOpenOutput AssetBundles/Open Current Output; const string kToolsOpenPerisitentData AssetBundles/Open PersistentData; const string kToolsClearOutput AssetBundles/Clear Current Output; const string kToolsClearStreamingAssets AssetBundles/Clear StreamingAssets; const string kToolsClearPersistentAssets AssetBundles/Clear PersistentData;const string kCreateAssetbundleForCurrent Assets/AssetBundles/Create Assetbundle For Current #z; const string kCreateAssetbundleForChildren Assets/AssetBundles/Create Assetbundle For Children #x; const string kAssetDependencis Assets/AssetBundles/Asset Dependencis #h; const string kAssetbundleAllDependencis Assets/AssetBundles/Assetbundle All Dependencis #j; const string kAssetbundleDirectDependencis Assets/AssetBundles/Assetbundle Direct Dependencis #k; 复制lua文件 .lua文件没有办法被Xlua打成ab包因此我们要把这些文件加上.bytes结尾这样能打成二进制包并且复制到对应的路径下 using UnityEngine; using UnityEditor; using System.IO; using Debug UnityEngine.Debug; using AssetBundles;[InitializeOnLoad] public static class XLuaMenu {[MenuItem(AssetBundles/Copy Lua Files To AssetsPackage, false, 51)]public static void CopyLuaFilesToAssetsPackage(){// Application.dataPath — Assets所在目录 AssetsPackagestring destination Path.Combine(Application.dataPath, AssetBundleConfig.AssetsFolderName);// string destination Path.Combine(Application.dataPath, AssetsPackage);// Assets/AssetsPackage/Luadestination Path.Combine(destination, xLuaMgr.luaAssetbundleAssetName);Debug.Log(destination);// Assets/LuaScripts/string source Path.Combine(Application.dataPath, xLuaMgr.luaScriptsFolder);GameUtility.SafeDeleteDir(destination); // 删除目标路径下所有得文件FileUtil.CopyFileOrDirectoryFollowSymlinks(source, destination); // // 将不是.lua 文件名字的文件全部都获取出来;var notLuaFiles GameUtility.GetSpecifyFilesInFolder(destination, new string[] { .lua }, true);if (notLuaFiles ! null notLuaFiles.Length 0){for (int i 0; i notLuaFiles.Length; i){GameUtility.SafeDeleteFile(notLuaFiles[i]); // .meta}}// 找出所有的.lua文件;var luaFiles GameUtility.GetSpecifyFilesInFolder(destination, new string[] { .lua }, false);if (luaFiles ! null luaFiles.Length 0){// 重新命名文件加上一个.bytes的后缀; .lua.bytesfor (int i 0; i luaFiles.Length; i){GameUtility.SafeRenameFile(luaFiles[i], luaFiles[i] .bytes);}}AssetDatabase.Refresh();Debug.Log(Copy lua files over);} } 切换代码启动模式 分为两种模式 编辑器模式直接从代码lua脚本读取模拟模式从打包的AssetBundle读取资源发布模式从打包的AssetBundle读取资源 // 点击编辑器模式按钮[MenuItem(kEditorMode, false)] public static void ToggleEditorMode() {if (AssetBundleConfig.IsSimulateMode){AssetBundleConfig.IsEditorMode true; // set里面保存到EditorPrefs里面;LaunchAssetBundleServer.CheckAndDoRunning();} }// 如果是true, 就会打一个勾; [MenuItem(kEditorMode, true)] public static bool ToggleEditorModeValidate() {Menu.SetChecked(kEditorMode, AssetBundleConfig.IsEditorMode);return true; } // 点击模拟模式按钮 [MenuItem(kSimulateMode)] public static void ToggleSimulateMode() {if (AssetBundleConfig.IsEditorMode){AssetBundleConfig.IsSimulateMode true;CheckSimulateModelEnv();LaunchAssetBundleServer.CheckAndDoRunning();}}[MenuItem(kSimulateMode, true)] public static bool ToggleSimulateModeValidate() {Menu.SetChecked(kSimulateMode, AssetBundleConfig.IsSimulateMode);return true; }在配置文件AssetBundleConfig中 using UnityEngine; #if UNITY_EDITOR using UnityEditor; using System.IO; #endif/// summary /// added by wsh 2017.12.25 /// 注意 /// 1、所有ab路径中目录、文件名不能以下划线打头否则出包时StreamingAssets中的资源不能打到真机上很坑爹 /// /summarynamespace AssetBundles {public class AssetBundleConfig{public const string localSvrAppPath Editor/AssetBundle/LocalServer/AssetBundleServer.exe;public const string AssetBundlesFolderName AssetBundles;public const string AssetBundleSuffix .assetbundle;public const string AssetsFolderName AssetsPackage;public const string ChannelFolderName Channel;public const string AssetsPathMapFileName AssetsMap.bytes;public const string VariantsMapFileName VariantsMap.bytes;public const string AssetBundleServerUrlFileName AssetBundleServerUrl.txt;public const string VariantMapParttren Variant;public const string CommonMapPattren ,;#if UNITY_EDITORpublic static string AssetBundlesBuildOutputPath{get{string outputPath Path.Combine(System.Environment.CurrentDirectory, AssetBundlesFolderName);GameUtility.CheckDirAndCreateWhenNeeded(outputPath);return outputPath;}}public static string LocalSvrAppPath{get{return Path.Combine(Application.dataPath, localSvrAppPath);}}public static string LocalSvrAppWorkPath{get{return AssetBundlesBuildOutputPath;}}private static int mIsEditorMode -1;private const string kIsEditorMode IsEditorMode;private static int mIsSimulateMode -1;private const string kIsSimulateMode IsSimulateMode;public static bool IsEditorMode{get{if (mIsEditorMode -1){if (!EditorPrefs.HasKey(kIsEditorMode)){EditorPrefs.SetBool(kIsEditorMode, false);}mIsEditorMode EditorPrefs.GetBool(kIsEditorMode, true) ? 1 : 0;}return mIsEditorMode ! 0;}set{int newValue value ? 1 : 0;if (newValue ! mIsEditorMode){mIsEditorMode newValue;EditorPrefs.SetBool(kIsEditorMode, value);if (value){IsSimulateMode false;}}}}public static bool IsSimulateMode{get{if (mIsSimulateMode -1){if (!EditorPrefs.HasKey(kIsSimulateMode)){EditorPrefs.SetBool(kIsSimulateMode, true);}mIsSimulateMode EditorPrefs.GetBool(kIsSimulateMode, true) ? 1 : 0;}return mIsSimulateMode ! 0;}set{int newValue value ? 1 : 0;if (newValue ! mIsSimulateMode){mIsSimulateMode newValue;EditorPrefs.SetBool(kIsSimulateMode, value);if (value){IsEditorMode false;}}}} #endif} }渠道和版本管理、打包工具 渠道配置文件 // 目前就设置了Test渠道 namespace GameChannel {public enum ChannelType{Test,} } 打包设置 public class PackageTool : EditorWindow {static private BuildTarget buildTarget EditorUserBuildSettings.activeBuildTarget;static private ChannelType channelType ChannelType.Test;static private string resVersion 1.0.0;static PackageTool(){// EditorPrefs—ChannelName—字符串—解析出来时哪种渠道枚举如果没有就用Test;channelType PackageUtils.GetCurSelectedChannel();}//打包工具显示OnGUI中的界面// Tools/Package;[MenuItem(Tools/Package, false, 0)]static void Init() {EditorWindow.GetWindow(typeof(PackageTool));}void OnGUI(){GUILayout.BeginVertical();GUILayout.Space(10);// 目标平台;buildTarget (BuildTarget)EditorGUILayout.EnumPopup(Build Target : , buildTarget);GUILayout.Space(5);// 渠道;channelType (ChannelType)EditorGUILayout.EnumPopup(Build Channel : , channelType);GUILayout.EndVertical();if (GUI.changed){// 如果渠道修改了我就保存这个渠道;PackageUtils.SaveCurSelectedChannel(channelType);}DrawConfigGUI();DrawAssetBundlesGUI();DrawXLuaGUI();DrawBuildPlayerGUI();} }PackageUtils.GetCurSelectedChannel: public static ChannelType GetCurSelectedChannel(){ChannelType channelType ChannelType.Test;string channelName EditorPrefs.GetString(ChannelName);if (Enum.IsDefined(typeof(ChannelType), channelName)){channelType (ChannelType)Enum.Parse(typeof(ChannelType), channelName);}else{EditorPrefs.SetString(ChannelName, ChannelType.Test.ToString());}return channelType;}打包的时候拥有app版本和资源版本两者可能不同。保存为app_version.bytesres_version.bytes文件 AssetBundle打包操作 流程 打包预备所有生成出来的lua脚本都以.lua.bytes结尾的文件形式存放在AssetsPackage/lua中其他资源也存放在AssetsPackage的子文件夹中例如AssetsPackage/Sound。AssetsPackage的子文件夹在未选择打包的时候其Inspector中会存在Create AssetBundle Dispatcher按钮由代码决定点击之后可以选择四种打包模式。选择的打包模式绘制在Editor/Database/AssetsPackage中生成对应的.asset配置文件用于打包点击自定义菜单AssetBundles/Build For Current Settings进行打包这时候在AssetsPackage目录下将生成AssetsMap和VariantMap文件。 Unity ScriptableObject 一个C#对象继承自ScriptableObject则会将对应的数据写入.asset文件中 这个脚本定义了基本的数据结构 using UnityEngine; using System.Collections.Generic; namespace AssetBundles {public class AssetBundleDispatcherConfig : ScriptableObject{public string PackagePath string.Empty;public AssetBundleDispatcherFilterType Type AssetBundleDispatcherFilterType.Root;public ListAssetBundleCheckerFilter CheckerFilters new ListAssetBundleCheckerFilter();// 序列化用的AssetBundleCheckerFilter的字段拆成两个数组[SerializeField]string[] RelativePaths null;[SerializeField]string[] ObjectFilters null;public AssetBundleDispatcherConfig(){Load();}public void Load(){CheckerFilters.Clear();if (RelativePaths ! null RelativePaths.Length 0){for (int i 0; i RelativePaths.Length; i){CheckerFilters.Add(new AssetBundleCheckerFilter(RelativePaths[i], ObjectFilters[i]));}}}public void Apply(){if (CheckerFilters.Count 0){RelativePaths null;ObjectFilters null;return;}RelativePaths new string[CheckerFilters.Count];ObjectFilters new string[CheckerFilters.Count];for (int i 0; i CheckerFilters.Count; i){RelativePaths[i] CheckerFilters[i].RelativePath;ObjectFilters[i] CheckerFilters[i].ObjectFilter;}}} } 这个脚本负责对应的点击GUI的操作 注意这里应用了编辑器扩展给特定文件夹的Inspector下添加了按钮 每次属性检查器刷新的时候会调用OnInspectorGUI 每次点击文件夹的时候这个脚本会调用OnEnable using UnityEngine; using UnityEditor; using System.IO; using System.Collections.Generic;/// summary /// added by wsh 2018.01.06 /// 说明Assetbundle分发器Inspector为其提供可视化的编辑界面 /// TODO /// 1、还未完成目前只是做了基本的配置功能 /// /summarynamespace AssetBundles {[CustomEditor(typeof(DefaultAsset), true)]public class AssetBundleDispatcherInspector : Editor{AssetBundleDispatcherConfig dispatcherConfig null;string packagePath null;string targetAssetPath null;string databaseAssetPath null;static Dictionarystring, bool inspectorSate new Dictionarystring, bool();AssetBundleDispatcherFilterType filterType AssetBundleDispatcherFilterType.Root;bool configChanged false;void OnEnable(){Initialize();}// 每次选中这个文件夹的时候我们会调用Initialize;void Initialize(){configChanged false;filterType AssetBundleDispatcherFilterType.Root; // 默认的打包方式;targetAssetPath AssetDatabase.GetAssetPath(target); // 获取我们选的当前的路径;if (!AssetBundleUtility.IsPackagePath(targetAssetPath)) // 这个路径是否在AssetsPackages路径下;{return;}// Assets/AssetsPackage/Lua — pakcage path LuapackagePath AssetBundleUtility.AssetsPathToPackagePath(targetAssetPath); // packagePath// 文件对应的数据库目录下 xxx.asset文件;databaseAssetPath AssetBundleInspectorUtils.AssetPathToDatabasePath(targetAssetPath);// 加载数据库文件 例如: Lua.assetdispatcherConfig AssetDatabase.LoadAssetAtPathAssetBundleDispatcherConfig(databaseAssetPath);if (dispatcherConfig ! null) // 如果有就不为null, 之前已经创建吧数据加载进来;{dispatcherConfig.Load();filterType dispatcherConfig.Type;}}// 如果读不到数据库文件配置那么这个时候走这里绘制一个创建按钮;void DrawCreateAssetBundleDispatcher(){if (GUILayout.Button(Create AssetBundle Dispatcher)){// 创建数据库文件路径var dir Path.GetDirectoryName(databaseAssetPath);GameUtility.CheckDirAndCreateWhenNeeded(dir); // 是否存在如果不存在就创建一个;// 创建一个ScriptableObject 对象; —构造函数;, 初始化数据;// 所以你创建完以后默认的初始值, 对象里面初始值决定的;var instance CreateInstanceAssetBundleDispatcherConfig();AssetDatabase.CreateAsset(instance, databaseAssetPath); // 将这个对象实例—创建到.asset文件里面AssetDatabase.Refresh();// 重新同步一下到当前的对象里面;Initialize();// 调用Repaint时候—-》 引发 OnInspectorGUIRepaint();}}void DrawFilterItem(AssetBundleCheckerFilter checkerFilter){GUILayout.BeginVertical(); var relativePath GUILayoutUtils.DrawInputField(RelativePath:, checkerFilter.RelativePath, 300f, 80f);var objectFilter GUILayoutUtils.DrawInputField(ObjectFilter:, checkerFilter.ObjectFilter, 300f, 80f);if (relativePath ! checkerFilter.RelativePath){configChanged true;checkerFilter.RelativePath relativePath;}if (objectFilter ! checkerFilter.ObjectFilter){configChanged true;checkerFilter.ObjectFilter objectFilter;}GUILayout.EndVertical();}void DrawFilterTypesList(ListAssetBundleCheckerFilter checkerFilters){GUILayout.BeginVertical(EditorStyles.textField);GUILayout.Space(3);EditorGUILayout.Separator();for (int i 0; i checkerFilters.Count; i){var curFilter checkerFilters[i];var relativePath string.IsNullOrEmpty(curFilter.RelativePath) ? root : curFilter.RelativePath;var objectFilter string.IsNullOrEmpty(curFilter.ObjectFilter) ? all : curFilter.ObjectFilter;var filterType relativePath : objectFilter ;var stateKey CheckerFilters i.ToString();if (GUILayoutUtils.DrawRemovableSubHeader(1, filterType, inspectorSate, stateKey, () {configChanged true;checkerFilters.RemoveAt(i);i–;})){DrawFilterItem(curFilter);}EditorGUILayout.Separator();}if (GUILayout.Button(Add)){configChanged true;checkerFilters.Add(new AssetBundleCheckerFilter(, t:prefab));}EditorGUILayout.Separator();GUILayout.Space(3);GUILayout.EndVertical();}void DrawAssetDispatcherConfig(){GUILayoutUtils.BeginContents(false);GUILayoutUtils.DrawProperty(Path:, AssetBundleUtility.AssetsPathToPackagePath(targetAssetPath), 300f, 80f);EditorGUILayout.BeginHorizontal();EditorGUILayout.LabelField(FilterType:, GUILayout.MaxWidth(80f));// 打包的模式var selectType (AssetBundleDispatcherFilterType)EditorGUILayout.EnumPopup(filterType);if (selectType ! filterType){filterType selectType;configChanged true;}EditorGUILayout.EndHorizontal();EditorGUILayout.Separator();var filtersCount dispatcherConfig.CheckerFilters.Count;if (GUILayoutUtils.DrawSubHeader(0, CheckerFilters:, inspectorSate, CheckerFilters, filtersCount.ToString())){DrawFilterTypesList(dispatcherConfig.CheckerFilters);}Color color GUI.color;if (configChanged){GUI.color color * new Color(1, 1, 0.5f);}EditorGUILayout.Separator();GUILayout.BeginHorizontal();if (GUILayout.Button(Apply)) // 同步到数据库;{Apply();}GUI.color new Color(1, 0.5f, 0.5f);if (GUILayout.Button(Remove)) // 删除数据库文件;{Remove();}GUI.color color;GUILayout.EndHorizontal();EditorGUILayout.Separator();GUILayoutUtils.EndContents(false);}void Apply(){dispatcherConfig.PackagePath packagePath;dispatcherConfig.Type filterType;dispatcherConfig.Apply();EditorUtility.SetDirty(dispatcherConfig);AssetDatabase.SaveAssets();// 刷新编辑器;Initialize();Repaint();configChanged false;}void Remove(){bool checkRemove EditorUtility.DisplayDialog(Remove Warning,Sure to remove the AssetBundle dispatcher ?,Confirm, Cancel);if (!checkRemove){return;}// 删除数据库文件;databaseAssetPathGameUtility.SafeDeleteFile(databaseAssetPath);AssetDatabase.Refresh();// Initialize(); Repaint(); // 调用一下OnIntxxGUI() — create 按钮configChanged false;}void DrawAssetBundleDispatcherInspector(){// 创建一个Layout面板出来;if (GUILayoutUtils.DrawHeader(AssetBundle Dispatcher : , inspectorSate, DispatcherConfig, true, false)){DrawAssetDispatcherConfig();}}public override void OnInspectorGUI(){base.OnInspectorGUI();// 检查一下这个路径是否为AssetsPackageif (!AssetBundleInspectorUtils.CheckMaybeAssetBundleAsset(targetAssetPath)) { // 其它文件夹下路径, return;return;}GUI.enabled true;if (dispatcherConfig null) // 数据库配置为null;{DrawCreateAssetBundleDispatcher(); // 创建按钮, 视图}else // 绘制编辑按钮;{DrawAssetBundleDispatcherInspector(); // 编辑配置模式的一个视图}}void OnDisable(){if (configChanged){bool checkApply EditorUtility.DisplayDialog(Modify Warning,You have modified the AssetBundle dispatcher setting, Apply it ?,Confirm, Cancel);if (checkApply){Apply();}}dispatcherConfig null;inspectorSate.Clear();}} } 对应的数据库创建完成后可以点击RunAllCheck来检查 正式打包 [MenuItem(kToolBuildForCurrentSetting, false, 1100)] static public void ToolBuildForCurrentSetting() {var buildTargetName PackageUtils.GetCurPlatformName();var channelName PackageUtils.GetCurSelectedChannel().ToString();bool checkCopy EditorUtility.DisplayDialog(Build AssetBundles Warning,string.Format(Build AssetBundles for : \n\nplatform : {0} \nchannel : {1} \n\nContinue ?, buildTargetName, channelName),Confirm, Cancel);if (!checkCopy){return;}PackageTool.BuildAssetBundlesForCurrentChannel(); }PackageTool public static void BuildAssetBundlesForCurrentChannel() {var start DateTime.Now;BuildPlayer.BuildAssetBundles(buildTarget, channelType.ToString());var buildTargetName PackageUtils.GetPlatformName(buildTarget);EditorUtility.DisplayDialog(Success, string.Format(Build AssetBundles for : \n\nplatform : {0} \nchannel : {1} \n\ndone! use {2}s, buildTargetName, channelType, (DateTime.Now - start).TotalSeconds), Confirm); }BuildPlayer public static void BuildAssetBundles(BuildTarget buildTarget, string channelName) {var start DateTime.Now;CheckAssetBundles.Run();Debug.Log(Finished CheckAssetBundles.Run! use (DateTime.Now - start).TotalSeconds s);start DateTime.Now;CheckAssetBundles.SwitchChannel(channelName.ToString());Debug.Log(Finished CheckAssetBundles.SwitchChannel! use (DateTime.Now - start).TotalSeconds s);start DateTime.Now;InnerBuildAssetBundles(buildTarget, channelName, true);Debug.Log(Finished InnerBuildAssetBundles! use (DateTime.Now - start).TotalSeconds s);var targetName PackageUtils.GetPlatformName(buildTarget);Debug.Log(string.Format(Build assetbundles for platform : {0} and channel : {1} done!, targetName, channelName)); }private static void InnerBuildAssetBundles(BuildTarget buildTarget, string channelName, bool writeConfig) {BuildAssetBundleOptions buildOption BuildAssetBundleOptions.IgnoreTypeTreeChanges | BuildAssetBundleOptions.DeterministicAssetBundle;var outputPath PackageUtils.GetBuildPlatformOutputPath(buildTarget, channelName);// 正式打包AssetBundleManifest manifest BuildPipeline.BuildAssetBundles(outputPath, buildOption, buildTarget);if (manifest ! null writeConfig){// 生成信息文件AssetsPathMappingEditor.BuildPathMapping(manifest);VariantMappingEditor.BuildVariantMapping(manifest);// 把这两个文件也打包进去BuildPipeline.BuildAssetBundles(outputPath, buildOption, buildTarget);}// 写包的名字和大小WritePackageNameFile(buildTarget, channelName);WriteAssetBundleSize(buildTarget, channelName);AssetDatabase.Refresh(); }资源加载 我们需要编写一个AssetBundleManager脚本作为单例在一开始就加载进来。 using UnityEngine; using System.Collections; using System.Collections.Generic; using XLua; using System; #if UNITY_EDITOR using UnityEditor; #endif/// summary /// added by wsh 2017-12-21 /// 功能assetbundle管理类为外部提供统一的资源加载界面、协调Assetbundle各个子系统的运行 /// 注意 /// 1、抛弃Resources目录的使用官方建议https://unity3d.com/cn/learn/tutorials/temas/best-practices/resources-folder?playlist30089 /// 2、提供Editor和Simulate模式前者不适用Assetbundle直接加载资源快速开发后者使用Assetbundle用本地服务器模拟资源更新 /// 3、场景不进行打包场景资源打包为预设 /// 4、只提供异步接口所有加载按异步进行 /// 5、采用LZMA压缩方式性能瓶颈在Assetbundle加载上ab加载异步asset加载同步ab加载后导出全部asset并卸载ab /// 6、所有公共ab包被多个ab包依赖常驻内存非公共包加载asset以后立刻卸载被依赖的公共ab包会随着资源预加载自动加载并常驻内存 /// 7、随意卸载公共ab包可能导致内存资源重复最好在切换场景时再手动清理不需要的公共ab包 /// 8、常驻包公共ab包引用计数不为0时手动清理无效正在等待加载的所有ab包不能强行终止—一旦发起创建就一定要等操作结束异步过程进行中清理无效 /// 9、切换场景时最好预加载所有可能使用到的资源所有加载器用完以后记得Dispose回收清理GC时注意先释放所有Asset缓存 /// 10、逻辑层所有Asset路径带文件类型后缀且是AssetBundleConfig.ResourcesFolderName下的相对路径注意路径区分大小写 /// TODO /// 1、区分场景常驻包和全局公共包切换场景时自动卸载场景公共包 /// 使用说明 /// 1、由Asset路径获取AssetName、AssetBundleNameParseAssetPathToNames /// 2、设置常驻(公共)ab包SetAssetBundleResident(assebundleName, true)—公共ab包已经自动设置常驻 /// 2、(预)加载资源var loader LoadAssetBundleAsync(assetbundleName)协程等待加载完毕后Disposeloader.Dispose() /// 3、加载Asset资源var loader LoadAssetAsync(assetPath, TextAsset)协程等待加载完毕后Disposeloader.Dispose() /// 4、离开场景清理所有Asset缓存ClearAssetsCache()UnloadUnusedAssetBundles(), Resources.UnloadUnusedAssets() /// 5、离开场景清理必要的(公共)ab包TryUnloadAssetBundle()注意这里只是尝试卸载所有引用计数不为0的包还正在加载不会被清理 /// /summarynamespace AssetBundles {[Hotfix][LuaCallCSharp]public class AssetBundleManager : UnitySingletonAssetBundleManager{// 最大同时进行的ab创建数量const int MAX_ASSETBUNDLE_CREATE_NUM 5;// manifest提供依赖关系查找以及hash值比对Manifest manifest null;// 资源路径相关的映射表AssetsPathMapping assetsPathMapping null;// 常驻ab包需要手动添加公共ab包进来常驻包不会自动卸载即使引用计数为0引用计数为0时可以手动卸载HashSetstring assetbundleResident new HashSetstring();// ab缓存包所有目前已经加载的ab包包括临时ab包与公共ab包Dictionarystring, AssetBundle assetbundlesCaching new Dictionarystring, AssetBundle();// ab缓存包引用计数卸载ab包时只有引用计数为0时才会真正执行卸载Dictionarystring, int assetbundleRefCount new Dictionarystring, int(); // asset缓存给非公共ab包的asset提供逻辑层的复用Dictionarystring, UnityEngine.Object assetsCaching new Dictionarystring, UnityEngine.Object();// 加载数据请求正在prosessing或者等待prosessing的资源请求Dictionarystring, ResourceWebRequester webRequesting new Dictionarystring, ResourceWebRequester();// 等待处理的资源请求QueueResourceWebRequester webRequesterQueue new QueueResourceWebRequester();// 正在处理的资源请求ListResourceWebRequester prosessingWebRequester new ListResourceWebRequester();// 逻辑层正在等待的ab加载异步句柄ListAssetBundleAsyncLoader prosessingAssetBundleAsyncLoader new ListAssetBundleAsyncLoader();// 逻辑层正在等待的asset加载异步句柄ListAssetAsyncLoader prosessingAssetAsyncLoader new ListAssetAsyncLoader();public static string ManifestBundleName{get;set;}#if UNITY_EDITOR || CLIENT_DEBUG #if !CLIENT_DEBUG[BlackList] #endif// Hotfix测试—用于侧测试资源模块的热修复public void TestHotfix(){Debug.Log(********** AssetBundleManager : Call TestHotfix in cs…);} #endifpublic IEnumerator Initialize(){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){yield break;} #endifmanifest new Manifest();assetsPathMapping new AssetsPathMapping();// 说明同时请求资源可以提高加载速度var manifestRequest RequestAssetBundleAsync(manifest.AssetbundleName);var pathMapRequest RequestAssetBundleAsync(assetsPathMapping.AssetbundleName);yield return manifestRequest;var assetbundle manifestRequest.assetbundle;manifest.LoadFromAssetbundle(assetbundle);assetbundle.Unload(false);manifestRequest.Dispose();yield return pathMapRequest;assetbundle pathMapRequest.assetbundle;var mapContent assetbundle.LoadAssetTextAsset(assetsPathMapping.AssetName);if (mapContent ! null){assetsPathMapping.Initialize(mapContent.text);}assetbundle.Unload(true);pathMapRequest.Dispose();// 设置所有公共包为常驻包var start DateTime.Now;var allAssetbundleNames manifest.GetAllAssetBundleNames();foreach (var curAssetbundleName in allAssetbundleNames){if (string.IsNullOrEmpty(curAssetbundleName)){continue;}int count 0;foreach (var checkAssetbundle in allAssetbundleNames){if (checkAssetbundle curAssetbundleName || string.IsNullOrEmpty(checkAssetbundle)){continue;}var allDependencies manifest.GetAllDependencies(checkAssetbundle);if (Array.IndexOf(allDependencies, curAssetbundleName) 0){count;if (count 2){break;}}}if (count 2){SetAssetBundleResident(curAssetbundleName, true);}}Debug.Log(string.Format(AssetBundleResident Initialize use {0}ms, (DateTime.Now - start).Milliseconds));yield break;}public IEnumerator Cleanup(){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){yield break;} #endif// 等待所有请求完成// 要是不等待Unity很多版本都有各种Bugyield return new WaitUntil(() {return prosessingWebRequester.Count 0;});yield return new WaitUntil(() {return prosessingAssetBundleAsyncLoader.Count 0;});yield return new WaitUntil(() {return prosessingAssetAsyncLoader.Count 0;});ClearAssetsCache();foreach (var assetbunle in assetbundlesCaching.Values){if (assetbunle ! null){assetbunle.Unload(false);}}assetbundlesCaching.Clear();assetbundleRefCount.Clear();assetbundleResident.Clear();yield break;}public Manifest curManifest{get{return manifest;}}public string DownloadUrl{get{// return Setting.SERVER_RESOURCE_ADDR;return null;}}public void SetAssetBundleResident(string assetbundleName, bool resident){Debug.Log(SetAssetBundleResident : assetbundleName , resident.ToString());bool exist assetbundleResident.Contains(assetbundleName);if (resident !exist){assetbundleResident.Add(assetbundleName);}else if(!resident exist){assetbundleResident.Remove(assetbundleName);}}public bool IsAssetBundleResident(string assebundleName){return assetbundleResident.Contains(assebundleName);}public bool IsAssetBundleLoaded(string assetbundleName){return assetbundlesCaching.ContainsKey(assetbundleName);}public AssetBundle GetAssetBundleCache(string assetbundleName){AssetBundle target null;assetbundlesCaching.TryGetValue(assetbundleName, out target);return target;}protected void RemoveAssetBundleCache(string assetbundleName){assetbundlesCaching.Remove(assetbundleName);}protected void AddAssetBundleCache(string assetbundleName, AssetBundle assetbundle){assetbundlesCaching[assetbundleName] assetbundle;}public bool IsAssetLoaded(string assetName){return assetsCaching.ContainsKey(assetName);}public UnityEngine.Object GetAssetCache(string assetName){UnityEngine.Object target null;assetsCaching.TryGetValue(assetName, out target);return target;}public void AddAssetCache(string assetName, UnityEngine.Object asset){assetsCaching[assetName] asset;}public void AddAssetbundleAssetsCache(string assetbundleName){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){return;} #endifif (!IsAssetBundleLoaded(assetbundleName)){Debug.LogError(Try to add assets cache from unloaded assetbundle : assetbundleName);return;}var curAssetbundle GetAssetBundleCache(assetbundleName);var allAssetNames assetsPathMapping.GetAllAssetNames(assetbundleName);for (int i 0; i allAssetNames.Count; i){var assetName allAssetNames[i];if (IsAssetLoaded(assetName)){continue;}var assetPath AssetBundleUtility.PackagePathToAssetsPath(assetName);var asset curAssetbundle null ? null : curAssetbundle.LoadAsset(assetPath);AddAssetCache(assetName, asset);#if UNITY_EDITOR// 说明在Editor模拟时Shader要重新指定var go asset as GameObject;if (go ! null){var renderers go.GetComponentsInChildrenRenderer();for (int j 0; j renderers.Length; j){var mat renderers[j].sharedMaterial;if (mat null){continue;}var shader mat.shader;if (shader ! null){var shaderName shader.name;mat.shader Shader.Find(shaderName);}}} #endif}}public void ClearAssetsCache(){assetsCaching.Clear();}public ResourceWebRequester GetAssetBundleAsyncCreater(string assetbundleName){ResourceWebRequester creater null;webRequesting.TryGetValue(assetbundleName, out creater);return creater;}protected int GetReferenceCount(string assetbundleName){int count 0;assetbundleRefCount.TryGetValue(assetbundleName, out count);return count;}protected int IncreaseReferenceCount(string assetbundleName){int count 0;assetbundleRefCount.TryGetValue(assetbundleName, out count);count;assetbundleRefCount[assetbundleName] count;return count;}protected int DecreaseReferenceCount(string assetbundleName){int count 0;assetbundleRefCount.TryGetValue(assetbundleName, out count);count–;assetbundleRefCount[assetbundleName] count;return count;}protected bool CreateAssetBundleAsync(string assetbundleName){if (IsAssetBundleLoaded(assetbundleName) || webRequesting.ContainsKey(assetbundleName)){return false;}var creater ResourceWebRequester.Get();var url AssetBundleUtility.GetAssetBundleFileUrl(assetbundleName);creater.Init(assetbundleName, url);webRequesting.Add(assetbundleName, creater);webRequesterQueue.Enqueue(creater);// 创建器持有的引用创建器对每个ab来说是全局唯一的IncreaseReferenceCount(assetbundleName);return true;}// 异步请求Assetbundle资源AB是否缓存取决于是否设置为常驻包Assets一律缓存处理依赖public BaseAssetBundleAsyncLoader LoadAssetBundleAsync(string assetbundleName){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){return new EditorAssetBundleAsyncLoader(assetbundleName);} #endifvar loader AssetBundleAsyncLoader.Get();prosessingAssetBundleAsyncLoader.Add(loader);if (manifest ! null){string[] dependancies manifest.GetAllDependencies(assetbundleName);for (int i 0; i dependancies.Length; i){var dependance dependancies[i];if (!string.IsNullOrEmpty(dependance) dependance ! assetbundleName){CreateAssetBundleAsync(dependance);// ab缓存对依赖持有的引用IncreaseReferenceCount(dependance);}}loader.Init(assetbundleName, dependancies);}else{loader.Init(assetbundleName, null);}CreateAssetBundleAsync(assetbundleName);// 加载器持有的引用同一个ab能同时存在多个加载器等待ab创建器完成IncreaseReferenceCount(assetbundleName);return loader;}// 从服务器下载网页内容需提供完整urlpublic ResourceWebRequester DownloadWebResourceAsync(string url){var creater ResourceWebRequester.Get();creater.Init(url, url, true);webRequesting.Add(url, creater);webRequesterQueue.Enqueue(creater);return creater;}// 从资源服务器下载非Assetbundle资源public ResourceWebRequester DownloadAssetFileAsync(string filePath){if (string.IsNullOrEmpty(DownloadUrl)){Debug.LogError(You should set download url first!!!);return null;}var creater ResourceWebRequester.Get();var url DownloadUrl filePath;creater.Init(filePath, url, true);webRequesting.Add(filePath, creater);webRequesterQueue.Enqueue(creater);return creater;}// 从资源服务器下载Assetbundle资源不缓存无依赖public ResourceWebRequester DownloadAssetBundleAsync(string filePath){// 如果ResourceWebRequester升级到使用UnityWebRequester那么下载AB和下载普通资源需要两个不同的DownLoadHandler// 兼容升级的可能性这里也做一下区分return DownloadAssetFileAsync(filePath);}// 本地异步请求非Assetbundle资源public ResourceWebRequester RequestAssetFileAsync(string filePath, bool streamingAssetsOnly true){var creater ResourceWebRequester.Get();string url null;if (streamingAssetsOnly){url AssetBundleUtility.GetStreamingAssetsFilePath(filePath);}else{url AssetBundleUtility.GetAssetBundleFileUrl(filePath);}creater.Init(filePath, url, true);webRequesting.Add(filePath, creater);webRequesterQueue.Enqueue(creater);return creater;}// 本地异步请求Assetbundle资源不缓存无依赖public ResourceWebRequester RequestAssetBundleAsync(string assetbundleName){var creater ResourceWebRequester.Get();var url AssetBundleUtility.GetAssetBundleFileUrl(assetbundleName);creater.Init(assetbundleName, url, true);webRequesting.Add(assetbundleName, creater);webRequesterQueue.Enqueue(creater);return creater;}public void UnloadAssetBundleDependencies(string assetbundleName){if (manifest ! null){string[] dependancies manifest.GetAllDependencies(assetbundleName);for (int i 0; i dependancies.Length; i){var dependance dependancies[i];if (!string.IsNullOrEmpty(dependance) dependance ! assetbundleName){UnloadAssetBundle(dependance);}}}}protected bool UnloadAssetBundle(string assetbundleName, bool unloadResident false, bool unloadAllLoadedObjects false){int count GetReferenceCount(assetbundleName);if (count 0){return false;}count DecreaseReferenceCount(assetbundleName);if (count 0){return false;}var assetbundle GetAssetBundleCache(assetbundleName);var isResident IsAssetBundleResident(assetbundleName);if (assetbundle ! null){if (!isResident || isResident unloadResident){assetbundle.Unload(unloadAllLoadedObjects);RemoveAssetBundleCache(assetbundleName);UnloadAssetBundleDependencies(assetbundleName);return true;}}return false;}public bool TryUnloadAssetBundle(string assetbundleName, bool unloadAllLoadedObjects false){int count GetReferenceCount(assetbundleName);if (count 0){return false;}return UnloadAssetBundle(assetbundleName, true, unloadAllLoadedObjects);}public void UnloadUnusedAssetBundles(bool unloadResident false, bool unloadAllLoadedObjects false){int unloadCount 0;bool hasDoUnload false;do{hasDoUnload false;var iter assetbundleRefCount.GetEnumerator();while (iter.MoveNext()){var assetbundleName iter.Current.Key;var referenceCount iter.Current.Value;if (referenceCount 0){var result UnloadAssetBundle(assetbundleName, unloadResident, unloadAllLoadedObjects);if (result){unloadCount;hasDoUnload true;}}}} while (hasDoUnload);}public bool MapAssetPath(string assetPath, out string assetbundleName, out string assetName){return assetsPathMapping.MapAssetPath(assetPath, out assetbundleName, out assetName);}public BaseAssetAsyncLoader LoadAssetAsync(string assetPath, System.Type assetType){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){string path AssetBundleUtility.PackagePathToAssetsPath(assetPath); UnityEngine.Object target AssetDatabase.LoadAssetAtPath(path, assetType);return new EditorAssetAsyncLoader(target);} #endifstring assetbundleName null;string assetName null;bool status MapAssetPath(assetPath, out assetbundleName, out assetName);if (!status){Debug.LogError(No assetbundle at asset path : assetPath);return null;}var loader AssetAsyncLoader.Get();prosessingAssetAsyncLoader.Add(loader);if (IsAssetLoaded(assetName)){loader.Init(assetName, GetAssetCache(assetName));return loader;}else{var assetbundleLoader LoadAssetBundleAsync(assetbundleName);loader.Init(assetName, assetbundleLoader);return loader;}}void Update(){OnProsessingWebRequester();OnProsessingAssetBundleAsyncLoader();OnProsessingAssetAsyncLoader();}void OnProsessingWebRequester(){for (int i prosessingWebRequester.Count - 1; i 0; i–){var creater prosessingWebRequester[i];creater.Update();if (creater.IsDone()){prosessingWebRequester.RemoveAt(i);webRequesting.Remove(creater.assetbundleName);UnloadAssetBundle(creater.assetbundleName);if (creater.noCache){return;}// 说明有错误也缓存下来只不过资源为空// 1、避免再次错误加载// 2、如果不存下来加载器将无法判断什么时候结束AddAssetBundleCache(creater.assetbundleName, creater.assetbundle);creater.Dispose();}}int slotCount prosessingWebRequester.Count;while (slotCount MAX_ASSETBUNDLE_CREATE_NUM webRequesterQueue.Count 0){var creater webRequesterQueue.Dequeue();creater.Start();prosessingWebRequester.Add(creater);slotCount;}}void OnProsessingAssetBundleAsyncLoader(){for (int i prosessingAssetBundleAsyncLoader.Count - 1; i 0; i–){var loader prosessingAssetBundleAsyncLoader[i];loader.Update();if (loader.IsDone()){UnloadAssetBundle(loader.assetbundleName);prosessingAssetBundleAsyncLoader.RemoveAt(i);}}}void OnProsessingAssetAsyncLoader(){for (int i prosessingAssetAsyncLoader.Count - 1; i 0; i–){var loader prosessingAssetAsyncLoader[i];loader.Update();if (loader.IsDone()){prosessingAssetAsyncLoader.RemoveAt(i);}}}#if UNITY_EDITOR[BlackList]public HashSetstring GetAssetbundleResident(){return assetbundleResident;}[BlackList]public ICollectionstring GetAssetbundleCaching(){return assetbundlesCaching.Keys;}[BlackList]public Dictionarystring, ResourceWebRequester GetWebRequesting(){return webRequesting;}[BlackList]public QueueResourceWebRequester GetWebRequestQueue(){return webRequesterQueue;}[BlackList]public ListResourceWebRequester GetProsessingWebRequester(){return prosessingWebRequester;}[BlackList]public ListAssetBundleAsyncLoader GetProsessingAssetBundleAsyncLoader(){return prosessingAssetBundleAsyncLoader;}[BlackList]public ListAssetAsyncLoader GetProsessingAssetAsyncLoader(){return prosessingAssetAsyncLoader;}[BlackList]public string GetAssetBundleName(string assetName){return assetsPathMapping.GetAssetBundleName(assetName);}[BlackList]public int GetAssetCachingCount(){return assetsCaching.Count;}[BlackList]public Dictionarystring, Liststring GetAssetCaching(){var assetbundleDic new Dictionarystring, Liststring();Liststring assetNameList null;var iter assetsCaching.GetEnumerator();while (iter.MoveNext()){var assetName iter.Current.Key;var assetbundleName assetsPathMapping.GetAssetBundleName(assetName);assetbundleDic.TryGetValue(assetbundleName, out assetNameList);if (assetNameList null){assetNameList new Liststring();}assetNameList.Add(assetName);assetbundleDic[assetbundleName] assetNameList;}return assetbundleDic;}[BlackList]public int GetAssetbundleRefrenceCount(string assetbundleName){return GetReferenceCount(assetbundleName);}[BlackList]public int GetAssetbundleDependenciesCount(string assetbundleName){string[] dependancies manifest.GetAllDependencies(assetbundleName);int count 0;for (int i 0; i dependancies.Length; i){var cur dependancies[i];if (!string.IsNullOrEmpty(cur) cur ! assetbundleName){count;}}return count;}[BlackList]public Liststring GetAssetBundleRefrences(string assetbundleName){Liststring refrences new Liststring();var cachingIter assetbundlesCaching.GetEnumerator();while (cachingIter.MoveNext()){var curAssetbundleName cachingIter.Current.Key;if (curAssetbundleName assetbundleName){continue;}string[] dependancies manifest.GetAllDependencies(curAssetbundleName);for (int i 0; i dependancies.Length; i){var dependance dependancies[i];if (dependance assetbundleName){refrences.Add(curAssetbundleName);}}}var requestingIter webRequesting.GetEnumerator();while (requestingIter.MoveNext()){var curAssetbundleName requestingIter.Current.Key;if (curAssetbundleName assetbundleName){continue;}string[] dependancies manifest.GetAllDependencies(curAssetbundleName);for (int i 0; i dependancies.Length; i){var dependance dependancies[i];if (dependance assetbundleName){refrences.Add(curAssetbundleName);}}}return refrences;}[BlackList]public Liststring GetWebRequesterRefrences(string assetbundleName){Liststring refrences new Liststring();var iter webRequesting.GetEnumerator();while (iter.MoveNext()){var curAssetbundleName iter.Current.Key;var webRequster iter.Current.Value;if (curAssetbundleName assetbundleName){refrences.Add(webRequster.Sequence.ToString());continue;}}return refrences;}[BlackList]public Liststring GetAssetBundleLoaderRefrences(string assetbundleName){Liststring refrences new Liststring();var iter prosessingAssetBundleAsyncLoader.GetEnumerator();while (iter.MoveNext()){var curAssetbundleName iter.Current.assetbundleName;var curLoader iter.Current;if (curAssetbundleName assetbundleName){refrences.Add(curLoader.Sequence.ToString());}}return refrences;} #endif} } 启动游戏时 GameLauch public class GameLaunch : MonoBehaviour {void Awake() { // 初始化框架this.gameObject.AddComponentshow_fps();this.gameObject.AddComponentxLuaMgr();this.gameObject.AddComponentResMgr();// end xLuaMgr.Instance.Init();}IEnumerator InitPackageName(){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){yield break;} #endif// 重要请求本地文件这里要先看看可写的本地目录而不是Streaming目录var packageNameRequest AssetBundleManager.Instance.RequestAssetFileAsync(BuildUtils.PackageNameFileName);yield return packageNameRequest; // 中断协程直到请求结束var packageName packageNameRequest.text;packageNameRequest.Dispose();AssetBundleManager.ManifestBundleName packageName;ChannelManager.instance.Init(packageName);Debug.Log(string.Format(packageName {0}, packageName));yield break;}IEnumerator GameStart(){var start DateTime.Now;yield return InitPackageName();Debug.Log(string.Format(InitPackageName use {0}ms, (DateTime.Now - start).Milliseconds));// 启动资源管理模块start DateTime.Now;yield return AssetBundleManager.Instance.Initialize();Debug.Log(string.Format(AssetBundleManager Initialize use {0}ms, (DateTime.Now - start).Milliseconds));string luaAssetbundleName xLuaMgr.Instance.AssetbundleName;AssetBundleManager.Instance.SetAssetBundleResident(luaAssetbundleName, true);var abloader AssetBundleManager.Instance.LoadAssetBundleAsync(luaAssetbundleName);yield return abloader;abloader.Dispose();xLuaMgr.Instance.EnterLuaGame();yield break;}void Start () {this.StartCoroutine(this.GameStart());}void Update () {} } ResMgr using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using XLua; using AssetBundles;[LuaCallCSharp] // Lua 能否调用到这个装饰器很重要; public class ResMgr : UnitySingletonResMgr { public override void Awake() {base.Awake();}public UnityEngine.Object GetAssetCache(string name, string type_name) { #if UNITY_EDITOR// Type.GetType(资源名字)if (AssetBundleConfig.IsEditorMode){string path AssetBundleUtility.PackagePathToAssetsPath(name);// LoadAssetAtPath 只支持模板模式;// UnityEditor.AssetDatabase.LoadAssetAtPath(name, GameObject)// 根据资源类型的名字来加if判断来使用模板函数;UnityEngine.Object target UnityEditor.AssetDatabase.LoadAssetAtPathGameObject(path);return target;} #endifreturn AssetBundleManager.Instance.GetAssetCache(name);}public void LoadAssetBundleAsync(string assetbundleName, Action end_func){this.StartCoroutine(this.IE_LoadAssetBundleAsync(assetbundleName, end_func));}IEnumerator IE_LoadAssetBundleAsync(string assetbundleName, Action end_func) {var loader AssetBundleManager.Instance.LoadAssetBundleAsync(assetbundleName);yield return loader;end_func();} } 代码热更 具体热更步骤从前面的文章查看简单来说是比较本地版本和服务器版本再看看哪些有变动然后下载。 using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using AssetBundles; using GameChannel;public class GameLaunch : MonoBehaviour {void Awake() { // 初始化框架this.gameObject.AddComponentAssetBundleManager(); // 实例化一个AssetBudnleManager;this.gameObject.AddComponentshow_fps();this.gameObject.AddComponentxLuaMgr();this.gameObject.AddComponentResMgr();// end xLuaMgr.Instance.Init();}IEnumerator InitPackageName(){ #if UNITY_EDITORif (AssetBundleConfig.IsEditorMode){yield break;} #endifvar packageNameRequest AssetBundleManager.Instance.RequestAssetFileAsync(BuildUtils.PackageNameFileName);yield return packageNameRequest; // 中断当前协程直到请求结束;var packageName packageNameRequest.text;packageNameRequest.Dispose(); // 释放请求;AssetBundleManager.ManifestBundleName packageName; // 包名字;ChannelManager.instance.Init(packageName);Debug.Log(string.Format(packageName {0}, packageName));yield break;}IEnumerator CheckAndDownload() {// 如果已经是最新的直接返回就可以了;// end // 更新的资源包的下载; 直接下载, 最新的ab包;// 根据版本拉取要下载文件列表然后来一个个下载, 下载完成后直接进入游戏即可;// 检车更新;var downloadRequest AssetBundleManager.Instance.DownloadAssetBundleAsync(lua.assetbundle);yield return downloadRequest;GameUtility.SafeWriteAllBytes(AssetBundleUtility.GetPersistentDataPath() /lua.assetbundle, downloadRequest.bytes);downloadRequest.Dispose();// end yield break;}IEnumerator GameStart(){var start DateTime.Now;yield return InitPackageName();Debug.Log(string.Format(InitPackageName use {0}ms, (DateTime.Now - start).Milliseconds));// 启动资源管理模块start DateTime.Now;yield return AssetBundleManager.Instance.Initialize();Debug.Log(string.Format(AssetBundleManager Initialize use {0}ms, (DateTime.Now - start).Milliseconds));// 启动检测更新yield return CheckAndDownload();// end string luaAssetbundleName xLuaMgr.Instance.AssetbundleName;// Lua脚本设置的是常驻AB包不是释放的;AssetBundleManager.Instance.SetAssetBundleResident(luaAssetbundleName, true);var abloader AssetBundleManager.Instance.LoadAssetBundleAsync(luaAssetbundleName);yield return abloader;abloader.Dispose();xLuaMgr.Instance.EnterLuaGame();yield break;}void Start () {this.StartCoroutine(this.GameStart());}void Update () {} } Lua和C#通讯原理重要 应用 想要在Lua添加C#写的组件需要在代码中加上[LuaCallCSharp]在Lua脚本中AddComponentAddComponent之后我们就可以在lua中利用这个组件调用其中的函数了 原理 [LuaCallCSharp]这个注解会做lua导出在Xlua插件中的Gen文件夹有很多Wrap代码当点击Xlua插件的生成代码按钮后拥有[LuaCallCSharp]注解的类会写到一个link.xml文件中并会导出对应的包装文件到wrap文件夹中导出后Lua才能调用C#脚本中编写的函数。要想通过lua调用这个方法lua类型中的元表必须有这个方法因此在C#端需要把元表设置好。Utils.BeginObjectRegister往类型中添加了元表加了元表后把方法都注册到了元表中这些Wrap文件会在XLuaGenAutoRegister脚本中统一被调用注册和导出函数这些都放在脚本的初始化函数中初始化函数在虚拟机启动时调用。 以ResMgrWrap示例 #if USE_UNI_LUA using LuaAPI UniLua.Lua; using RealStatePtr UniLua.ILuaState; using LuaCSFunction UniLua.CSharpFunctionDelegate; #else using LuaAPI XLua.LuaDLL.Lua; using RealStatePtr System.IntPtr; using LuaCSFunction XLua.LuaDLL.lua_CSFunction; #endif using XLua; using System.Collections.Generic;namespace XLua.CSObjectWrap {using Utils XLua.Utils;public class ResMgrWrap {public static void __Register(RealStatePtr L){ObjectTranslator translator ObjectTranslatorPool.Instance.Find(L);System.Type type typeof(ResMgr);// 创建一个元表Utils.BeginObjectRegister(type, L, translator, 0, 3, 0, 0);// 注册方法Utils.RegisterFunc(L, Utils.METHOD_IDX, Awake, _m_Awake);Utils.RegisterFunc(L, Utils.METHOD_IDX, GetAssetCache, _m_GetAssetCache);Utils.RegisterFunc(L, Utils.METHOD_IDX, LoadAssetBundleAsync, _m_LoadAssetBundleAsync);Utils.EndObjectRegister(type, L, translator, null, null,null, null, null);Utils.BeginClassRegister(type, L, __CreateInstance, 1, 0, 0);Utils.EndClassRegister(type, L, translator);}[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]static int CreateInstance(RealStatePtr L){try {ObjectTranslator translator ObjectTranslatorPool.Instance.Find(L);if(LuaAPI.lua_gettop(L) 1){ResMgr gen_ret new ResMgr();translator.Push(L, gen_ret);return 1;}}catch(System.Exception gen_e) {return LuaAPI.luaL_error(L, c# exception: gen_e);}return LuaAPI.luaL_error(L, invalid arguments to ResMgr constructor!);} [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]static int _m_Awake(RealStatePtr L){try {ObjectTranslator translator ObjectTranslatorPool.Instance.Find(L);ResMgr gen_to_be_invoked (ResMgr)translator.FastGetCSObj(L, 1); {gen_to_be_invoked.Awake( );return 0;}} catch(System.Exception gen_e) {return LuaAPI.luaL_error(L, c# exception: gen_e);}}[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]static int _m_GetAssetCache(RealStatePtr L){try {ObjectTranslator translator ObjectTranslatorPool.Instance.Find(L); ResMgr gen_to_be_invoked (ResMgr)translator.FastGetCSObj(L, 1);{string _name LuaAPI.lua_tostring(L, 2);string _type_name LuaAPI.lua_tostring(L, 3);UnityEngine.Object gen_ret gen_to_be_invoked.GetAssetCache( _name, _type_name );translator.Push(L, gen_ret); return 1;}} catch(System.Exception gen_e) {return LuaAPI.luaL_error(L, c# exception: gen_e);}}[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]static int _m_LoadAssetBundleAsync(RealStatePtr L){try {ObjectTranslator translator ObjectTranslatorPool.Instance.Find(L);ResMgr gen_to_be_invoked (ResMgr)translator.FastGetCSObj(L, 1);{string _assetbundleName LuaAPI.lua_tostring(L, 2);System.Action _end_func translator.GetDelegateSystem.Action(L, 3);gen_to_be_invoked.LoadAssetBundleAsync( _assetbundleName, _end_func ); return 0;}} catch(System.Exception gen_e) {return LuaAPI.luaL_error(L, c# exception: gen_e);}} } } public class XLua_Gen_Initer_Register {static void wrapInit0(LuaEnv luaenv, ObjectTranslator translator){ translator.DelayWrapLoader(typeof(ResMgr), ResMgrWrap.Register);–snip–static void Init(LuaEnv luaenv, ObjectTranslator translator){wrapInit0(luaenv, translator);wrapInit1(luaenv, translator);translator.AddInterfaceBridgeCreator(typeof(System.Collections.IEnumerator), SystemCollectionsIEnumeratorBridge.Create);translator.AddInterfaceBridgeCreator(typeof(XLuaTest.IExchanger), XLuaTestIExchangerBridge.Create);translator.AddInterfaceBridgeCreator(typeof(Tutorial.CSCallLua.ItfD), TutorialCSCallLuaItfDBridge.Create);translator.AddInterfaceBridgeCreator(typeof(XLuaTest.InvokeLua.ICalc), XLuaTestInvokeLuaICalcBridge.Create);}static XLua_Gen_Initer_Register(){XLua.LuaEnv.AddIniter(Init);} namespace XLua {public partial class ObjectTranslator{static XLua.CSObjectWrap.XLua_Gen_Initer_Register__ s_gen_reg_dumb_obj new XLua.CSObjectWrap.XLua_Gen_Initer_Register();static XLua.CSObjectWrap.XLua_Gen_Initer_Register gen_reg_dumb_obj {get{return s_gen_reg_dumb_obj;}}}internal partial class InternalGlobals{static InternalGlobals(){extensionMethodMap new DictionaryType, IEnumerableMethodInfo(){};genTryArrayGetPtr StaticLuaCallbacks.tryArrayGet;genTryArraySetPtr StaticLuaCallbacks.tryArraySet;}} } 从上面的ObjectTranslator就会进行启动注册 为什么CS.XXX能访问C#中的代码 是因为LuaEnv初始化的时候会执行lua代码代码里面会创建CS {},CS这个table的元表的__index元方法你可以看下里面会用到xlua.import_type函数。干活的逻辑就是csharp层的ImportType 也就是说创建一个类型表一种方式是lua层CS.A.B.C 会通过触发元方法走到csharp层创建也是到getTypeId。另一种是当push该类型的obj实例的时候也会到getTypeId 可参考的链接 https://www.cnblogs.com/iwiniwin/p/15307368.html https://www.cnblogs.com/iwiniwin/p/15323970.html