484 lines
16 KiB
C#
484 lines
16 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
namespace VersionFlow.Runtime
|
|
{
|
|
public class BundleManager
|
|
{
|
|
/// <summary> Bundle名:ABEntity </summary>
|
|
private readonly Dictionary<string, ABEntity> m_entities = new Dictionary<string, ABEntity>();
|
|
/// <summary> 资源路径:ABEntity </summary>
|
|
private readonly Dictionary<string, ABEntity> m_assetPathToAB = new Dictionary<string, ABEntity>();
|
|
/// <summary> 组名:ABEntity </summary>
|
|
private readonly Dictionary<string, List<ABEntity>> m_groupToAB = new Dictionary<string, List<ABEntity>>();
|
|
/// <summary> Shader专属Bundle </summary>
|
|
private readonly List<ABEntity> m_shaderBundles = new List<ABEntity>();
|
|
/// <summary> 全部已加载shader </summary>
|
|
private readonly Dictionary<string, Shader> m_shaders = new Dictionary<string, Shader>();
|
|
/// <summary> shader变体收集器 </summary>
|
|
public ShaderVariantCollection SVC { get; private set; }
|
|
|
|
private readonly HashSet<LoadingChain> m_loadingChains = new HashSet<LoadingChain>();
|
|
private readonly Type m_gameObjectType = typeof(GameObject);
|
|
/// <summary> 可选Bundle组:ABEntity </summary>
|
|
private readonly Dictionary<string, OptionalBundleDownloader> m_optionGroupDownloader = new Dictionary<string, OptionalBundleDownloader>();
|
|
|
|
public string Version { get; set; } = "N/A";
|
|
|
|
internal HashSet<LoadingChain> GetLoadingChains() => m_loadingChains;
|
|
|
|
internal BundleManager(BundleManifest local, BundleManifest remote)
|
|
{
|
|
//初始化BundleManager
|
|
Stopwatch sw = Stopwatch.StartNew();
|
|
|
|
//创建Bundle对象和资源清单
|
|
foreach (var remoteBundle in remote.Bundles)
|
|
{
|
|
var localBundle = local.GetBundleByName(remoteBundle.BundleName);
|
|
var createBundle = new ABEntity(remoteBundle, localBundle != null && localBundle.Hash == remoteBundle.Hash);
|
|
m_entities[remoteBundle.BundleName] = createBundle;
|
|
|
|
//建立AssetPath到AB的缓存
|
|
foreach (var path in createBundle.Assets)
|
|
{
|
|
m_assetPathToAB[path] = createBundle;
|
|
}
|
|
//建立Group到AB的缓存
|
|
if (remoteBundle.Group != null)
|
|
{
|
|
if (!m_groupToAB.ContainsKey(remoteBundle.Group)) m_groupToAB[remoteBundle.Group] = new List<ABEntity>();
|
|
m_groupToAB[remoteBundle.Group].Add(createBundle);
|
|
|
|
if (remoteBundle.OptionBundle)
|
|
{
|
|
if (!m_optionGroupDownloader.ContainsKey(remoteBundle.Group)) m_optionGroupDownloader[remoteBundle.Group] = new OptionalBundleDownloader(remoteBundle.Group);
|
|
m_optionGroupDownloader[remoteBundle.Group].CollectAB(createBundle);
|
|
}
|
|
}
|
|
|
|
//记录shader专用bundle
|
|
if (createBundle.m_isShaderBundle)
|
|
m_shaderBundles.Add(createBundle);
|
|
}
|
|
//创建引用关系和资源清单
|
|
foreach (var remoteBundle in remote.Bundles)
|
|
{
|
|
var abEntity = m_entities[remoteBundle.BundleName];
|
|
foreach (var bundleName in remoteBundle.Dependencies)
|
|
{
|
|
var depAB = m_entities[bundleName];
|
|
abEntity.m_dependencyAB.Add(depAB);
|
|
}
|
|
}
|
|
|
|
//计算可选组下载状态
|
|
foreach (var optionDownloader in m_optionGroupDownloader.Values)
|
|
{
|
|
optionDownloader.CalcState();
|
|
}
|
|
|
|
sw.Stop();
|
|
|
|
VersionFlowX.Log($"创建资源清单耗时:{sw.Elapsed.Milliseconds}ms");
|
|
}
|
|
|
|
internal BundleManager() { }
|
|
|
|
internal ABEntity GetEntityByAssetPath(string assetPath)
|
|
{
|
|
m_assetPathToAB.TryGetValue(assetPath, out var bundle);
|
|
return bundle;
|
|
}
|
|
internal IEnumerator Internal_LoadAssetAsync<T>(string path) where T : UnityEngine.Object
|
|
{
|
|
if (!VersionFlowX.Setting.Enable)
|
|
{
|
|
#if UNITY_EDITOR
|
|
yield return UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
path = path.ToLower();
|
|
|
|
LoadingChain targetChain = GetLoadChain(path);
|
|
if (targetChain == null)
|
|
{
|
|
yield return null;
|
|
}
|
|
else
|
|
{
|
|
var itor = targetChain.LoadAssetAsync<T>(path);
|
|
while (itor.MoveNext()) yield return itor.Current;
|
|
|
|
yield return itor.Current;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal ABEntity GetABEntity(string abName)
|
|
{
|
|
m_entities.TryGetValue(abName, out var aBEntity);
|
|
return aBEntity;
|
|
}
|
|
|
|
internal LoadingChain GetLoadChain(string path)
|
|
{
|
|
m_assetPathToAB.TryGetValue(path, out var ab);
|
|
if (ab == null)
|
|
{
|
|
VersionFlowX.Error($"没有任何AssetBundle包含<color=cyan>{path}</color>");
|
|
return null;
|
|
}
|
|
|
|
LoadingChain targetChain = GetLoadChain(ab, false);
|
|
|
|
return targetChain;
|
|
}
|
|
|
|
internal LoadingChain GetLoadChain(ABEntity ab, bool shaderOnly)
|
|
{
|
|
LoadingChain targetChain = null;
|
|
foreach (var chain in m_loadingChains)
|
|
{
|
|
if (chain.m_topEntity == ab)
|
|
{
|
|
targetChain = chain;
|
|
break;
|
|
}
|
|
}
|
|
if (targetChain == null)
|
|
{
|
|
targetChain = LoadingChain.Create(ab, shaderOnly);
|
|
m_loadingChains.Add(targetChain);
|
|
}
|
|
|
|
return targetChain;
|
|
}
|
|
|
|
internal void LoadShaderBundles()
|
|
{
|
|
return;
|
|
VersionFlowX.Log("开始加载所有的ShaderBundle");
|
|
|
|
UnityResourceAPIOverride overrideAPI = new UnityResourceAPIOverride();
|
|
ResourcesAPI.overrideAPI = overrideAPI;
|
|
|
|
foreach (var shaderBundle in m_shaderBundles)
|
|
{
|
|
var chain = GetLoadChain(shaderBundle, true);
|
|
chain.LoadAllBundle();
|
|
|
|
foreach (var shaderPath in shaderBundle.Assets)
|
|
{
|
|
var shaderAsset = LoadAsset<UnityEngine.Object>(shaderPath);
|
|
if (shaderAsset == null) continue;
|
|
VersionFlowX.Log($"加载Shader:<color=cyan>{shaderAsset.name}</color>");
|
|
if (shaderAsset is Shader shader)
|
|
m_shaders[shader.name] = shader;
|
|
else if (shaderAsset is ShaderVariantCollection svc)
|
|
SVC = svc;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal Shader GetShader(string name)
|
|
{
|
|
m_shaders.TryGetValue(name, out var shader);
|
|
|
|
return shader;
|
|
}
|
|
|
|
public void LoadScene(string path)
|
|
{
|
|
if (!VersionFlowX.Setting.Enable)
|
|
{
|
|
#if UNITY_EDITOR
|
|
UnityEditor.SceneManagement.EditorSceneManager.LoadSceneInPlayMode(path, new LoadSceneParameters(LoadSceneMode.Single));
|
|
#else
|
|
throw new Exception("不可能!");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
//找到已加载场景的loadchain
|
|
LoadingChain hasSceneChain = null;
|
|
|
|
foreach (var chain in m_loadingChains)
|
|
{
|
|
if (chain.m_currentLoadScenePath != null)
|
|
{
|
|
hasSceneChain = chain;
|
|
break;
|
|
}
|
|
}
|
|
var targetChain = GetLoadChain(path);
|
|
if (targetChain == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
targetChain.LoadScene(path);
|
|
|
|
if (hasSceneChain != null) hasSceneChain.m_currentLoadScenePath = null;
|
|
}
|
|
}
|
|
|
|
public AsyncOperation LoadSceneAsync(string path)
|
|
{
|
|
if (!VersionFlowX.Setting.Enable)
|
|
{
|
|
#if UNITY_EDITOR
|
|
return UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(path, new LoadSceneParameters(LoadSceneMode.Single));
|
|
#else
|
|
throw new Exception("不可能!");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
//找到已加载场景的loadchain
|
|
LoadingChain hasSceneChain = null;
|
|
|
|
foreach (var chain in m_loadingChains)
|
|
{
|
|
if (chain.m_currentLoadScenePath != null)
|
|
{
|
|
hasSceneChain = chain;
|
|
break;
|
|
}
|
|
}
|
|
var targetChain = GetLoadChain(path);
|
|
if (targetChain == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var req = targetChain.LoadSceneAsync(path);
|
|
req.completed += (asyncop) =>
|
|
{
|
|
targetChain.m_currentLoadScenePath = path;
|
|
if (hasSceneChain != null) hasSceneChain.m_currentLoadScenePath = null;
|
|
};
|
|
|
|
return req;
|
|
}
|
|
}
|
|
|
|
public T LoadAsset<T>(string path) where T : UnityEngine.Object
|
|
{
|
|
if (!VersionFlowX.Setting.Enable)
|
|
{
|
|
#if UNITY_EDITOR
|
|
return UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path);
|
|
#endif
|
|
}
|
|
|
|
if (typeof(T) == m_gameObjectType)
|
|
{
|
|
VersionFlowX.Log("GameObject类型Asset不可以使用LoadAsset的方式,请使用Instantiate的方式");
|
|
return null;
|
|
}
|
|
|
|
path = path.ToLower();
|
|
LoadingChain targetChain = GetLoadChain(path);
|
|
if (targetChain == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var asset = targetChain.LoadAsset<T>(path);
|
|
|
|
return asset;
|
|
}
|
|
|
|
public GameObject Instantiate(string path, Transform parent = null)
|
|
{
|
|
if (!VersionFlowX.Setting.Enable)
|
|
{
|
|
#if UNITY_EDITOR
|
|
var go = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
|
if (go == null)
|
|
{
|
|
VersionFlowX.Error($"没有找到<color=cyan>{path}</color>的资源");
|
|
return null;
|
|
}
|
|
return GameObject.Instantiate(go, parent);
|
|
#endif
|
|
}
|
|
|
|
path = path.ToLower();
|
|
LoadingChain targetChain = GetLoadChain(path);
|
|
if (targetChain == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var prefabInstance = targetChain.Instantiate(path, parent);
|
|
|
|
return prefabInstance;
|
|
}
|
|
|
|
public InstantiateRequest InstantiateAsync(string path)
|
|
{
|
|
var request = new InstantiateRequest(Internal_InstantiateAsync(path));
|
|
request.Start();
|
|
|
|
return request;
|
|
}
|
|
|
|
internal IEnumerator Internal_InstantiateAsync(string path)
|
|
{
|
|
if (!VersionFlowX.Setting.Enable)
|
|
{
|
|
#if UNITY_EDITOR
|
|
var go = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
|
if (go == null)
|
|
{
|
|
VersionFlowX.Error($"没有找到<color=cyan>{path}</color>的资源");
|
|
yield break;
|
|
}
|
|
else yield return GameObject.Instantiate(go);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
path = path.ToLower();
|
|
LoadingChain targetChain = GetLoadChain(path);
|
|
if (targetChain == null)
|
|
{
|
|
VersionFlowX.Error($"没有找到<color=cyan>{path}</color>的资源");
|
|
yield break;
|
|
}
|
|
|
|
var itor = targetChain.InstantiateAsync(path);
|
|
while (itor.MoveNext()) yield return itor.Current;
|
|
|
|
yield return itor.Current;
|
|
}
|
|
}
|
|
|
|
public LoadAssetRequest<T> LoadAssetAsync<T>(string path) where T : UnityEngine.Object
|
|
{
|
|
if (typeof(T) == m_gameObjectType)
|
|
{
|
|
VersionFlowX.Error("GameObject类型Asset不可以使用LoadAsset的方式,请使用Instantiate的方式");
|
|
return null;
|
|
}
|
|
|
|
var request = new LoadAssetRequest<T>(Internal_LoadAssetAsync<T>(path));
|
|
request.Start();
|
|
|
|
return request;
|
|
}
|
|
|
|
public void ReleaseAsset<T>(ref T asset) where T : UnityEngine.Object
|
|
{
|
|
if (!VersionFlowX.Setting.Enable)
|
|
{
|
|
asset = null;
|
|
}
|
|
else
|
|
{
|
|
foreach (var chain in m_loadingChains)
|
|
{
|
|
chain.UnLoadAsset(asset);
|
|
}
|
|
|
|
asset = null;
|
|
|
|
if (VersionFlowX.Setting.AutoReleaseBundle)
|
|
TryUnLoadBundles();
|
|
}
|
|
}
|
|
public void ReleaseAsset<T>(IList<T> assetList) where T : UnityEngine.Object
|
|
{
|
|
foreach (var item in assetList)
|
|
{
|
|
var temp = item;
|
|
ReleaseAsset(ref temp);
|
|
}
|
|
|
|
assetList.Clear();
|
|
}
|
|
|
|
HashSet<LoadingChain> m_needRemoveChains = new HashSet<LoadingChain>();
|
|
/// <summary>
|
|
/// 检查已加载的Bundle的引用计数为0并且没有任何依赖bundle被加载
|
|
/// 然后移除它
|
|
/// </summary>
|
|
public void TryUnLoadBundles()
|
|
{
|
|
if (!VersionFlowX.Setting.Enable)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_needRemoveChains.Clear();
|
|
|
|
foreach (var chain in m_loadingChains)
|
|
{
|
|
if (chain.TryDispose())
|
|
m_needRemoveChains.Add(chain);
|
|
}
|
|
|
|
m_loadingChains.ExceptWith(m_needRemoveChains);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 指定GroupName,获得可选Bundle组的下载器,无论该组是否已完成下载,或是正在下载,或没有下载
|
|
/// </summary>
|
|
/// <param name="groupName">在builder中配置的Group名称,并且该Group需要设置为OptionBundle</param>
|
|
/// <returns></returns>
|
|
public OptionalBundleDownloader GetDownloaderByGroup(string groupName)
|
|
{
|
|
m_optionGroupDownloader.TryGetValue(groupName, out var downloader);
|
|
return downloader;
|
|
}
|
|
|
|
/// <summary> 获得所有设置为OptionBundle的组的下载器,无论该组是否已完成下载,或是正在下载,或没有下载 </summary>
|
|
/// <returns></returns>
|
|
public List<OptionalBundleDownloader> GetAllOptionBundleDownloader()
|
|
{
|
|
return m_optionGroupDownloader.Values.ToList();
|
|
}
|
|
}
|
|
|
|
|
|
[Serializable]
|
|
public class Bundle
|
|
{
|
|
public string BundleName;
|
|
public List<string> Assets;
|
|
public List<string> Dependencies;
|
|
public string Hash;
|
|
public long Size;
|
|
/// <summary> 所属Group </summary>
|
|
public string Group;
|
|
/// <summary> 可选资源包 </summary>
|
|
public bool OptionBundle;
|
|
/// <summary> Shader专属bundle </summary>
|
|
public bool ShaderBundle;
|
|
/// <summary> 随包资源包 </summary>
|
|
public bool InstallBundle;
|
|
|
|
internal void CopyFrom(Bundle bundle)
|
|
{
|
|
BundleName = bundle.BundleName;
|
|
Assets = bundle.Assets;
|
|
Dependencies = bundle.Dependencies;
|
|
Hash = bundle.Hash;
|
|
Size = bundle.Size;
|
|
OptionBundle = bundle.OptionBundle;
|
|
Group = bundle.Group;
|
|
ShaderBundle = bundle.ShaderBundle;
|
|
InstallBundle = bundle.InstallBundle;
|
|
}
|
|
}
|
|
}
|