您好,登錄后才能下訂單哦!
unity提供了assetbunlde機(jī)制,下面介紹一種方法將指定目錄下的所有文件打包成AssetBundle
先說明步驟,再上代碼。
步驟一、選擇要打包成assetbundle的目錄,本案例使用assetbundle_raw
步驟二、把要打包的資源或者目錄都放到assetbundle_raw目錄,資源可以是
prefab,png,fbx,font,audio,txt等。
步驟三、給assetbundle_raw目錄下所有的資源文件設(shè)置assetbundle名稱,如果是目錄,會遞歸。
名稱格式:資源名稱.資源后綴.unity3d,
如果有子目錄,則資源名稱為:子目錄/子目錄/.../子目錄/資源名稱.資源后綴.unity3d
在給這些資源設(shè)置assetbundle名稱的時(shí)候會把該資源所依賴的所有資源都設(shè)置上
assetbundle名稱。編輯器腳本已經(jīng)寫好,稍后附上,這一步你只需要點(diǎn)擊
Tools->AssetBundle->Set Asset Bundle Names就可以。
步驟四、選擇assetbundle輸出目錄,本案例使用StreamingAssets目錄。只所以要使用這個(gè)目錄,主要
是考慮到本地測試會比較方便,打包好的assetbundle肯定需要進(jìn)行一下測試,沒問題后再放到
資源服務(wù)器上,另外就是可以把StreamingAssets目錄作為本地資源測試服務(wù)器,還可以打包到
手機(jī)設(shè)備上進(jìn)行測試。操作:Tools->AssetBundle->Build Bundles。
步驟五、生成資源版本文件。步驟四執(zhí)行完畢后,assetbundle已經(jīng)生成,另外還會生成Manifest文件,
因?yàn)閍ssetbundle和平臺相關(guān),以android平臺為例,會生成android和android.manifest文件,
我們需要根據(jù)android文件獲取每個(gè)bundle的hash值,生成版本文件,用于熱更新。廢話不多
說,操作:Tools->AssetBundle->Generate Resource Version File。
至此,assetbundle打包完成。
特別需要注意:
放到assetbundle_raw目錄下的資源,互相之間不要有任何依賴關(guān)系,否則,在步驟三設(shè)置名稱的階段,被依賴的資源就會被設(shè)置兩次或者多次assetbundle名稱,這樣它只能使用最后設(shè)置的名稱。如果是這樣的話,在加載assetbundle的時(shí)候,就可能出現(xiàn)已經(jīng)加載的assetbundle被再次加載而報(bào)錯(cuò)。因?yàn)楸患虞d的assetbundle在被釋放前不能再次加載。所以,放到assetbundle_raw目錄下的資源,互相之間不要有任何依賴關(guān)系;放到assetbundle_raw目錄下的資源,互相之間不要有任何依賴關(guān)系;放到assetbundle_raw目錄下的資源,互相之間不要有任何依賴關(guān)系。重要的話說三遍。
另外需要注意的地方,在前面的文章中也提到了。
要打包的資源名稱不要有空格,空格在pc上支持,在android上不支持
名稱用小寫,下劃線,數(shù)字,盡量不要用大寫,因?yàn)閡nity打包成assetbundle后的資源名稱都是小寫
建議unity所有的資源命名的時(shí)候用小寫,下劃線,數(shù)字,盡量不要用大寫
如果用到一些插件,例如NGUI等,里面的資源,如,shader文件名稱中間是有空格的,它會作為依賴資源打包成assetbundle,這樣的手機(jī)上加載就可能出錯(cuò)。
編輯器代碼:
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
using System;
using System.Collections.Generic;
using System.Text;
using LitJson;
public class Builder : Editor
{
public static string sourcePath = Application.dataPath + "/assetbundle_raw";
const string AssetBundlesOutputPath = "Assets/StreamingAssets";
static string m_assetPath = Application.streamingAssetsPath;
static string assetTail = ".unity3d";
static bool canFinish = false;
//生成的bundle的相對路徑列表
static List<string> bundleRelativePaths = new List<string>();
[MenuItem("Tools/AssetBundle/Set Asset Bundle Names")]
public static void SetBundleNames()
{
ClearAssetBundlesName();
bundleRelativePaths.Clear();
Pack(sourcePath);
Debug.Log("Bundles 名稱設(shè)置完成");
}
[MenuItem("Tools/AssetBundle/Build Bundles")]
public static void BuildAssetBundle()
{
string outputPath = Path.Combine(AssetBundlesOutputPath, Platform.GetPlatformFolder(EditorUserBuildSettings.activeBuildTarget));
if (!Directory.Exists(outputPath))
{
Directory.CreateDirectory(outputPath);
}
//根據(jù)BuildSetting里面所激活的平臺進(jìn)行打包
BuildPipeline.BuildAssetBundles(outputPath, 0, EditorUserBuildSettings.activeBuildTarget);
Debug.Log("打包完成");
AssetDatabase.Refresh();
}
[MenuItem("Tools/AssetBundle/Generate Resource Version File")]
public static void GenerateVersionFile()
{
//生成配置文件
GenerateCfgFile();
EditorCoroutineRunner.StartEditorCoroutine(wait());
Debug.Log("成功生成資源版本文件");
AssetDatabase.Refresh();
}
static IEnumerator wait()
{
Debug.Log("wait");
while (!canFinish)
yield return null;
}
/// <summary>
/// 生成資源配置文件resource_version
/// </summary>
static void GenerateCfgFile()
{
Debug.Log("GenerateCfgFile");
getManifest();
}
/// <summary>
/// 清除之前設(shè)置過的AssetBundleName,避免產(chǎn)生不必要的資源也打包
/// 之前說過,只要設(shè)置了AssetBundleName的,都會進(jìn)行打包,不論在什么目錄下
/// </summary>
static void ClearAssetBundlesName()
{
string[] bundleNames = AssetDatabase.GetAllAssetBundleNames();
int length = bundleNames.Length;
Debug.Log(length);
string[] oldAssetBundleNames = new string[length];
for (int j = 0; j < bundleNames.Length; j++)
{
AssetDatabase.RemoveAssetBundleName(bundleNames[j], true);
}
length = AssetDatabase.GetAllAssetBundleNames().Length;
Debug.Log(length);
}
static void Pack(string source)
{
DirectoryInfo folder = new DirectoryInfo(source);
FileSystemInfo[] files = folder.GetFileSystemInfos();
int length = files.Length;
for (int i = 0; i < length; i++)
{
if (files[i] is DirectoryInfo)
{
Pack(files[i].FullName);
}
else
{
if (!files[i].Name.EndsWith(".meta"))
{
file(files[i].FullName);
Debug.Log("files[i].FullName is " + files[i].FullName);
}
}
}
}
static void file(string source)
{
string _source = Replace(source);
string _assetPath = "Assets" + _source.Substring(Application.dataPath.Length);
string _assetPath3 = _source.Substring(Application.dataPath.Length + 1);
Debug.Log("_assetPath is " + _assetPath);
Debug.Log("_assetPath3 is " + _assetPath3);
//在代碼中給資源設(shè)置AssetBundleName
AssetImporter assetImporter = AssetImporter.GetAtPath(_assetPath);
string assetName = _assetPath3.Substring(_assetPath3.IndexOf("/") + 1);
//assetName = assetName.Replace(Path.GetExtension(assetName), ".unity3d");
assetName = assetName + ".unity3d";
assetImporter.assetBundleName = assetName;
fileDependences(_assetPath);
Debug.Log("assetName is " + assetName);
//保存bundle的相對路徑
if (!bundleRelativePaths.Contains(assetName))
bundleRelativePaths.Add(assetName);
}
static string Replace(string s)
{
return s.Replace("\\", "/");
}
static void fileDependences(string path)
{
try
{
//string path = AssetDatabase.GetAssetPath(0);
Debug.Log("path is " + path);
string[] deps = AssetDatabase.GetDependencies(path);
int counter = 0;
for (int i = 0; i < deps.Length; i++)
{
if (deps[i].Equals(path) || deps[i].EndsWith(".cs"))
continue;
++counter;
}
if (counter == 0)
return;
for (int i = 0; i < deps.Length; i++)
{
Debug.Log("deps " + i + " is " + deps[i]);
if (deps[i].Equals(path) || deps[i].EndsWith(".cs"))
continue;
AssetImporter ai = AssetImporter.GetAtPath(deps[i]);
string assetName = deps[i] + ".unity3d";
assetName = assetName.Substring(assetName.IndexOf("/") + 1);
Debug.Log("assetName is " + assetName);
ai.assetBundleName = assetName;
//保存bundle的相對路徑
if (!bundleRelativePaths.Contains(assetName))
bundleRelativePaths.Add(assetName);
fileDependences(deps[i]);
}
}
catch (Exception error)
{
Debug.Log("error is " + error);
}
}
#region LoadAssetBundle
/// <summary>
/// 加載目標(biāo)資源
/// </summary>
/// <param name="name"></param>
/// <param name="callback"></param>
public static void LoadAssetBundle(string name, Action<UnityEngine.Object> callback)
{
name = name + assetTail;//eg:ui/panel.unity3d
Action<List<AssetBundle>> action = (depenceAssetBundles) =>
{
string realName = GetRuntimePlatform() + "/" + name;//eg:Windows/ui/panel.unity3d
Debug.Log("realName is " + realName);
LoadResReturnWWW(realName, (www) =>
{
int index = realName.LastIndexOf("/");
string assetName = realName.Substring(index + 1);
assetName = assetName.Replace(assetTail, "");
AssetBundle assetBundle = www.assetBundle;
UnityEngine.Object obj = assetBundle.LoadAsset(assetName);//LoadAsset(name),這個(gè)name沒有后綴,eg:panel
//卸載資源內(nèi)存
assetBundle.Unload(false);
for (int i = 0; i < depenceAssetBundles.Count; i++)
{
depenceAssetBundles[i].Unload(false);
}
//加載目標(biāo)資源完成的回調(diào)
callback(obj);
});
};
LoadDependenceAssets(name, action);
}
static void writeCfgFile(AssetBundleManifest manifest)
{
if (null == manifest)
return;
StringBuilder sb = new StringBuilder();
JsonWriter js = new JsonWriter(sb);
try
{
js.WriteObjectStart();
js.WritePropertyName("id");
js.Write(0);
js.WritePropertyName("version");
js.Write("1.0");
string platform = Platform.GetPlatformFolder(EditorUserBuildSettings.activeBuildTarget);
js.WritePropertyName("manifest");
js.Write(platform);
js.WritePropertyName("resource");
string[] bundleNames = AssetDatabase.GetAllAssetBundleNames();
js.WriteObjectStart();
foreach (string path in bundleNames)
{
Hash228 hash = manifest.GetAssetBundleHash(path);
//if (hash.isValid)
{
js.WritePropertyName(path);
js.Write(hash.ToString());
}
}
js.WriteObjectEnd();
js.WriteObjectEnd();
}
catch (Exception error)
{
Debug.Log("Write json error : " + error.Message);
}
string strVersion = sb.ToString().ToLower();
try
{
string platform = GetRuntimePlatform();
File.WriteAllText(AssetBundlesOutputPath + "/" + platform + "/resource_version", strVersion);
}
catch (Exception error)
{
Debug.Log("Write Cfg file error : " + error.Message);
}
canFinish = true;
}
/// <summary>
/// 獲取總manifest
/// </summary>
static void getManifest()
{
Action<AssetBundleManifest> dependenceAction = (manifest) =>
{
writeCfgFile(manifest);
};
LoadAssetBundleManifest(dependenceAction);
}
/// <summary>
/// 加載目標(biāo)資源的依賴資源
/// </summary>
/// <param name="targetAssetName"></param>
/// <param name="action"></param>
private static void LoadDependenceAssets(string targetAssetName, Action<List<AssetBundle>> action)
{
Debug.Log("要加載的目標(biāo)資源:" + targetAssetName);//ui/panel.unity3d
Action<AssetBundleManifest> dependenceAction = (manifest) =>
{
List<AssetBundle> depenceAssetBundles = new List<AssetBundle>();//用來存放加載出來的依賴資源的AssetBundle
string[] dependences = manifest.GetAllDependencies(targetAssetName);
//獲取hash值
Hash228 hash = manifest.GetAssetBundleHash(targetAssetName);
Debug.Log(targetAssetName + " hash is " + hash);
Debug.Log("依賴文件個(gè)數(shù):" + dependences.Length);
int length = dependences.Length;
int finishedCount = 0;
if (length == 0)
{
//沒有依賴
action(depenceAssetBundles);
}
else
{
//有依賴,加載所有依賴資源
for (int i = 0; i < length; i++)
{
string dependenceAssetName = dependences[i];
dependenceAssetName = GetRuntimePlatform() + "/" + dependenceAssetName;//eg:Windows/altas/heroiconatlas.unity3d
//加載,加到assetpool
LoadResReturnWWW(dependenceAssetName, (www) =>
{
int index = dependenceAssetName.LastIndexOf("/");
string assetName = dependenceAssetName.Substring(index + 1);
assetName = assetName.Replace(assetTail, "");
AssetBundle assetBundle = www.assetBundle;
UnityEngine.Object obj = assetBundle.LoadAsset(assetName);
//assetBundle.Unload(false);
depenceAssetBundles.Add(assetBundle);
finishedCount++;
if (finishedCount == length)
{
//依賴都加載完了
action(depenceAssetBundles);
}
});
}
}
};
LoadAssetBundleManifest(dependenceAction);
}
/// <summary>
/// 加載AssetBundleManifest
/// </summary>
/// <param name="action"></param>
private static void LoadAssetBundleManifest(Action<AssetBundleManifest> action)
{
string manifestName = GetRuntimePlatform();
Debug.Log("Application.platform is " + Application.platform);
manifestName = manifestName + "/" + manifestName;//eg:Windows/Windows
Debug.Log("manifestName is " + manifestName);
LoadResReturnWWW(manifestName, (www) =>
{
AssetBundle assetBundle = www.assetBundle;
UnityEngine.Object obj = assetBundle.LoadAsset("AssetBundleManifest");
assetBundle.Unload(false);
AssetBundleManifest manif = obj as AssetBundleManifest;
Debug.Log("(www) " + manif.name);
action(manif);
});
}
#endregion
#region ExcuteLoader
public static void LoadResReturnWWW(string name, Action<WWW> callback)
{
string path = "file://" + m_assetPath + "/" + name;
Debug.Log("m_assetPath is " + m_assetPath);
Debug.Log("name is " + name);
Debug.Log("加載:" + path);
EditorCoroutineRunner.StartEditorCoroutine(LoaderRes(path, callback));
}
static IEnumerator LoaderRes(string path, Action<WWW> callback)
{
WWW www = new WWW(path);
yield return www;
callback(www);
}
#endregion
#region Util
/// <summary>
/// 平臺對應(yīng)文件夾
/// </summary>
/// <returns></returns>
private static string GetRuntimePlatform()
{
string platform = "";
if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor)
{
//platform = "Windows";
platform = "android";
}
else if (Application.platform == RuntimePlatform.Android)
{
platform = "android";
}
else if (Application.platform == RuntimePlatform.IPhonePlayer)
{
platform = "ios";
}
else if (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.OSXEditor)
{
platform = "osx";
}
return platform;
}
#endregion
}
public class Platform
{
public static string GetPlatformFolder(BuildTarget target)
{
switch (target)
{
case BuildTarget.Android:
return "android";
case BuildTarget.iOS:
return "ios";
case BuildTarget.WebPlayer:
return "webplayer";
case BuildTarget.StandaloneWindows:
case BuildTarget.StandaloneWindows64:
return "windows";
case BuildTarget.StandaloneOSXIntel:
case BuildTarget.StandaloneOSXIntel64:
case BuildTarget.StandaloneOSXUniversal:
return "osx";
default:
return null;
}
}
}
用到了一個(gè)工具類,這是一個(gè)在編輯器下可運(yùn)行的協(xié)程類,我也是從網(wǎng)上找的
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
public static class EditorCoroutineRunner
{
private class EditorCoroutine : IEnumerator
{
private Stack<IEnumerator> executionStack;
public EditorCoroutine(IEnumerator iterator)
{
this.executionStack = new Stack<IEnumerator>();
this.executionStack.Push(iterator);
}
public bool MoveNext()
{
IEnumerator i = this.executionStack.Peek();
if (i.MoveNext())
{
object result = i.Current;
if (result != null && result is IEnumerator)
{
this.executionStack.Push((IEnumerator)result);
}
return true;
}
else
{
if (this.executionStack.Count > 1)
{
this.executionStack.Pop();
return true;
}
}
return false;
}
public void Reset()
{
throw new System.NotSupportedException("This Operation Is Not Supported.");
}
public object Current
{
get { return this.executionStack.Peek().Current; }
}
public bool Find(IEnumerator iterator)
{
return this.executionStack.Contains(iterator);
}
}
private static List<EditorCoroutine> editorCoroutineList;
private static List<IEnumerator> buffer;
public static IEnumerator StartEditorCoroutine(IEnumerator iterator)
{
if (editorCoroutineList == null)
{
// test
editorCoroutineList = new List<EditorCoroutine>();
}
if (buffer == null)
{
buffer = new List<IEnumerator>();
}
if (editorCoroutineList.Count == 0)
{
EditorApplication.update += Update;
}
// add iterator to buffer first
buffer.Add(iterator);
return iterator;
}
private static bool Find(IEnumerator iterator)
{
// If this iterator is already added
// Then ignore it this time
foreach (EditorCoroutine editorCoroutine in editorCoroutineList)
{
if (editorCoroutine.Find(iterator))
{
return true;
}
}
return false;
}
private static void Update()
{
// EditorCoroutine execution may append new iterators to buffer
// Therefore we should run EditorCoroutine first
editorCoroutineList.RemoveAll
(
coroutine => { return coroutine.MoveNext() == false; }
);
// If we have iterators in buffer
if (buffer.Count > 0)
{
foreach (IEnumerator iterator in buffer)
{
// If this iterators not exists
if (!Find(iterator))
{
// Added this as new EditorCoroutine
editorCoroutineList.Add(new EditorCoroutine(iterator));
}
}
// Clear buffer
buffer.Clear();
}
// If we have no running EditorCoroutine
// Stop calling update anymore
if (editorCoroutineList.Count == 0)
{
EditorApplication.update -= Update;
}
}
}
后面我會分享熱更新策略,用到了上面生成的資源版本文件。
以上是我用assetbundle打包的使用案例,本人能力有限,有錯(cuò)誤和不足的地方,還請不吝賜教,萬分感激!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。