chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
666
Assets/Scripts/Managers/SceneLoader.cs
Normal file
666
Assets/Scripts/Managers/SceneLoader.cs
Normal file
@@ -0,0 +1,666 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.ResourceProviders;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.AddressableAssets.ResourceLocators;
|
||||
|
||||
/// <summary>
|
||||
/// Centralized scene loader that supports:
|
||||
/// - Built-in scenes (always tries first)
|
||||
/// - Remote Addressables (old system - fallback)
|
||||
/// - Custom AssetBundles (new system - Editor: built-in, Android: remote bundles)
|
||||
/// </summary>
|
||||
public static class SceneLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper abstraction that mimics AsyncOperation while backing either a SceneManager load or an Addressables handle.
|
||||
/// </summary>
|
||||
public sealed class SceneLoadHandle : CustomYieldInstruction
|
||||
{
|
||||
private readonly string _sceneName;
|
||||
private readonly Action<SceneLoadHandle> _onCompleted;
|
||||
private readonly LoadSceneMode _mode;
|
||||
|
||||
private readonly AsyncOperation _unityAsyncOp;
|
||||
private AsyncOperationHandle<SceneInstance>? _addressableHandle;
|
||||
private AsyncOperation _activationOperation;
|
||||
|
||||
private bool _allowSceneActivation;
|
||||
private bool _activationRequested;
|
||||
private bool _completed;
|
||||
|
||||
// Constructor for pending initialization (no operation yet)
|
||||
internal SceneLoadHandle(string sceneName, LoadSceneMode mode, bool initialActivation, Action<SceneLoadHandle> onCompleted)
|
||||
{
|
||||
_sceneName = sceneName;
|
||||
_mode = mode;
|
||||
_allowSceneActivation = initialActivation;
|
||||
_onCompleted = onCompleted;
|
||||
}
|
||||
|
||||
internal SceneLoadHandle(string sceneName, LoadSceneMode mode, AsyncOperation unityAsyncOp, bool initialActivation, Action<SceneLoadHandle> onCompleted)
|
||||
{
|
||||
_sceneName = sceneName;
|
||||
_mode = mode;
|
||||
_unityAsyncOp = unityAsyncOp;
|
||||
_onCompleted = onCompleted;
|
||||
|
||||
if (_unityAsyncOp != null)
|
||||
{
|
||||
_unityAsyncOp.allowSceneActivation = initialActivation;
|
||||
_unityAsyncOp.completed += _ => Complete();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[SceneLoader] Unity AsyncOperation is null for '{sceneName}'.");
|
||||
Complete();
|
||||
}
|
||||
}
|
||||
|
||||
internal SceneLoadHandle(string sceneName, LoadSceneMode mode, AsyncOperationHandle<SceneInstance> addressableHandle, bool initialActivation, Action<SceneLoadHandle> onCompleted)
|
||||
{
|
||||
_sceneName = sceneName;
|
||||
_mode = mode;
|
||||
_addressableHandle = addressableHandle;
|
||||
_allowSceneActivation = initialActivation;
|
||||
_onCompleted = onCompleted;
|
||||
|
||||
if (!addressableHandle.IsValid())
|
||||
{
|
||||
Debug.LogError($"[SceneLoader] Addressables handle is invalid for '{sceneName}'.");
|
||||
Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
addressableHandle.Completed += OnAddressableLoaded;
|
||||
|
||||
if (initialActivation)
|
||||
{
|
||||
TryBeginActivation();
|
||||
}
|
||||
}
|
||||
|
||||
// Update this handle with an Addressables operation
|
||||
internal void SetAddressableHandle(AsyncOperationHandle<SceneInstance> addressableHandle)
|
||||
{
|
||||
_addressableHandle = addressableHandle;
|
||||
|
||||
if (!addressableHandle.IsValid())
|
||||
{
|
||||
Debug.LogError($"[SceneLoader] Addressables handle is invalid for '{_sceneName}'.");
|
||||
Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
addressableHandle.Completed += OnAddressableLoaded;
|
||||
|
||||
if (_allowSceneActivation)
|
||||
{
|
||||
TryBeginActivation();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool keepWaiting => !isDone;
|
||||
|
||||
public string SceneName => _sceneName;
|
||||
|
||||
public bool allowSceneActivation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_unityAsyncOp != null)
|
||||
{
|
||||
return _unityAsyncOp.allowSceneActivation;
|
||||
}
|
||||
|
||||
return _allowSceneActivation;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_unityAsyncOp != null)
|
||||
{
|
||||
_unityAsyncOp.allowSceneActivation = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_allowSceneActivation == value)
|
||||
return;
|
||||
|
||||
_allowSceneActivation = value;
|
||||
if (value)
|
||||
{
|
||||
TryBeginActivation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float progress
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_unityAsyncOp != null)
|
||||
{
|
||||
return _unityAsyncOp.progress;
|
||||
}
|
||||
|
||||
if (!_addressableHandle.HasValue || !_addressableHandle.Value.IsValid())
|
||||
return 0f;
|
||||
|
||||
float baseProgress = _addressableHandle.Value.PercentComplete;
|
||||
|
||||
if (_activationOperation != null)
|
||||
{
|
||||
return Mathf.Lerp(Mathf.Min(baseProgress, 0.9f), 1f, _activationOperation.progress);
|
||||
}
|
||||
|
||||
if (!_allowSceneActivation && baseProgress >= 0.9f)
|
||||
{
|
||||
return 0.9f;
|
||||
}
|
||||
|
||||
return baseProgress;
|
||||
}
|
||||
}
|
||||
|
||||
public bool isDone
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_unityAsyncOp != null)
|
||||
{
|
||||
return _unityAsyncOp.isDone;
|
||||
}
|
||||
|
||||
if (_activationOperation != null)
|
||||
{
|
||||
return _activationOperation.isDone;
|
||||
}
|
||||
|
||||
if (!_addressableHandle.HasValue || !_addressableHandle.Value.IsValid())
|
||||
{
|
||||
return _completed;
|
||||
}
|
||||
|
||||
if (!_allowSceneActivation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var handle = _addressableHandle.Value;
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Succeeded && handle.Result.Scene.IsValid() && handle.Result.Scene.isLoaded)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncOperationHandle<SceneInstance>? AddressableHandle => _addressableHandle;
|
||||
|
||||
private void OnAddressableLoaded(AsyncOperationHandle<SceneInstance> handle)
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
Debug.LogError($"[SceneLoader] Addressables failed to load '{_sceneName}': {handle.OperationException?.Message}");
|
||||
Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_allowSceneActivation)
|
||||
{
|
||||
BeginActivation(handle);
|
||||
}
|
||||
}
|
||||
|
||||
private void TryBeginActivation()
|
||||
{
|
||||
if (!_addressableHandle.HasValue || !_addressableHandle.Value.IsValid())
|
||||
return;
|
||||
|
||||
if (_activationRequested)
|
||||
return;
|
||||
|
||||
var handle = _addressableHandle.Value;
|
||||
if (handle.Status == AsyncOperationStatus.Succeeded)
|
||||
{
|
||||
BeginActivation(handle);
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginActivation(AsyncOperationHandle<SceneInstance> handle)
|
||||
{
|
||||
if (_activationRequested)
|
||||
return;
|
||||
|
||||
_activationRequested = true;
|
||||
|
||||
try
|
||||
{
|
||||
_activationOperation = handle.Result.ActivateAsync();
|
||||
if (_activationOperation != null)
|
||||
{
|
||||
_activationOperation.completed += _ =>
|
||||
{
|
||||
if (_mode == LoadSceneMode.Single)
|
||||
{
|
||||
SceneManager.SetActiveScene(handle.Result.Scene);
|
||||
}
|
||||
Complete();
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_mode == LoadSceneMode.Single)
|
||||
{
|
||||
SceneManager.SetActiveScene(handle.Result.Scene);
|
||||
}
|
||||
Complete();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
Complete();
|
||||
}
|
||||
}
|
||||
|
||||
internal void Complete()
|
||||
{
|
||||
if (_completed)
|
||||
return;
|
||||
|
||||
_completed = true;
|
||||
_onCompleted?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static SceneLoadHandle _currentHandle;
|
||||
private static string _currentSceneName;
|
||||
private static bool _addressablesInitialized;
|
||||
private static AsyncOperationHandle<IResourceLocator>? _initializationHandle;
|
||||
private static readonly object _addressablesInitLock = new();
|
||||
|
||||
// Custom AssetBundle system (new system - replaces Addressables)
|
||||
private static bool _customAssetBundleInitialized;
|
||||
|
||||
public static bool IsLoading => _currentHandle != null && !_currentHandle.isDone;
|
||||
public static string CurrentSceneName => _currentSceneName;
|
||||
public static SceneLoadHandle CurrentOperation => _currentHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Load a scene once, preferring build scenes but falling back to custom AssetBundles or Addressables.
|
||||
/// For "Game" scene: Uses built-in in Editor, remote AssetBundle in Android build.
|
||||
/// </summary>
|
||||
public static SceneLoadHandle LoadSceneAsyncOnce(
|
||||
string sceneName,
|
||||
LoadSceneMode mode = LoadSceneMode.Single,
|
||||
bool allowSceneActivation = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sceneName))
|
||||
{
|
||||
Debug.LogError("SceneLoader: sceneName is null or empty.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (IsLoading)
|
||||
{
|
||||
if (string.Equals(_currentSceneName, sceneName, StringComparison.Ordinal))
|
||||
{
|
||||
Debug.LogWarning($"SceneLoader: '{sceneName}' is already loading. Returning existing handle.");
|
||||
return _currentHandle;
|
||||
}
|
||||
|
||||
Debug.LogWarning($"SceneLoader: A different scene ('{_currentSceneName}') is already loading. Ignoring new request '{sceneName}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_currentSceneName = sceneName;
|
||||
|
||||
// DISABLED: AssetBundle loading for Game scene
|
||||
// Uncomment the block below to re-enable custom AssetBundle loading on Android
|
||||
/*
|
||||
#if !UNITY_EDITOR
|
||||
// CRITICAL FIX: On Android, ALWAYS use custom AssetBundle system for Game scene
|
||||
// Don't check Application.CanStreamedLevelBeLoaded - it's unreliable!
|
||||
if (sceneName == "Game")
|
||||
{
|
||||
Debug.Log("[SceneLoader] Android build detected - Loading Game scene via custom AssetBundle system...");
|
||||
var initHandle = new SceneLoadHandle(sceneName, mode, allowSceneActivation, OnHandleCompleted);
|
||||
_currentHandle = initHandle;
|
||||
|
||||
if (GameManager.Instance != null)
|
||||
{
|
||||
GameManager.Instance.StartCoroutine(LoadGameSceneViaCustomAssetBundle(mode, allowSceneActivation, initHandle));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[SceneLoader] GameManager.Instance is null! Cannot load via custom AssetBundles.");
|
||||
ResetState();
|
||||
return null;
|
||||
}
|
||||
|
||||
return initHandle;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
// 1. Try built-in scene first (works in Editor for development)
|
||||
if (Application.CanStreamedLevelBeLoaded(sceneName))
|
||||
{
|
||||
var asyncOp = SceneManager.LoadSceneAsync(sceneName, mode);
|
||||
if (asyncOp == null)
|
||||
{
|
||||
Debug.LogError($"SceneLoader: SceneManager returned null AsyncOperation for '{sceneName}'.");
|
||||
ResetState();
|
||||
return null;
|
||||
}
|
||||
|
||||
asyncOp.allowSceneActivation = allowSceneActivation;
|
||||
_currentHandle = new SceneLoadHandle(sceneName, mode, asyncOp, allowSceneActivation, OnHandleCompleted);
|
||||
Debug.Log($"[SceneLoader] Started built-in async load for '{sceneName}'.");
|
||||
return _currentHandle;
|
||||
}
|
||||
|
||||
// 2. For other scenes not in build: Use Addressables (fallback)
|
||||
Debug.Log($"[SceneLoader] Scene '{sceneName}' not in build settings.");
|
||||
|
||||
// DISABLED: Addressables fallback - make sure all scenes are in Build Settings
|
||||
// Uncomment the block below to re-enable Addressables fallback loading
|
||||
/*
|
||||
#if !UNITY_EDITOR
|
||||
// This should not be reached for Game scene anymore (handled above)
|
||||
Debug.LogWarning($"[SceneLoader] Scene '{sceneName}' not found in build settings and not Game scene!");
|
||||
#endif
|
||||
|
||||
// 3. Fallback: Try Addressables for other scenes
|
||||
Debug.Log($"[SceneLoader] Attempting Addressables load for '{sceneName}'...");
|
||||
var addressablesHandle = new SceneLoadHandle(sceneName, mode, allowSceneActivation, OnHandleCompleted);
|
||||
_currentHandle = addressablesHandle;
|
||||
|
||||
if (GameManager.Instance != null)
|
||||
{
|
||||
GameManager.Instance.StartCoroutine(InitializeAndLoadAddressableScene(sceneName, mode, allowSceneActivation, addressablesHandle));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[SceneLoader] GameManager.Instance is null! Cannot start coroutine for Addressables init.");
|
||||
ResetState();
|
||||
return null;
|
||||
}
|
||||
|
||||
return addressablesHandle;
|
||||
*/
|
||||
// Since AssetBundles/Addressables are disabled, log error and return null
|
||||
Debug.LogError($"[SceneLoader] Scene '{sceneName}' is NOT in Build Settings! Add it to Build Settings to load.");
|
||||
ResetState();
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void OnHandleCompleted(SceneLoadHandle handle)
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
var addrHandleNullable = handle.AddressableHandle;
|
||||
if (addrHandleNullable.HasValue)
|
||||
{
|
||||
var addrHandle = addrHandleNullable.Value;
|
||||
if (addrHandle.IsValid() && addrHandle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
Debug.LogError($"SceneLoader: Addressables load failed for '{handle.SceneName}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_currentHandle = null;
|
||||
_currentSceneName = null;
|
||||
}
|
||||
|
||||
// Coroutine to load Game scene via custom AssetBundle system (Android builds only)
|
||||
private static IEnumerator LoadGameSceneViaCustomAssetBundle(
|
||||
LoadSceneMode mode,
|
||||
bool allowSceneActivation,
|
||||
SceneLoadHandle handle)
|
||||
{
|
||||
Debug.Log("[SceneLoader] Initializing custom AssetBundle system...");
|
||||
|
||||
// Initialize custom AssetBundle system if not already done
|
||||
if (!_customAssetBundleInitialized)
|
||||
{
|
||||
yield return CustomAssetBundleSystem.RemoteAssetBundleManager.Instance.InitializeAsync();
|
||||
|
||||
if (CustomAssetBundleSystem.RemoteAssetBundleManager.Instance.IsInitialized)
|
||||
{
|
||||
_customAssetBundleInitialized = true;
|
||||
Debug.Log("[SceneLoader] ✓ Custom AssetBundle system initialized!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[SceneLoader] ✗ Failed to initialize custom AssetBundle system!");
|
||||
handle.Complete();
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
// Load the Game scene from AssetBundle
|
||||
Debug.Log("[SceneLoader] Loading Game scene from remote AssetBundle...");
|
||||
bool sceneLoaded = false;
|
||||
|
||||
yield return CustomAssetBundleSystem.RemoteAssetBundleManager.Instance.LoadSceneAsync("Game", (success) =>
|
||||
{
|
||||
sceneLoaded = success;
|
||||
});
|
||||
|
||||
if (sceneLoaded)
|
||||
{
|
||||
Debug.Log("[SceneLoader] ✓ Game scene loaded successfully from AssetBundle!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[SceneLoader] ✗ Failed to load Game scene from AssetBundle!");
|
||||
}
|
||||
|
||||
// Complete the handle
|
||||
handle.Complete();
|
||||
}
|
||||
|
||||
// Coroutine to handle async Addressables initialization without blocking main thread
|
||||
private static System.Collections.IEnumerator InitializeAndLoadAddressableScene(
|
||||
string sceneName,
|
||||
LoadSceneMode mode,
|
||||
bool allowSceneActivation,
|
||||
SceneLoadHandle handle)
|
||||
{
|
||||
Debug.Log($"[SceneLoader] Coroutine: Starting async Addressables init for '{sceneName}'...");
|
||||
|
||||
// Start the async init task
|
||||
var initTask = EnsureAddressablesInitializedAsync();
|
||||
|
||||
// Wait until the task completes without blocking
|
||||
while (!initTask.IsCompleted)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Check result
|
||||
bool initSuccess = false;
|
||||
try
|
||||
{
|
||||
initSuccess = initTask.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[SceneLoader] Addressables initialization threw exception: {ex.Message}");
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
|
||||
if (!initSuccess)
|
||||
{
|
||||
Debug.LogError($"[SceneLoader] Addressables init failed. Cannot load '{sceneName}'.");
|
||||
ResetState();
|
||||
yield break;
|
||||
}
|
||||
|
||||
Debug.Log("[SceneLoader] Addressables initialized. Locating scene address...");
|
||||
|
||||
if (!TryLocateSceneAddress(sceneName, out var address))
|
||||
{
|
||||
Debug.LogError($"[SceneLoader] Scene '{sceneName}' not found in Addressables catalog.");
|
||||
ResetState();
|
||||
yield break;
|
||||
}
|
||||
|
||||
Debug.Log($"[SceneLoader] Loading scene '{sceneName}' from address '{address}'...");
|
||||
var addressableHandle = Addressables.LoadSceneAsync(address, mode, activateOnLoad: false);
|
||||
handle.SetAddressableHandle(addressableHandle);
|
||||
}
|
||||
|
||||
private static void ResetState()
|
||||
{
|
||||
_currentHandle = null;
|
||||
_currentSceneName = null;
|
||||
}
|
||||
|
||||
internal static async System.Threading.Tasks.Task<bool> EnsureAddressablesInitializedAsync()
|
||||
{
|
||||
// Quick check without lock first
|
||||
if (_addressablesInitialized)
|
||||
return true;
|
||||
|
||||
AsyncOperationHandle<IResourceLocator> handle;
|
||||
|
||||
// Lock only for checking/starting initialization
|
||||
lock (_addressablesInitLock)
|
||||
{
|
||||
if (_addressablesInitialized)
|
||||
return true;
|
||||
|
||||
if (!_initializationHandle.HasValue || !_initializationHandle.Value.IsValid())
|
||||
{
|
||||
_initializationHandle = Addressables.InitializeAsync();
|
||||
}
|
||||
|
||||
handle = _initializationHandle.Value;
|
||||
|
||||
if (!handle.IsValid())
|
||||
{
|
||||
Debug.LogError("[SceneLoader] Addressables initialization returned an invalid handle.");
|
||||
_initializationHandle = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Await OUTSIDE the lock to avoid CS1996 error
|
||||
try
|
||||
{
|
||||
await handle.Task;
|
||||
|
||||
// Capture status immediately after await, BEFORE any lock or release
|
||||
bool succeeded = false;
|
||||
AsyncOperationStatus status = AsyncOperationStatus.None;
|
||||
string operationException = null;
|
||||
|
||||
if (handle.IsValid())
|
||||
{
|
||||
status = handle.Status;
|
||||
succeeded = status == AsyncOperationStatus.Succeeded;
|
||||
|
||||
// Capture exception details if available
|
||||
if (handle.OperationException != null)
|
||||
{
|
||||
operationException = handle.OperationException.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// Lock again to update shared state and release
|
||||
lock (_addressablesInitLock)
|
||||
{
|
||||
if (handle.IsValid())
|
||||
{
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
|
||||
_initializationHandle = null;
|
||||
|
||||
if (succeeded)
|
||||
{
|
||||
_addressablesInitialized = true;
|
||||
Debug.Log("[SceneLoader] Addressables initialized successfully (async).");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Log detailed failure information
|
||||
Debug.LogError($"[SceneLoader] Addressables initialization failed. Status: {status}");
|
||||
if (!string.IsNullOrEmpty(operationException))
|
||||
{
|
||||
Debug.LogError($"[SceneLoader] Addressables OperationException: {operationException}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[SceneLoader] No OperationException available. Check catalog URL, network connectivity, and Addressables build settings.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lock (_addressablesInitLock)
|
||||
{
|
||||
if (_initializationHandle.HasValue && _initializationHandle.Value.IsValid())
|
||||
{
|
||||
Addressables.Release(_initializationHandle.Value);
|
||||
}
|
||||
|
||||
_initializationHandle = null;
|
||||
}
|
||||
Debug.LogError($"[SceneLoader] Addressables async init exception: {ex.GetType().Name} - {ex.Message}");
|
||||
Debug.LogError($"[SceneLoader] Full exception details:\n{ex.ToString()}");
|
||||
Debug.LogException(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryLocateSceneAddress(string sceneName, out string address)
|
||||
{
|
||||
address = null;
|
||||
|
||||
foreach (var candidate in EnumerateAddressCandidates(sceneName))
|
||||
{
|
||||
foreach (var locator in Addressables.ResourceLocators)
|
||||
{
|
||||
if (locator.Locate(candidate, typeof(SceneInstance), out IList<IResourceLocation> locations) && locations != null && locations.Count > 0)
|
||||
{
|
||||
address = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> EnumerateAddressCandidates(string sceneName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sceneName)) yield break;
|
||||
|
||||
yield return sceneName;
|
||||
|
||||
if (!sceneName.EndsWith(".unity", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
yield return sceneName + ".unity";
|
||||
}
|
||||
|
||||
yield return $"Assets/Scenes/{sceneName}.unity";
|
||||
yield return $"Assets/{sceneName}.unity";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user