一、思路如下:
①开发专门的标记脚本,自动给指定目录下面的所有合法资源文件(预设、贴图、材质等)添加标记。
②通过写专门的脚本读取Unity自动创建的 *.manifest文件;自动分析和维护AssetBundle包之间的依赖关系,使得包的依赖关系可以实现循环依赖和自动化加载。
③开发针对AssetBundle专门框架,按照一定的严格流程解决AB包加载、复杂依赖、资源提取释放等事宜,尽可能让最终使用的框架人员只关心输入和输出结果部分,屏蔽内部的复杂性。
④开发的AssetBundle框架中,需要对AssetBundle包之间,以及AssetBundle包内存的资源做缓存设计,并且提供参数开关,让开发者自行决定是否应用缓存加载。
二、AssetBundle框架总体分为三个部分
《1》自动化创建AssetBundle
《2》单一AssetBundle包的加载与管理
《3》AssetBundle整体管理
《1》自动化创建AssetBundle(以下三个脚本需要放在Editor文件夹下面)
①自动给资源文件添加标记(AutoSetLableToPrefabs.cs)
/***
*
* Title: "AssetBundle工具包"项目
* (自动)给资源文件(预设)添加标记
*
* Description:
* 开发思路:
* 1:定位需要打包资源的文件夹根目录。
* 2:遍历每个“场景”文件夹(目录)
* 2.1> 遍历本场景目录下的所有的目录或者文件。
如果是目录,则继续递归访问里面的文件,直到定位到文件。
* 2.2> 找到文件,修改AssetBundle 的标签(label)
* 具体用AssetImporter 类实现,修改包名与后缀。
* Date: 2018
*
* Version: 1.0
*
* Modify Recorder:
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
namespace ABTools
{
public class AutoSetLabelToPrefabs
{
[MenuItem("AssetBundleTools/Set AB Label")]
public static void SetABLabels(){
//需要给AB做标记的根目录
string strNeedSetABLableRootDIR = string.Empty;
//目录信息
DirectoryInfo[] dirScenesDIRArray = null;
//清空无用AB标记
AssetDatabase.RemoveUnusedAssetBundleNames();
//定位需要打包资源的文件夹根目录。
strNeedSetABLableRootDIR = PathTools.GetABResourcesPath();
DirectoryInfo dirTempInfo = new DirectoryInfo(strNeedSetABLableRootDIR);
dirScenesDIRArray = dirTempInfo.GetDirectories();
//遍历每个“场景”文件夹(目录)
foreach (DirectoryInfo currentDIR in dirScenesDIRArray){
//遍历本场景目录下的所有的目录或者文件,
//如果是目录,则继续递归访问里面的文件,直到定位到文件。
string tmpScenesDIR = strNeedSetABLableRootDIR + "/" + currentDIR.Name;
DirectoryInfo tmpScenesDIRInfo = new DirectoryInfo(tmpScenesDIR); //场景目录信息
int tmpIndex = tmpScenesDIR.LastIndexOf("/");
string tmpScenesName = tmpScenesDIR.Substring(tmpIndex+1); //场景名称
//递归调用与处理目录或文件系统,如果找到文件,修改AssetBundle 的标签(label)
JudgeDIROrFileByRecursive(currentDIR,tmpScenesName);
}//foreach_end
//刷新
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);
}
//目录类型
else {
//递归下一层
JudgeDIROrFileByRecursive(fileInfo, scenesName);
}
}
}
///
/// 修改文件的AssetBundle 标记
///
/// 文件信息
/// 场景名称
private static void SetFileABLabel(FileInfo fileInfo,string scenesName){
//AssetBundle 包名称
string strABName = string.Empty;
//(资源)文件路径(相对路径)
string strAssetFilePath = string.Empty;
//参数检查
if (fileInfo.Extension == ".meta") return;
//得到AB包名
strABName = GetABName(fileInfo, scenesName).ToLower();
/* 使用AssetImporter 类,修改名称与后缀 */
//获取资源文件相对路径
int tmpIndex = fileInfo.FullName.IndexOf("Assets");
strAssetFilePath = fileInfo.FullName.Substring(tmpIndex);
//给资源文件设置AB名称与后缀
AssetImporter tmpAssetImportObj = AssetImporter.GetAtPath(strAssetFilePath);
tmpAssetImportObj.assetBundleName = strABName; //设置AB包名
if (fileInfo.Extension==".unity") //设置AB包扩展名称
tmpAssetImportObj.assetBundleVariant = "u3d";
else
tmpAssetImportObj.assetBundleVariant = "ab";//AB资源包
}
///
/// 获取包名
///
/// 文件信息
/// 场景名称
///
/// 返回: 包名称
///
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;
}
}//Class_end
}
②打包资源且输出路径(BuildAssetBundle.cs)
/***
*
* Title: "AssetBundle工具包"项目
* 给AssetBundle目录(资源)打包
*
* Description:
* 功能:[本脚本的主要功能描述]
*
* Date: 2018
*
* Version: 1.0
*
* Modify Recorder:
*
*/
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace ABTools
{
public class BuildAssetBundle
{
///
/// 打包生成所有AssetBundles
///
[MenuItem("AssetBundleTools/BuildAllAssetBundles")]
public static void BuildAllAB()
{
//(打包)AB的输出路径
string strABOutPathDIR = string.Empty;
strABOutPathDIR = PathTools.GetABOutPath();
if (!Directory.Exists(strABOutPathDIR))
{
Directory.CreateDirectory(strABOutPathDIR);
}
//打包生成
BuildPipeline.BuildAssetBundles(strABOutPathDIR,BuildAssetBundleOptions.None,BuildTarget.StandaloneWindows64);
}
}//Class_end
}
③删除路径中的所有资源(DeleteAssetBundle.cs)
/***
*
* Title: "AssetBundle工具包"项目
* 删除AssetBundle包文件
*
* Description:
* 功能:[本脚本的主要功能描述]
*
* Date: 2018
*
* Version: 1.0
*
* Modify Recorder:
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
namespace ABTools
{
public class DeleteAssetBundle
{
[MenuItem("AssetBundleTools/DeleteAllAssetBundles")]
public static void DeleteAllABs()
{
//(打包)AB的输出路径
string strNeedDeleteDIR = string.Empty;
strNeedDeleteDIR = PathTools.GetABOutPath();
if (!string.IsNullOrEmpty(strNeedDeleteDIR))
{
//参数true 表示可以删除非空目录。
Directory.Delete(strNeedDeleteDIR, true);
//去除删除警告
File.Delete(strNeedDeleteDIR + ".meta");
//刷新
AssetDatabase.Refresh();
}
}
}//Class_end
}
注意:涉及到路径及其常量定义专门用一个Helps文件夹存储
/***
*
* Title: "AssetBundle工具包"项目
* 项目常量、枚举、委托等定义
*
* Description:
* 注意:
* 不包含关于本项目路径信息,由其他帮助类单独管理
*
* Date: 2018
*
* Version: 1.0
*
* Modify Recorder:
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ABTools
{
/* 委托定义区 */
///
/// AssetBundle 加载完成
///
/// AssetBundle 名称
public delegate void DelLoadComplete(string abName);
/* 枚举类型定义区 */
/* 本项目常量定义区 */
public class ABDefine
{
public static string ASSETBUNDLE_MANIFEST = "AssetBundleManifest";
}//Class_end
}
/***
*
* Title: "AssetBundle工具包"项目
* 路径工具类
*
* Description:
* 功能:包含路径方法、路径常量
*
* Date: 2018
*
* Version: 1.0
*
* Modify Recorder:
*
*/
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace ABTools
{
public class PathTools
{
/* 路径常量 */
public const string AB_RESOURCES = "AB_Res";//AB_Resources
///
/// 获取AB资源(输入目录)
///
///
public static string GetABResourcesPath()
{
return Application.dataPath + "/"+ AB_RESOURCES;
}
///
/// 获取AB输出路径
/// 说明:
/// 由两部分构成
/// 1: 平台(PC/移动端)路径。
/// 2: 平台(PC/移动端)名称
///
///
public static string GetABOutPath()
{
return GetPlatformPath() + "/" + GetPlatformName();
}
///
/// 获取WWW协议路径
///
///
public static string GetWWWPath()
{
string strReturnWWWPath = string.Empty;
switch (Application.platform)
{
case RuntimePlatform.WindowsPlayer:
case RuntimePlatform.WindowsEditor:
strReturnWWWPath = "file://"+GetABOutPath();
break;
case RuntimePlatform.Android:
strReturnWWWPath = "jar:file://" + GetABOutPath();
break;
default:
break;
}
return strReturnWWWPath;
}
///
/// 获取平台路径
///
///
private static string GetPlatformPath()
{
string strReturnPlatformPath = string.Empty;
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;
}
///
/// 获取平台名称
///
///
public static string GetPlatformName()
{
string strReturnPlatformName = string.Empty;
switch (Application.platform)
{
case RuntimePlatform.WindowsPlayer:
case RuntimePlatform.WindowsEditor:
strReturnPlatformName = "Win";
break;
case RuntimePlatform.IPhonePlayer:
strReturnPlatformName = "Iphone";
break;
case RuntimePlatform.Android:
strReturnPlatformName = "Android";
break;
default:
break;
}
return strReturnPlatformName;
}
}//Class_end
}
《2》单一AssetBundle包的加载与管理
/***
*
* Title: "AssetBundle工具包"项目
* 第1层: AB资源加载类
*
* Description:
* 功能:管理指定AssetBundle 中的资源
* 1:加载AssetBundle
* 2: 卸载、释放AssetBundle 资源
* 3:查看当前AssetBundle内资源
*
* Date: 2018
*
* Version: 1.0
*
* Modify Recorder:
*
*/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ABTools
{
public class AssetLoader : System.IDisposable{
//当前AssetBundle
private AssetBundle _CurrentAssetBundle;
//容器键值对集合
private Hashtable _Ht;
///
/// 构造函数
///
/// 给定AssetBundle
public AssetLoader(AssetBundle abObj){
if (abObj!=null){
_CurrentAssetBundle = abObj;
_Ht = new Hashtable();
}
else
Debug.LogError(GetType()+ "/构造函数AssetLoader()/参数abObj==null!,请检查!");
}
///
/// 加载当前包中指定单个资源
/// 说明:带有资源缓冲功能
///
///
///
public UnityEngine.Object LoadAsset(string assetName,bool isCache=false){
return LoadResource(assetName, isCache);
}
///
/// 加载当前包资源,带缓冲技术
///
///
///
///
///
private T LoadResource(string assetName, bool isCache) where T:UnityEngine.Object{
if (_Ht.Contains(assetName)){
return _Ht[assetName] as T;
}
T tmpTResource = _CurrentAssetBundle.LoadAsset(assetName);
if (tmpTResource!=null && isCache)
{
_Ht.Add(assetName, tmpTResource);
}
else if(tmpTResource==null)
{
Debug.LogError(GetType() + "/LoadResource()/参数tmpTResource==null!,请检查!");
}
return tmpTResource;
}
///
/// 卸载资源
/// 功能: 卸载指定AssetBundle 包中指定资源
///
///
///
/// true: 表明卸载资源成功
///
public bool UnLoadAsset(UnityEngine.Object asset)
{
if (asset!=null)
{
Resources.UnloadAsset(asset);
return true;
}
Debug.LogError(GetType() + "/UnLoadAsset()/参数asset==null!,请检查!");
return false;
}
///
/// 释放当前AssetBundle资源(包)
///
public void Dispose()
{
_CurrentAssetBundle.Unload(false);
}
///
/// 释放当前AssetBundle资源(包),且卸载所有资源
///
public void DisposeALL()
{
_CurrentAssetBundle.Unload(true);
}
///
/// 查询当前AsserBundle 所有资源
///
///
public string[] RetrivalALLAssetName()
{
return _CurrentAssetBundle.GetAllAssetNames();
}
}//Class_end
}
/***
*
* Title: "AssetBundle工具包"项目
* 第2层:WWW加载AssetBundle
*
* Description:
* 功能:
*
* Date: 2018
*
* Version: 1.0
*
* Modify Recorder:
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ABTools
{
public class SingleABLoader:System.IDisposable
{
//引用类: 资源加载类
private AssetLoader _AssetLoader;
//委托: 加载完成
private DelLoadComplete _LoadCompleteHandle;
//AssetBundle名称
private string _ABName;
//AssetBundle 下载路径
private string _ABDownloadPath;
///
/// 构造函数
///
///
///
///
public SingleABLoader(string abName, DelLoadComplete loadComplete)
{
_ABName = abName;
_LoadCompleteHandle = loadComplete;
_ABDownloadPath = PathTools.GetWWWPath() + "/" + _ABName;
_AssetLoader = null;
}
///
/// 加载AssetBundle资源包
///
///
public IEnumerator LoadAssetBundle(){
using (WWW www = new WWW(_ABDownloadPath)){
yield return www;
if (www.progress >= 1){
//加载完成,获取AssetBundel实例
AssetBundle abObj = www.assetBundle;
if (abObj != null){
_AssetLoader = new AssetLoader(www.assetBundle);
if (_LoadCompleteHandle != null)
_LoadCompleteHandle(_ABName);
}
else{
Debug.LogError(GetType() + "/LoadAssetBundle()/WWW 下载出错,请检查 AssetBundle URL :" + _ABDownloadPath + " 错误信息: " + www.error);
}
}
}//using_end
}//Method_end
///
/// 加载(AB包内)资源
///
/// 资源名称
/// 是否使用缓存
///
public UnityEngine.Object LoadAsset(string assetName,bool isCache)
{
if (_AssetLoader != null)
{
return _AssetLoader.LoadAsset(assetName, isCache);
}
Debug.LogError(GetType() + "/LoadAsset()/参数 _AssetLoader ==null!,请检查!");
return null;
}
///
/// 卸载(AB包内)资源
///
/// 资源名称
public void UnLoadAsset(UnityEngine.Object asset)
{
if (_AssetLoader != null)
{
_AssetLoader.UnLoadAsset(asset);
}
else {
Debug.LogError(GetType() + "/UnLoadAsset()/参数 _AssetLoader ==null!,请检查!");
}
}
///
/// 释放(AB包)
///
public void Dispose()
{
if (_AssetLoader != null)
{
_AssetLoader.Dispose();
_AssetLoader = null;
}
else {
Debug.LogError(GetType() + "/Dispose()/参数 _AssetLoader ==null!,请检查!");
}
}
///
/// 释放当前AssetBundle资源(包),且卸载所有资源
///
public void DisposeALL()
{
if (_AssetLoader != null)
{
_AssetLoader.DisposeALL();
_AssetLoader = null;
}
else
{
Debug.LogError(GetType() + "/DisposeALL()/参数 _AssetLoader ==null!,请检查!");
}
}
///
/// 查询当前AssetBundle包内所有资源
///
///
public string[] RetrivalALLAssetName()
{
if (_AssetLoader != null)
{
return _AssetLoader.RetrivalALLAssetName();
}
Debug.LogError(GetType() + "/RetrivalALLAssetName()/参数 _AssetLoader ==null!,请检查!");
return null;
}
}//Class_end
}
三、进行测试这个核心的框架内容
/***
*
* Title: "AssetBundle工具包"项目
* "AssetBundle框架"内部阶段验证测试
*
*
* Description:
* 检测“SingleABLoader”类工作是否正常。
*
* Date: 20178
*
* Version: 1.0
*
* Modify Recorder:
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ABTools
{
public class SingleABLoader_TestClass:MonoBehaviour {
//引用类
SingleABLoader loaderObj = null;
//AB包名称
private string _ABName1 = "commonscenes/prefabs_1.ab";
//AB包内资源名称
private string _AssetName = "Cylinder";
private void Start(){
loaderObj = new SingleABLoader(_ABName1, LoadCompleate);
//加载AB资源包
StartCoroutine(loaderObj.LoadAssetBundle());
}
private void LoadCompleate(string abName){
Debug.Log("abName= " + abName + " 调用完毕!");
//加载资源
UnityEngine.Object tmpCloneObj= loaderObj.LoadAsset(_AssetName,false);
Instantiate(tmpCloneObj);
//显示AB包内资源
string[] strArray = loaderObj.RetrivalALLAssetName();
Debug.Log("包内所有资源");
foreach (string item in strArray)
{
Debug.Log(item);
}
}
private void Update()
{
//释放AB包
if (Input.GetKeyDown(KeyCode.A))
{
print("释放AB包");
loaderObj.Dispose();
}
}
}//Class_end
}
注意:本内容来自《Unity3D/2D游戏开发从0到1》第30章