打包:
首先,新版本的Unity提供的打包工具 AssetBundlesBrowser 很方便,极力推荐,版本高的话可以在 Package 中找到。使用方法的话,可以参考 这篇文章 。
除此之外,手写 Editor 代码也是一个很不错的选择。代码一定要放在 Editor 文件下!主要的过程其实很简单,先通过 AssetImporter 修改一下 assetBundleName(与assetBundleVariant),然后就可以调用 BuildPipeline.BuildAssetBundles 函数进行打包操作了。
BuildPipeline.BuildAssetBundles 函数的具体用法还是参考 官网文档, 这里主要提一下第二个参数 assetBundleOptions[打包方式] 中的几个常用的选择:
None = 0 : // 对ab不进行数据压缩。输入的ab包会很大,不过输入和加载会很快
ChunkBasedCompression = 256: // 使用lz4的格式压缩ab,ab会在加载资源时才进行解压。默认的压缩格式是lzma,它会使用ab在下立即解压。
依赖关系 .manifest 文件是打包中比较让人头疼的问题,当一个资源和 与其相关联的资源 没有被打包在同一个包体中时,该资源就会对 关联资源 产生依赖关系,当我们没有加载 有依赖关系的包体时, 就会产生类似材质丢失之类的问题。这篇文章 中对依赖关系解释的很清楚。这时候,上文中提到的 AssetBundlesBrowser 就展现优势了,当你加入某一资源时,它会很清楚的提示出该资源的依赖关系,如上图中 AssetBundlesBrowser 面板上显示的,以防止出现上面提到的问题以及 重复打包造成资源浪费的问题。
下面是一个简单的Editor 打包代码:
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
/// <summary>
/// 打包 AssetBundle
/// </summary>
public class AssetBundleEditor : MonoBehaviour
{
[MenuItem("Tools/BuildAssetBundle")]
static void BuildAssetBundle()
{
string outputPath = Application.dataPath + "/AssetBundles";
if (!Directory.Exists(outputPath))
{
Directory.CreateDirectory(outputPath);
}
// 强制删除所有AssetBundle名称
string[] abNames = AssetDatabase.GetAllAssetBundleNames();
for (int i = 0; i < abNames.Length; i++)
{
AssetDatabase.RemoveAssetBundleName(abNames[i], true);
}
// 删除之前的ab文件
FileInfo[] fs = new DirectoryInfo(outputPath).GetFiles("*", SearchOption.AllDirectories);
foreach (var f in fs)
{
f.Delete();
}
foreach (UnityEngine.Object selected in Selection.objects)
{
//返回所有对象相对于工程目录的存储路径如 Assets/Scenes/Main.unity, 减去 Assets 6个字节
FindMoudles(AssetDatabase.GetAssetPath(selected).Substring(6));
}
BuildPipeline.BuildAssetBundles(outputPath, BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget);
AssetDatabase.Refresh();
print("BuildAssetBundles Complete");
}
/// <summary>
/// 按模块组织
/// </summary>
/// <param name="pathSuf"></param>
private static void FindMoudles(string pathSuf)
{
string path = Application.dataPath + pathSuf;
// 通过路径拿到文件夹
DirectoryInfo dir = new DirectoryInfo(path);
if (!dir.Exists) return;
AssetImport(dir);
// 对该文件夹的下一层文件夹进行标记
DirectoryInfo[] subDirs = dir.GetDirectories();
foreach (DirectoryInfo dInfo in subDirs)
{
string itemPath = path + "/" + dInfo.Name;
DirectoryInfo subDir = new DirectoryInfo(itemPath);
if (subDir.Exists)
{
AssetImport(subDir);
}
}
}
/// <summary>
/// 标记某个文件夹下所有文件为文件夹名
/// </summary>
/// <param name="dirInfo">文件夹名</param>
private static void AssetImport(DirectoryInfo dirInfo)
{
FileInfo[] files = dirInfo.GetFiles("*", SearchOption.AllDirectories); // 取出所有文件
foreach (FileInfo fileInfo in files)
{
if (fileInfo.Name.EndsWith(".mata")) continue;
string filePath = fileInfo.FullName.Substring(fileInfo.FullName.IndexOf("Assets", StringComparison.Ordinal)); // 获取 "Assets"目录起的 文件名, 可不用转 "\\"
AssetImporter importer = AssetImporter.GetAtPath(filePath); // 拿到该文件的 AssetImporter
if (importer && importer.assetBundleName != dirInfo.Name)
{
importer.assetBundleName = dirInfo.Name;
importer.assetBundleVariant = "ab";
importer.SaveAndReimport();
}
}
}
}
加载资源:
资源加载的方式挺多的,除去废弃的 WWW 加载,这边提供4种加载方式:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Networking;
/// <summary>
/// AssetBundle 资源加载
/// </summary>
public class AssetLoad : MonoBehaviour
{
/// <summary>
/// 资源在项目中的名字
/// </summary>
const string AssetName = "Cube";
/// <summary>
/// AB包名
/// </summary>
const string AssetBundleName = "prefabs.ab";
/// <summary>
/// AB包的 Assets 路径
/// </summary>
const string Path = "Assets/AssetBundles/prefabs.ab";
/// <summary>
/// Manifest 路径
/// </summary>
const string ManifestPath = "Assets/AssetBundles/AssetBundles";
/// <summary>
/// 服务器路径
/// </summary>
const string URI = @"file:///D:/Study/GitHubProjects/trunk/Might_Be_Useful/Assets/AssetBundles/prefabs.ab";
// const string uri = @"http://localhost/AssetBundles";
/// <summary>
/// 1、同步加载
/// </summary>
public void LoadAssetsSync()
{
AssetBundle bundle = AssetBundle.LoadFromFile(Path);
//AssetBundle bundle = AssetBundle.LoadFromMemory(File.ReadAllBytes(path));
if (!bundle) return;
UnityEngine.Object obj = bundle.LoadAsset(AssetName) as GameObject;
//UnityEngine.Object[] objs = bundle.LoadAllAssets() as GameObject[];
Instantiate(obj);
bundle.Unload(false);
}
/// <summary>
/// 2、异步加载
/// </summary>
/// <returns></returns>
public IEnumerator LoadAssetsAsync()
{
// 1、LoadFromFile 获取AssetBundle
AssetBundleCreateRequest createRequest = AssetBundle.LoadFromFileAsync(Path);
yield return createRequest;
AssetBundle bundle = createRequest.assetBundle;
// 2、LoadFromMemoryAsync 获取AssetBundle
//AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
//yield return createRequest;
//AssetBundle bundle = createRequest.assetBundle;
// LoadAssetAsync 读取资源
AssetBundleRequest request = bundle.LoadAssetAsync(AssetName, typeof(GameObject));
//AssetBundleRequest requests = bundle.LoadAllAssetsAsync();
GameObject obj = request.asset as GameObject;
Instantiate(obj);
bundle.Unload(false);
}
/// <summary>
/// 3、从文件夹里通过依赖关系加载
/// </summary>
public void LoadWithManifest()
{
// 加载那个打包时额外打出来的总包,不需要后缀名 .manifest
AssetBundle manifestBundle = AssetBundle.LoadFromFile(ManifestPath);
// AssetBundleManifest不是某个文件的名字,是固定的
AssetBundleManifest manifest = manifestBundle.LoadAsset("AssetBundleManifest") as AssetBundleManifest;
// 获取 AssetBundleName 资源包所依赖的资源包的名字
string[] deps = manifest.GetAllDependencies(AssetBundleName);
List<AssetBundle> depList = new List<AssetBundle>(); // 保存该资源所依赖的AB包
for (int i = 0; i < deps.Length; ++i)
{
AssetBundle ab = AssetBundle.LoadFromFile("Assets/AssetBundles/" + deps[i]);
//UnityEngine.Object obj = ab.LoadAsset(assetName); // 具体的资源操作
print("AB包名: " + ab.name);
depList.Add(ab);
}
for (int i = 0; i < depList.Count; ++i)
{
depList[i].Unload(false);
}
manifestBundle.Unload(true);
}
/// <summary>
/// 4、网络加载
/// </summary>
/// <returns></returns>
public IEnumerator LoadWithUnityWebRequest()
{
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(URI); // 通过 URI 获取 UnityWebRequest
yield return request.SendWebRequest();
//AssetBundle ab8 = ((DownloadHandlerAssetBundle)request.downloadHandler).assetBundle;
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request); // 通过 request 获取 AB
// 使用里面的资源
UnityEngine.Object obj = bundle.LoadAsset(AssetName);
print(AssetName + " --- " + obj.name);
}
}