700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > unity 打包AssetBundle

unity 打包AssetBundle

时间:2020-02-02 14:17:37

相关推荐

unity 打包AssetBundle

unity提供了assetbunlde机制,下面介绍一种方法将指定目录下的所有文件打包成AssetBundle

先说明步骤,再上代码。

步骤一、选择要打包成assetbundle的目录,本案例使用assetbundle_raw

步骤二、把要打包的资源或者目录都放到assetbundle_raw目录,资源可以是

prefab,png,fbx,font,audio,txt等。

步骤三、给assetbundle_raw目录下所有的资源文件设置assetbundle名称,如果是目录,会递归。

名称格式:资源名称.资源后缀.unity3d,

如果有子目录,则资源名称为:子目录/子目录/.../子目录/资源名称.资源后缀.unity3d

在给这些资源设置assetbundle名称的时候会把该资源所依赖的所有资源都设置上

assetbundle名称。编辑器脚本已经写好,稍后附上,这一步你只需要点击

Tools->AssetBundle->Set Asset Bundle Names就可以。

步骤四、选择assetbundle输出目录,本案例使用StreamingAssets目录。只所以要使用这个目录,主要

是考虑到本地测试会比较方便,打包好的assetbundle肯定需要进行一下测试,没问题后再放到

资源服务器上,另外就是可以把StreamingAssets目录作为本地资源测试服务器,还可以打包到

手机设备上进行测试。操作:Tools->AssetBundle->Build Bundles。

步骤五、生成资源版本文件。步骤四执行完毕后,assetbundle已经生成,另外还会生成Manifest文件,

因为assetbundle和平台相关,以android平台为例,会生成android和android.manifest文件,

我们需要根据android文件获取每个bundle的hash值,生成版本文件,用于热更新。废话不多

说,操作:Tools->AssetBundle->Generate Resource Version File。

至此,assetbundle打包完成。

特别需要注意:

放到assetbundle_raw目录下的资源,互相之间不要有任何依赖关系,否则,在步骤三设置名称的阶段,被依赖的资源就会被设置两次或者多次assetbundle名称,这样它只能使用最后设置的名称。如果是这样的话,在加载assetbundle的时候,就可能出现已经加载的assetbundle被再次加载而报错。因为被加载的assetbundle在被释放前不能再次加载。所以,放到assetbundle_raw目录下的资源,互相之间不要有任何依赖关系;放到assetbundle_raw目录下的资源,互相之间不要有任何依赖关系;放到assetbundle_raw目录下的资源,互相之间不要有任何依赖关系。重要的话说三遍。

另外需要注意的地方,在前面的文章中也提到了。

要打包的资源名称不要有空格,空格在pc上支持,在android上不支持

名称用小写,下划线,数字,尽量不要用大写,因为unity打包成assetbundle后的资源名称都是小写

建议unity所有的资源命名的时候用小写,下划线,数字,尽量不要用大写

如果用到一些插件,例如NGUI等,里面的资源,如,shader文件名称中间是有空格的,它会作为依赖资源打包成assetbundle,这样的手机上加载就可能出错。

编辑器代码:

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 名称设置完成");

}

[MenuItem("Tools/AssetBundle/Build Bundles")]

public static void BuildAssetBundle()

{

string outputPath = bine(AssetBundlesOutputPath, Platform.GetPlatformFolder(EditorUserBuildSettings.activeBuildTarget));

if (!Directory.Exists(outputPath))

{

Directory.CreateDirectory(outputPath);

}

//根据BuildSetting里面所激活的平台进行打包

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>

/// 清除之前设置过的AssetBundleName,避免产生不必要的资源也打包

/// 之前说过,只要设置了AssetBundleName的,都会进行打包,不论在什么目录下

/// </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 _assetPath2 = _source.Substring(Application.dataPath.Length + 1);

Debug.Log("_assetPath is " + _assetPath);

Debug.Log("_assetPath2 is " + _assetPath2);

//在代码中给资源设置AssetBundleName

AssetImporter assetImporter = AssetImporter.GetAtPath(_assetPath);

string assetName = _assetPath2.Substring(_assetPath2.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>

/// 加载目标资源

/// </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),这个name没有后缀,eg:panel

//卸载资源内存

assetBundle.Unload(false);

for (int i = 0; i < depenceAssetBundles.Count; i++)

{

depenceAssetBundles[i].Unload(false);

}

//加载目标资源完成的回调

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)

{

Hash128 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>

/// 加载目标资源的依赖资源

/// </summary>

/// <param name="targetAssetName"></param>

/// <param name="action"></param>

private static void LoadDependenceAssets(string targetAssetName, Action<List<AssetBundle>> action)

{

Debug.Log("要加载的目标资源:" + targetAssetName);//ui/panel.unity3d

Action<AssetBundleManifest> dependenceAction = (manifest) =>

{

List<AssetBundle> depenceAssetBundles = new List<AssetBundle>();//用来存放加载出来的依赖资源的AssetBundle

string[] dependences = manifest.GetAllDependencies(targetAssetName);

//获取hash值

Hash128 hash = manifest.GetAssetBundleHash(targetAssetName);

Debug.Log(targetAssetName + " hash is " + hash);

Debug.Log("依赖文件个数:" + 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>

/// 平台对应文件夹

/// </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;

}

}

}

用到了一个工具类,这是一个在编辑器下可运行的协程类,我也是从网上找的

using UnityEngine;

using UnityEditor;

using System.Collections;

using System.Collections.Generic;

using pilerServices;

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打包的使用案例,本人能力有限,有错误和不足的地方,还请不吝赐教,万分感激!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。