Optimize the scene unload logic.

优化场景卸载逻辑,在加载新的主场景的时候自动卸载已经加载的所有场景。
pull/4/head
hevinci 2022-04-21 21:11:11 +08:00
parent 20d2c517b2
commit f3ab8f63e7
7 changed files with 112 additions and 58 deletions

View File

@ -10,6 +10,7 @@ namespace YooAsset
{ {
private static readonly List<AssetBundleLoaderBase> _loaders = new List<AssetBundleLoaderBase>(1000); private static readonly List<AssetBundleLoaderBase> _loaders = new List<AssetBundleLoaderBase>(1000);
private static readonly List<ProviderBase> _providers = new List<ProviderBase>(1000); private static readonly List<ProviderBase> _providers = new List<ProviderBase>(1000);
private static readonly Dictionary<string, SceneOperationHandle> _sceneHandles = new Dictionary<string, SceneOperationHandle>(100);
/// <summary> /// <summary>
/// 在编辑器下模拟运行 /// 在编辑器下模拟运行
@ -132,6 +133,14 @@ namespace YooAsset
/// </summary> /// </summary>
public static SceneOperationHandle LoadSceneAsync(string scenePath, LoadSceneMode sceneMode, bool activateOnLoad, int priority) public static SceneOperationHandle LoadSceneAsync(string scenePath, LoadSceneMode sceneMode, bool activateOnLoad, int priority)
{ {
// 注意:场景句柄永远保持唯一
if (_sceneHandles.ContainsKey(scenePath))
return _sceneHandles[scenePath];
// 如果加载的是主场景,则卸载所有缓存的场景
if (sceneMode == LoadSceneMode.Single)
UnloadAllScene();
ProviderBase provider = TryGetProvider(scenePath); ProviderBase provider = TryGetProvider(scenePath);
if (provider == null) if (provider == null)
{ {
@ -141,7 +150,9 @@ namespace YooAsset
provider = new BundledSceneProvider(scenePath, sceneMode, activateOnLoad, priority); provider = new BundledSceneProvider(scenePath, sceneMode, activateOnLoad, priority);
_providers.Add(provider); _providers.Add(provider);
} }
return provider.CreateHandle() as SceneOperationHandle; var handle = provider.CreateHandle() as SceneOperationHandle;
_sceneHandles.Add(scenePath, handle);
return handle;
} }
/// <summary> /// <summary>
@ -179,6 +190,48 @@ namespace YooAsset
} }
internal static void UnloadSubScene(ProviderBase provider)
{
string scenePath = provider.AssetPath;
if (_sceneHandles.ContainsKey(scenePath) == false)
throw new Exception("Should never get here !");
// 释放子场景句柄
_sceneHandles[scenePath].ReleaseInternal();
_sceneHandles.Remove(scenePath);
// 卸载未被使用的资源(包括场景)
AssetSystem.UnloadUnusedAssets();
// 检验子场景是否销毁
if (provider.IsDestroyed == false)
{
throw new Exception("Should never get here !");
}
}
internal static void UnloadAllScene()
{
// 释放所有场景句柄
foreach (var valuePair in _sceneHandles)
{
valuePair.Value.ReleaseInternal();
}
_sceneHandles.Clear();
// 卸载未被使用的资源(包括场景)
AssetSystem.UnloadUnusedAssets();
// 检验所有场景是否销毁
foreach (var provider in _providers)
{
if (provider.IsSceneProvider())
{
if (provider.IsDestroyed == false)
throw new Exception("Should never get here !");
}
}
}
internal static AssetBundleLoaderBase CreateOwnerAssetBundleLoader(string assetPath) internal static AssetBundleLoaderBase CreateOwnerAssetBundleLoader(string assetPath)
{ {
string bundleName = BundleServices.GetBundleName(assetPath); string bundleName = BundleServices.GetBundleName(assetPath);
@ -254,6 +307,7 @@ namespace YooAsset
return provider; return provider;
} }
#region 调试专属方法 #region 调试专属方法
internal static void GetDebugReport(DebugReport report) internal static void GetDebugReport(DebugReport report)
{ {

View File

@ -2,7 +2,7 @@
namespace YooAsset namespace YooAsset
{ {
public class AssetOperationHandle : OperationHandleBase public sealed class AssetOperationHandle : OperationHandleBase
{ {
private System.Action<AssetOperationHandle> _callback; private System.Action<AssetOperationHandle> _callback;

View File

@ -4,11 +4,13 @@ namespace YooAsset
{ {
public abstract class OperationHandleBase : IEnumerator public abstract class OperationHandleBase : IEnumerator
{ {
private readonly string _cachedAssetPath;
internal ProviderBase _provider { private set; get; } internal ProviderBase _provider { private set; get; }
internal OperationHandleBase(ProviderBase provider) internal OperationHandleBase(ProviderBase provider)
{ {
_provider = provider; _provider = provider;
_cachedAssetPath = provider.AssetPath;
} }
internal abstract void InvokeCallback(); internal abstract void InvokeCallback();
@ -63,7 +65,18 @@ namespace YooAsset
{ {
get get
{ {
return _provider != null && _provider.IsDestroyed == false; if (_provider != null && _provider.IsDestroyed == false)
{
return true;
}
else
{
if (_provider == null)
YooLogger.Warning($"Operation handle is released : {_cachedAssetPath}");
else if (_provider.IsDestroyed)
YooLogger.Warning($"Provider is destroyed : {_cachedAssetPath}");
return false;
}
} }
} }

View File

@ -69,10 +69,35 @@ namespace YooAsset
} }
/// <summary> /// <summary>
/// 异步卸载场景 /// 是否为主场景
/// </summary>
public bool IsMainScene()
{
if (IsValid == false)
return false;
if (_provider is DatabaseSceneProvider)
{
var temp = _provider as DatabaseSceneProvider;
return temp.SceneMode == LoadSceneMode.Single;
}
else if (_provider is BundledSceneProvider)
{
var temp = _provider as BundledSceneProvider;
return temp.SceneMode == LoadSceneMode.Single;
}
else
{
throw new System.NotImplementedException();
}
}
/// <summary>
/// 异步卸载子场景
/// </summary> /// </summary>
public UnloadSceneOperation UnloadAsync() public UnloadSceneOperation UnloadAsync()
{ {
// 如果句柄无效
if (IsValid == false) if (IsValid == false)
{ {
string error = $"{nameof(SceneOperationHandle)} is invalid."; string error = $"{nameof(SceneOperationHandle)} is invalid.";
@ -81,54 +106,23 @@ namespace YooAsset
return operation; return operation;
} }
ProviderBase provider = _provider; // 如果是主场景
if (IsMainScene())
// 释放场景句柄
ReleaseInternal();
// 卸载未被使用的资源(包括场景)
AssetSystem.UnloadUnusedAssets();
// 返回场景卸载异步操作类
if (provider.IsDestroyed == false)
{ {
YooLogger.Warning($"Scene can not unload. The provider not destroyed : {provider.AssetPath}"); string error = $"Cannot unload main scene. Use {nameof(YooAssets.LoadSceneAsync)} method to change the main scene !";
var operation = new UnloadSceneOperation(); YooLogger.Error(error);
var operation = new UnloadSceneOperation(error);
OperationSystem.ProcessOperaiton(operation); OperationSystem.ProcessOperaiton(operation);
return operation; return operation;
} }
else
{
if (IsAdditiveScene(provider))
{
var operation = new UnloadSceneOperation(provider.SceneObject);
OperationSystem.ProcessOperaiton(operation);
return operation;
}
else
{
var operation = new UnloadSceneOperation();
OperationSystem.ProcessOperaiton(operation);
return operation;
}
}
}
private bool IsAdditiveScene(ProviderBase provider) // 卸载子场景
Scene sceneObject = SceneObject;
AssetSystem.UnloadSubScene(_provider);
{ {
if (provider is DatabaseSceneProvider) var operation = new UnloadSceneOperation(sceneObject);
{ OperationSystem.ProcessOperaiton(operation);
var temp = provider as DatabaseSceneProvider; return operation;
return temp.SceneMode == LoadSceneMode.Additive;
}
else if (provider is BundledSceneProvider)
{
var temp = provider as BundledSceneProvider;
return temp.SceneMode == LoadSceneMode.Additive;
}
else
{
throw new System.NotImplementedException();
} }
} }
} }

View File

@ -1,7 +1,7 @@
 
namespace YooAsset namespace YooAsset
{ {
public class SubAssetsOperationHandle : OperationHandleBase public sealed class SubAssetsOperationHandle : OperationHandleBase
{ {
private System.Action<SubAssetsOperationHandle> _callback; private System.Action<SubAssetsOperationHandle> _callback;

View File

@ -48,12 +48,15 @@ namespace YooAsset
_steps = ESteps.Done; _steps = ESteps.Done;
Status = EOperationStatus.Failed; Status = EOperationStatus.Failed;
Error = $"{nameof(AssetOperationHandle)} is invalid."; Error = $"{nameof(AssetOperationHandle)} is invalid.";
return;
} }
if (_handle.AssetObject == null) if (_handle.AssetObject == null)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
Status = EOperationStatus.Failed; Status = EOperationStatus.Failed;
Error = $"{nameof(AssetOperationHandle.AssetObject)} is null."; Error = $"{nameof(AssetOperationHandle.AssetObject)} is null.";
return;
} }
if(_setPositionRotation) if(_setPositionRotation)

View File

@ -12,7 +12,6 @@ namespace YooAsset
{ {
Normal, Normal,
Error, Error,
Skip,
} }
private enum ESteps private enum ESteps
{ {
@ -40,10 +39,6 @@ namespace YooAsset
} }
} }
internal UnloadSceneOperation()
{
_flag = EFlag.Skip;
}
internal UnloadSceneOperation(string error) internal UnloadSceneOperation(string error)
{ {
_flag = EFlag.Error; _flag = EFlag.Error;
@ -60,11 +55,6 @@ namespace YooAsset
{ {
_steps = ESteps.UnLoad; _steps = ESteps.UnLoad;
} }
else if (_flag == EFlag.Skip)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else if (_flag == EFlag.Error) else if (_flag == EFlag.Error)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;