放在Editor文件夹下的编辑器扩展脚本,用于打包ab包,输出目录为StreamingAssets文件夹下 BuildAssetBundle .cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.IO; namespace FWork { public class BuildAssetBundle { public static KeyValueInfo keyValueList = null;//存储设置label的ab配置数据 /// <summary> /// 打包生成所有AssetBundles /// </summary> [MenuItem("AssetBundleTools/BuildAllAssetBundles")] public static void BuildAllAB() { //(打包)AB的输出路径 string strABOutPathDIR = ""; BuildTarget buildTarget = BuildTarget.NoTarget; buildTarget = GetActiveBuildTarget(); strABOutPathDIR = PathManger.GetABOutFile(); if (!Directory.Exists(strABOutPathDIR)) { Directory.CreateDirectory(strABOutPathDIR); } //打包生成 BuildPipeline.BuildAssetBundles(strABOutPathDIR, BuildAssetBundleOptions.ChunkBasedCompression, buildTarget); AssetDatabase.Refresh(); Debug.Log("AB资源打包完成-文件路径:" + strABOutPathDIR); } /// <summary> /// 删除AssetBundle包文件 /// </summary> [MenuItem("AssetBundleTools/DeleteAllAssetBundles")] public static void DeleteAllABs() { //(打包)AB的输出路径 string strNeedDeleteDIR = ""; strNeedDeleteDIR = PathManger.GetABOutFile(); if (!string.IsNullOrEmpty(strNeedDeleteDIR)) { try { //参数true 表示可以删除非空目录。 Directory.Delete(strNeedDeleteDIR, true); //去除删除警告 File.Delete(strNeedDeleteDIR + ".meta"); //刷新 AssetDatabase.Refresh(); Debug.Log("资源删除完成!"); } catch (System.Exception) { Debug.Log(strNeedDeleteDIR+"文件夹不存在"); } } } /// <summary> /// (自动)给资源文件(预设)添加标记 /// </summary> [MenuItem("AssetBundleTools/Set AB Label")] public static void SetABLabels() { keyValueList = new KeyValueInfo(); keyValueList.KeyValueList = new List<KeyValueNode>(); keyValueList.KeyValueList.Clear(); //需要给AB做标记的根目录 string strNeedSetABLableRootDIR = ""; //目录信息 DirectoryInfo[] dirScenesDIRArray = null; //清空无用AB标记 AssetDatabase.RemoveUnusedAssetBundleNames(); //定位需要打包资源的文件夹根目录。 strNeedSetABLableRootDIR = PathManger.GetABResoursesPath(); DirectoryInfo dirTempInfo = new DirectoryInfo(strNeedSetABLableRootDIR); dirScenesDIRArray = dirTempInfo.GetDirectories(); //遍历每个“场景”文件夹(目录) foreach (DirectoryInfo currentDIR in dirScenesDIRArray) { //遍历本场景目录下的所有的目录或者文件, //如果是目录,则继续递归访问里面的文件,直到定位到文件。 string tmpScenesDIR = strNeedSetABLableRootDIR + "/" + currentDIR.Name;//res/** DirectoryInfo tmpScenesDIRInfo = new DirectoryInfo(tmpScenesDIR); //场景目录信息res/**/** int tmpIndex = tmpScenesDIR.LastIndexOf("/"); string tmpScenesName = tmpScenesDIR.Substring(tmpIndex + 1); //场景名称 //递归调用与处理目录或文件系统,如果找到文件,修改AssetBundle 的标签(label) JudgeDIROrFileByRecursive(currentDIR, tmpScenesName); }//foreach_end //将生成的bundle名与资源名添加到json文件 JsonConfigManger.DataToJson(PathManger.jsonInfoPath, keyValueList); Debug.Log("Json数据配置完成!"); //刷新 AssetDatabase.Refresh(); //提示 Debug.Log("AssetBundles 标签设置完成!"); } /// <summary> /// 递归调用与处理目录或文件系统 /// 1:如果是目录,则进行递归调用。 /// 2:如果是文件,则给文件做“AB标记” /// </summary> /// <param name="dirInfo">目录信息</param> /// <param name="scenesName">场景名称</param> private static void JudgeDIROrFileByRecursive(FileSystemInfo fileSysInfo, string scenesName) { if (!fileSysInfo.Exists) { Debug.LogError("文件或目录名称: " + fileSysInfo.Name + " 不存在,请检查!"); return; } //得到当前目录下一级的文件信息集合 DirectoryInfo dirInfoObj = fileSysInfo as DirectoryInfo; FileSystemInfo[] fileSysArray = dirInfoObj.GetFileSystemInfos(); foreach (FileSystemInfo fileInfo in fileSysArray) { FileInfo fileInfoObj = fileInfo as FileInfo; //文件类型 if (fileInfoObj != null) { //修改此文件的AssetBundle的标签 // SetFileABLabel(fileInfoObj, scenesName); SetFileABLabel(fileInfoObj, dirInfoObj.Name); } //目录类型 else { //递归下一层 JudgeDIROrFileByRecursive(fileInfo, scenesName); } } } /// <summary> /// 修改文件的AssetBundle 标记 /// </summary> /// <param name="fileInfo">文件信息</param> /// <param name="scenesName">场景名称</param> private static void SetFileABLabel(FileInfo fileInfo, string prefabName) { //AssetBundle 包名称 string strABName = string.Empty; //(资源)文件路径(相对路径) string strAssetFilePath = string.Empty; //参数检查 if (fileInfo.Extension == ".meta") return; //得到AB包名 // strABName = GetABName(fileInfo, prefabName).ToLower(); /* 使用AssetImporter 类,修改名称与后缀 */ //获取资源文件相对路径 int tmpIndex = fileInfo.FullName.IndexOf("Assets"); strAssetFilePath = fileInfo.FullName.Substring(tmpIndex); //给资源文件设置AB名称与后缀 AssetImporter tmpAssetImportObj = AssetImporter.GetAtPath(strAssetFilePath); tmpAssetImportObj.assetBundleName = prefabName.ToLower(); //设置AB包名 if (fileInfo.Extension == ".unity") //设置AB包扩展名称 tmpAssetImportObj.assetBundleVariant = "u3d"; else tmpAssetImportObj.assetBundleVariant = "ab";//AB资源包 //将数据配置存储以备更新json中配置数据 int dex = strAssetFilePath.LastIndexOf("\\"); string abnameKey = strAssetFilePath.Substring(dex + 1).Replace(".prefab",""); string abnameValue = tmpAssetImportObj.assetBundleName + "." + tmpAssetImportObj.assetBundleVariant; KeyValueNode valueNode = new KeyValueNode { Key = abnameKey, Value = abnameValue }; keyValueList.KeyValueList.Add(valueNode); } /// <summary> /// 获取包名 /// </summary> /// <param name="fileInfo">文件信息</param> /// <param name="scenesName">场景名称</param> /// <returns> /// 返回: 包名称 /// </returns> private static string GetABName(FileInfo fileInfo, string scenesName) { string strABName = string.Empty; //Win路径 string tmpWinPath = fileInfo.FullName; //Unity路径 string tmpUnityPath = tmpWinPath.Replace("\\", "/"); //定位“场景名称”后面的字符位置 int tmpSceneNamePosIndex = tmpUnityPath.IndexOf(scenesName) + scenesName.Length; //AB文件名称大体区域 string strABFileNameArea = tmpUnityPath.Substring(tmpSceneNamePosIndex + 1); if (strABFileNameArea.Contains("/")) { string[] tmpStrArray = strABFileNameArea.Split('/'); strABName = scenesName + "/" + tmpStrArray[0]; } else { strABName = scenesName + "/" + scenesName; } return strABName; } /// <summary> ///获取到unity中当前切换的平台 /// </summary> /// <returns></returns> private static BuildTarget GetActiveBuildTarget() { return EditorUserBuildSettings.activeBuildTarget; } } }调用GameManger的方法将ab包中的资源加载到场景 GameManger.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using FWork; using System; public class GameManger : MonoBehaviour { private static GameManger instance; public static GameManger Instance { get { if (instance == null) { GameObject game = new GameObject("GameManger"); instance = game.AddComponent<GameManger>(); } return instance; } } private void Awake() { if (instance == null) instance = this; else Destroy(gameObject); DontDestroyOnLoad(gameObject); } /// <summary> /// 可以自定义加载ab包中资源的action操作 /// </summary> /// <param name="prefabName">要加载的ab包中的资源名称</param> /// <param name="action">加载到资源后的事件函数(参数为加载到的资源)</param> /// <param name="type">加载到的资源的类型</param> public void ShowABAsset(string prefabName,Action<UnityEngine.Object> action,Type type) { action += (s) => { SystemDefine.loadingPanel.SetActive(false); }; ABResourcesLoader.Instance.TryLoadAssetAndSave(prefabName, action, type); } /// <summary> /// 将ab包中资源加载到场景 /// </summary> /// <param name="assetName">ab包中资源的名称</param> public void ShowToGame(string assetName) { ShowABAsset(assetName, (s) => { GameObject o = s as GameObject; Instantiate<GameObject>(o); Debug.Log("加载到ab中资源并克隆到到场景:"+o.name); //TODO 这里可以给资源给定初始数据(旋转,位置等),可以在外部加载配置文件或者写在资源对象的挂在脚本中,加载时获取 // ABResourcesLoader.Instance.overAction();//停止加载动画 }, typeof(GameObject)); } }ABResourcesLoader脚本 主要功能是从指定位置(web上)加载ab包 ABResourcesLoader.cs
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; namespace FWork { public class ABResourcesLoader : MonoBehaviour { private static ABResourcesLoader instance; public static ABResourcesLoader Instance { get { if (instance == null) { GameObject loader = new GameObject("ABResourcesLoader"); instance= loader.AddComponent<ABResourcesLoader>(); } return instance; } } private void Awake() { if (instance == null) { instance = this; } else Destroy(this.gameObject); ABundleDic.Clear(); DontDestroyOnLoad(this.gameObject); } AssetSet thisLoader = null;//目前正在需要加载的资源 bool isLoadingAsset = false; /*开始加载时的事件*/ Action beignAction=null; public Action overAction=null; /* ab资源的队列*/ Queue<AssetSet> assetBundleQueue = new Queue<AssetSet>(); /*存放下载的ab包 key:ab的包名,value:ab包*/ public Dictionary<string, AssetBundle> ABundleDic = new Dictionary<string, AssetBundle>(); private void Start() { //添加开始加载资源动画事件 beignAction = () => { SystemDefine.loadingPanel.SetActive(true); }; //添加加载完后的停止动画事件 overAction = () => { SystemDefine.loadingPanel.SetActive(false); }; } #region privateMethon /// <summary> /// 加载单个ab包到缓存字典 /// </summary> /// <param name="abName">ab包名字</param> /// <returns></returns> private IEnumerator LoadABundle(string abName, Action ac) { //编辑器下test使用 string url = System.IO.Path.Combine(PathManger.GetLoaderPath(), abName); //外网链接(本地服务器测试) // string url = PathManger.assetBundleWebUrl + PathManger.ABProjectName+"/"+abName; Debug.Log("url:" + url); if (string.IsNullOrEmpty(url)) { Debug.LogError(GetType() + "需要下载的" + abName + "资源的url为空!"); } UnityWebRequest webRequest = UnityWebRequest.GetAssetBundle(url); yield return webRequest.SendWebRequest(); if (!string.IsNullOrEmpty(webRequest.error)) { Debug.LogError(webRequest.error + "URL: " + url); } else { // AssetBundle ab = (webRequest.downloadHandler as DownloadHandlerAssetBundle).assetBundle; AssetBundle ab = DownloadHandlerAssetBundle.GetContent(webRequest); if (ab != null) { ABundleDic[abName] = ab; Debug.Log(ab.name + "包加载完成"); ac(); } } } /// <summary> /// 将assetSet对象加入队列 /// </summary> /// <param name="assetSet"></param> private void AddSetToAssetBundleQueue(AssetSet assetSet) { assetBundleQueue.Enqueue(assetSet); } /// <summary> /// 尝试加载ab包,ab包中的文件 /// </summary> /// <param name="abName">ab包名</param> /// <param name="assetName">ab包中的资源名</param> /// <param name="_overAction">加载后的事件</param> /// <param name="loadType">加载的资源的类型</param> private void TryLoadAsset(string abName, string assetName, Action<UnityEngine.Object> _overAction, Type loadType) { //确保在添加事件时事件已赋值(在调用时脚本还未执行start方法) if (beignAction==null||overAction==null) { Start(); } AssetSet abset = new AssetSet//其中的一些配置(位置,旋转都能) { abName = abName.ToLower(), assetName = assetName, returnType = AssetBundleReturnType.Prefab, loadType = loadType, OnLoadBeign =Instance.beignAction, OnLoadOver = _overAction }; AddSetToAssetBundleQueue(abset); Load(); } //private void TryLoadAsset(string abName, string assetName, Action<UnityEngine.Object> _beignAction, Action<UnityEngine.Object> _loadingAction, Action<UnityEngine.Object> _overAction, Type loadType) //{ // AssetSet abset = new AssetSet // { // abName = abName.ToLower(), // assetName = assetName, // returnType = AssetBundleReturnType.Prefab, // loadType = loadType, // OnLoadBeign = _beignAction, // OnLoading = _loadingAction, // OnLoadOver = _overAction // }; // AddSetToAssetBundleQueue(abset); //} /// <summary> /// 查询资源包(加载) /// </summary> private void Load() { if (assetBundleQueue.Count > 0) { AssetSet assetSet = assetBundleQueue.Dequeue(); assetSet.OnLoadBeign();//开始加载动画界面 if (ABundleDic.ContainsKey(assetSet.abName)) { Debug.Log("加载到ab包:" + assetSet.abName); AssetBundle assetBundle = ABundleDic[assetSet.abName]; if (assetBundle != null) { GameObject obj = assetBundle.LoadAsset<GameObject>(assetSet.assetName); assetSet.OnLoadOver(obj); } } else { Debug.Log("开始加载:"+ assetSet.abName); StartCoroutine(LoadABundle(assetSet.abName, () => { AddSetToAssetBundleQueue(assetSet); Load(); })); } } } #endregion /// <summary> /// 加载资源名对应的ab包 /// </summary> /// <param name="assetName"></param> /// <param name="overAction"></param> /// <param name="loadType"></param> public void TryLoadAssetAndSave(string assetName, Action<UnityEngine.Object> overAction, Type loadType) { if (SystemDefine.jsonDic.Count>0) { if (SystemDefine.jsonDic.ContainsKey(assetName)) { string abname = SystemDefine.jsonDic[assetName]; TryLoadAsset(abname,assetName , overAction, loadType); } else { Debug.LogError("Json配置列表无"+assetName+",请检查!"); } } else { Debug.LogError("Json配置未被写入"); } } } }json的读取与解析(Resources文件夹下)备注:在这里我前一天用的json工具是unity中自带的JsonUtility类能用,但第二天却不起作用了 我改用了Litjson,也没找到原因 IConfigManger.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace FWork { public interface IConfigManger { /// <summary> /// 获取到到键值对存储 /// </summary> Dictionary<string, string> AppJsonSetting { get; } /// <summary> /// 获取配置的最大长度 /// </summary> /// <returns></returns> int maxAppJsonSettingNum(); } [SerializeField] public class KeyValueInfo { //json配置列表 public List<KeyValueNode> KeyValueList; } [SerializeField] public class KeyValueNode { //键(资源名) public string Key=""; //值(ab包名) public string Value=""; } }JsonConfigManger .cs
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using LitJson; namespace FWork { public class JsonConfigManger : IConfigManger { //保存(键值对)应用设置集合 private static Dictionary<string, string> _AppJsonSetting; /// <summary> /// 获取到有读取后的json配置存储的集合 /// </summary> public Dictionary<string, string> AppJsonSetting { get { return _AppJsonSetting; } } //集合的做大长度 public int maxAppJsonSettingNum() { if (_AppJsonSetting != null && _AppJsonSetting.Count >= 1) { return _AppJsonSetting.Count; } else { return 0; } } /// <summary> /// 构造函数 /// </summary> /// <param name="jsonPath">json文件的路径</param> public JsonConfigManger(string jsonName) { _AppJsonSetting = new Dictionary<string, string>(); if (string.IsNullOrEmpty(jsonName)) return; AnalysisJsonAndSave(jsonName); } /// <summary> /// 将打包的ab数据配置存储更新 /// </summary> /// <param name="keyValue"></param> public static void DataToJson(string path, KeyValueInfo kvInfo) { if (string.IsNullOrEmpty(path) || kvInfo == null) return; string abstr = JsonMapper.ToJson(kvInfo); try { //覆盖写入 System.IO.File.WriteAllText(Application.dataPath + "/Resources/" + path, abstr); } catch (Exception e) { Debug.LogError(e.GetType() + "-" + e.Message); } } /// <summary> /// 读取到json配置 并存储起来 /// </summary> /// <param name="jsonPath">json的路径</param> private void AnalysisJsonAndSave(string jsonName) { TextAsset jsonstring = null; KeyValueInfo keyValueInfo = null; if (string.IsNullOrEmpty(jsonName)) return; jsonstring = Resources.Load<TextAsset>(jsonName); keyValueInfo = JsonMapper.ToObject<KeyValueInfo>(jsonstring.text); foreach (var item in keyValueInfo.KeyValueList) { _AppJsonSetting.Add(item.Key, item.Value); } } } }路径管理类,其中assetBundleWebUrl 的地址是用xampp搭建的本地服务器,可以自己去搭建一个 xampp 搭建本地服务器: https://www.jianshu.com/p/68e12e98e040
PathManger.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace FWork { public class PathManger { /*需要打包制作成ab资源的文件夹*/ public const string ABResoursesPath = "AB_Resourses"; /*生成的ab资源的上一层文件夹名*/ public const string ABProjectName = "AB";//打包出的bundle文件夹的名字 /*ab资源包的后缀*/ public static string abSuffix = ".ab"; public static string jsonInfoPath = "ABInfoJson.json";//存储bundle名与对应资源名的json文件 //本地测试(使用xampp搭的本地服务器) public static string assetBundleWebUrl = "http://localhost:81/UnityAssets/";//需要下载的ab资源的地址 #region publicMethod /// <summary> /// 获取到需要加载的ab包路径 /// </summary> /// <returns></returns> public static string GetLoaderPath() { string re = assetBundleWebUrl; #if UNITY_EDITOR re = Application.dataPath + "/StreamingAssets/"; re += ABProjectName + "/"; #elif UNITY_STANDALONE_WIN re+="IOS/"; #elif UNITY_IPHONE re+="IOS/"; #elif UNITY_ANDROID re+="Android/"; #elif UNITY_WEBGL #endif return re; } /// <summary> /// 获取到打包出的ab包路径 /// </summary> /// <returns></returns> public static string GetABOutFile() { return GetPlatformPath() + "/" + GetPlatformName(); } /// <summary> /// 获取打ab资源存储的文件夹(ab_resourses根目录) /// </summary> /// <returns></returns> public static string GetABResoursesPath() { return Application.dataPath + "/" + ABResoursesPath; } #endregion #region privateMethod /// <summary> /// 获取平台路径 /// </summary> /// <returns></returns> private static string GetPlatformPath() { string strReturnPlatformPath = ""; switch (Application.platform) { case RuntimePlatform.WindowsPlayer: case RuntimePlatform.WindowsEditor: strReturnPlatformPath = Application.streamingAssetsPath; break; case RuntimePlatform.IPhonePlayer: case RuntimePlatform.Android: strReturnPlatformPath = Application.persistentDataPath; break; default: break; } return strReturnPlatformPath; } /// <summary> /// 获取平台名称 /// </summary> /// <returns></returns> private static string GetPlatformName() { string strReturnPlatformName = ""; switch (Application.platform) { case RuntimePlatform.WindowsPlayer: case RuntimePlatform.WindowsEditor: strReturnPlatformName = ABProjectName; break; case RuntimePlatform.IPhonePlayer: strReturnPlatformName = "Iphone"; break; case RuntimePlatform.Android: strReturnPlatformName = "Android"; break; default: break; } return strReturnPlatformName; } #endregion } }asset资源脚本 AssetSet.cs
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 一个ab包中的资源对象 /// </summary> public class AssetSet { public GameObject root;//ab包资源的对象 public string abName = null;//ab包名字 public string assetName = null; public AssetBundleReturnType returnType = AssetBundleReturnType.None;//ab包中的资源类型 public Vector3 position; public Vector3 scale; public Vector3 rotation; public Type loadType;//从ab包加载的资源的类型 public LoadTpye loadAssetTpye;//加载资源的方式 public bool onlyLoad; public Action OnLoadBeign;//开始加载的事件 public Action<GameObject> OnCloneOver;//gameobject克隆后的事件 public Action<UnityEngine.Object> OnLoading; //加载时的事件 public Action<UnityEngine.Object> OnLoadOver;//加载资源后的事件 public void LoadBeign() { if (OnLoadBeign != null) { try { OnLoadBeign(); } catch (Exception e) { Debug.Log(e.Message+" "+e.StackTrace); } } } public void CloneOver(GameObject asset) { if (OnCloneOver != null) { try { OnCloneOver(asset); } catch (Exception e) { Debug.Log(e.Message + " " + e.StackTrace); } } } public void Loading(UnityEngine.Object o) { if (OnLoading != null) { try { OnLoading(o); } catch (Exception e) { Debug.Log(e.Message + " " + e.StackTrace); } } } public void LoadOver(UnityEngine.Object o) { if (OnLoadOver != null) { try { OnLoadOver(o); } catch (Exception e) { Debug.Log(e.Message + " " + e.StackTrace); } } } } public enum AssetBundleReturnType { None = 0, Object = 1, Prefab = 2, Scene = 3, Sprites = 4, AllObjects = 5, } public enum LoadTpye { None=1, }定义的常量,静态的管理类,可以定义一切的想要保存且唯一的变量与对象
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace FWork { public static class SystemDefine { //常量参数 public const string jsonName = "ABInfoJson"; public const string cubeprefabName = "Cube"; public const string sphereprefabName = "Sphere"; //静态对象 public static Dictionary<string, string> jsonDic; public static GameObject loadingPanel; public static GameObject uuPrefab; } }完整工程github:https://github.com/Tend-R/LIangRepository(unity2017.3)