放在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配置数据
///
/// 打包生成所有AssetBundles
///
[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);
}
///
/// 删除AssetBundle包文件
///
[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+"文件夹不存在");
}
}
}
///
/// (自动)给资源文件(预设)添加标记
///
[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 标签设置完成!");
}
///
/// 递归调用与处理目录或文件系统
/// 1:如果是目录,则进行递归调用。
/// 2:如果是文件,则给文件做“AB标记”
///
/// 目录信息
/// 场景名称
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);
}
}
}
///
/// 修改文件的AssetBundle 标记
///
/// 文件信息
/// 场景名称
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);
}
///
/// 获取包名
///
/// 文件信息
/// 场景名称
///
/// 返回: 包名称
///
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;
}
///
///获取到unity中当前切换的平台
///
///
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);
}
///
/// 可以自定义加载ab包中资源的action操作
///
/// 要加载的ab包中的资源名称
/// 加载到资源后的事件函数(参数为加载到的资源)
/// 加载到的资源的类型
public void ShowABAsset(string prefabName,Action<UnityEngine.Object> action,Type type)
{
action += (s) => { SystemDefine.loadingPanel.SetActive(false); };
ABResourcesLoader.Instance.TryLoadAssetAndSave(prefabName, action, type);
}
///
/// 将ab包中资源加载到场景
///
/// ab包中资源的名称
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
///
/// 加载单个ab包到缓存字典
///
/// ab包名字
///
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();
}
}
}
///
/// 将assetSet对象加入队列
///
///
private void AddSetToAssetBundleQueue(AssetSet assetSet)
{
assetBundleQueue.Enqueue(assetSet);
}
///
/// 尝试加载ab包,ab包中的文件
///
/// ab包名
/// ab包中的资源名
/// 加载后的事件
/// 加载的资源的类型
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 _beignAction, Action _loadingAction, Action _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);
//}
///
/// 查询资源包(加载)
///
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
///
/// 加载资源名对应的ab包
///
///
///
///
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
{
///
/// 获取到到键值对存储
///
Dictionary<string, string> AppJsonSetting { get; }
///
/// 获取配置的最大长度
///
///
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;
///
/// 获取到有读取后的json配置存储的集合
///
public Dictionary<string, string> AppJsonSetting
{
get
{
return _AppJsonSetting;
}
}
//集合的做大长度
public int maxAppJsonSettingNum()
{
if (_AppJsonSetting != null && _AppJsonSetting.Count >= 1)
{
return _AppJsonSetting.Count;
}
else
{
return 0;
}
}
///
/// 构造函数
///
/// json文件的路径
public JsonConfigManger(string jsonName)
{
_AppJsonSetting = new Dictionary<string, string>();
if (string.IsNullOrEmpty(jsonName)) return;
AnalysisJsonAndSave(jsonName);
}
///
/// 将打包的ab数据配置存储更新
///
///
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);
}
}
///
/// 读取到json配置 并存储起来
///
/// json的路径
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
///
/// 获取到需要加载的ab包路径
///
///
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;
}
///
/// 获取到打包出的ab包路径
///
///
public static string GetABOutFile()
{
return GetPlatformPath() + "/" + GetPlatformName();
}
///
/// 获取打ab资源存储的文件夹(ab_resourses根目录)
///
///
public static string GetABResoursesPath()
{
return Application.dataPath + "/" + ABResoursesPath;
}
#endregion
#region privateMethod
///
/// 获取平台路径
///
///
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;
}
///
/// 获取平台名称
///
///
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;
///
/// 一个ab包中的资源对象
///
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)