起因:我们在打Bundle 的时候出现了同一个AB里面有相同文件名字的文件(虽然路径不同),具体报错如下:
14:22:18 Building AssetBundle failed because hash collision was detected in the deterministic id generation. 14:22:18 Conflict happened between Asset "Assets/Game/GameAsset/RofConfig/HMT/RofBonus.json" and "Assets/Game/GameAsset/RofConfig/SEA/RofBonus.json
上面报错的两个文件可以看到是文件名相同,文件夹相同
我们代码里面打包的代码如下,会发现我们指定了变体Variant值,
assetBundleBuilds.Add(new AssetBundleBuild()
{
assetBundleName = "Data",
assetBundleVariant = "Summer",
......
同时打包的参数是用的Hash值
var options =
BuildAssetBundleOptions.DeterministicAssetBundle |
BuildAssetBundleOptions.StrictMode |
BuildAssetBundleOptions.ChunkBasedCompression;
Unity API 解释,这里的Hash值居然是根据文件名字生成的,而不是meta 里面的hash值(开始在这里也掉坑了),真奇葩的设定
//
// 摘要:
// Builds an asset bundle using a hash for the id of the object stored in the asset
// bundle.
DeterministicAssetBundle = 16,
所以就会出现开始的虽然在不同的文件夹下也会报错提示Hash冲突的情况了。
这里研究下变体的运行原理
打包,场景下依赖其中的某个变体Data.Spring ,如下有三个变体 Data.Spring ; Data.Summer; Data.Fall ,打包代码如下
#if UNITY_EDITOR
using System.IO;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class Build
{
[MenuItem("Tools/Build")]
static void PlatformBuild()
{
Caching.ClearCache();
var assetBundleBuilds = new List<AssetBundleBuild>();
assetBundleBuilds.Add(new AssetBundleBuild()
{
assetBundleName = "Level",
assetBundleVariant = "",
assetNames = new string[] {
"Assets/Level.unity",
},
});
assetBundleBuilds.Add(new AssetBundleBuild()
{
assetBundleName = "Data1",
assetBundleVariant = "Spring",
assetNames = new string[] {
"Assets/Data/Spring/Main.jpg",
"Assets/Data/Spring/Text.prefab",
"Assets/Data/Spring/String.txt",
"Assets/Data/Spring/Color.asset",
},
});
assetBundleBuilds.Add(new AssetBundleBuild()
{
assetBundleName = "Data",
assetBundleVariant = "Fall",
assetNames = new string[] {
"Assets/Data/Fall/Text.prefab",
"Assets/Data/Fall/Main.jpg",
"Assets/Data/Fall/String.txt",
"Assets/Data/Fall/Color.asset",
},
});
assetBundleBuilds.Add(new AssetBundleBuild()
{
assetBundleName = "Data",
assetBundleVariant = "Summer",
assetNames = new string[] {
"Assets/Data/Summer/Main.jpg",
"Assets/Data/Summer/Text.prefab",
"Assets/Data/Summer/String.txt",
"Assets/Data/Summer/Color.asset",
},
});
var path = Path.GetFullPath(Application.dataPath + "/../Build");
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
BuildPipeline.BuildAssetBundles(path,
assetBundleBuilds.ToArray(),
BuildAssetBundleOptions.ForceRebuildAssetBundle,
BuildTarget.StandaloneWindows64);
if (!UnityEditorInternal.InternalEditorUtility.inBatchMode)
{
System.Diagnostics.Process.Start(path);
}
}
}
#endif
加载,通过Theme切换不同场景变体,会发现我们怎么切换都能加载到争取的资源
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Loader : MonoBehaviour {
public enum Theme
{
Spring,
Fuck,
Summer
}
public Theme loadTheme = Theme.Spring;
IEnumerator Start()
{
var root = Application.dataPath + "/../Build";
var themePath = root + "/data." + loadTheme.ToString().ToLower();
var levelPath = root + "/level";
var ab = AssetBundle.LoadFromFile(themePath);
foreach (var assetName in ab.GetAllAssetNames())
{
Debug.LogFormat("Assets: {0}", assetName);
}
AssetBundle.LoadFromFile(levelPath);
yield return SceneManager.LoadSceneAsync("Level", LoadSceneMode.Additive);
}
}
我们截取打包的AB进行分析 level.manifest 依赖关系如下,可以看到Level 依赖的是data.spring 这个AB
Assets:
- Assets/Level.unity
Dependencies:
- C:/Users/Admin/Desktop/Unity-AssetBundleVariantsExample-master/Build/data.spring
当我们切换到其他枚举去加载的时候同样可以很正确的加载到资源,那么引擎是怎么能找到这个正确的Data.Fall 或者 Data.Summer 的AB的呢?** 答案是变体的内部唯一值是相同的 **
所以在我们加载的时候就能正确找到对应的资源了