320 lines
8.7 KiB
C#
320 lines
8.7 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using DG.Tweening;
|
|
using NaughtyAttributes;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.UI;
|
|
using TMPro;
|
|
|
|
enum LoadState
|
|
{
|
|
Idle,
|
|
FadeOut,
|
|
UnityUnload,
|
|
Cleanup,
|
|
UnityLoad,
|
|
SceneSetup,
|
|
Sync,
|
|
FadeIn,
|
|
|
|
COUNT
|
|
}
|
|
|
|
public class Bootstrap : MonoBehaviour
|
|
{
|
|
[Header( "Debug" )]
|
|
[SerializeField]
|
|
private bool _debugMode;
|
|
|
|
[Header( "Config" )]
|
|
[SerializeField] [Expandable]
|
|
private BootstrapConfig _config;
|
|
|
|
private static Bootstrap _instance;
|
|
|
|
private LoadState _loadState;
|
|
private Coroutine _currentSceneLoad;
|
|
private SceneType _activeSceneType;
|
|
|
|
private SceneLoader _activeScene;
|
|
private string _activeSceneName;
|
|
|
|
[Header( "References" )]
|
|
[SerializeField]
|
|
private Image _fader;
|
|
|
|
[SerializeField]
|
|
private TMP_Text _debugDisplay;
|
|
|
|
//private LevelDescriptor _activeLevelDescriptor;
|
|
|
|
private List<LevelDescriptor> _loadedLevels = new List<LevelDescriptor>();
|
|
|
|
private Queue<LevelDescriptor> _toLoadLevels = new Queue<LevelDescriptor>();
|
|
private AsyncOperation _levelLoadAsync = null;
|
|
|
|
private Queue<LevelDescriptor> _toUnloadLevels = new Queue<LevelDescriptor>();
|
|
private AsyncOperation _levelUnloadAsync = null;
|
|
|
|
public bool IsLoading => _loadState != LoadState.Idle;
|
|
|
|
private void QueryLoadState()
|
|
{
|
|
if ( _activeScene != null && _activeScene.ToLoad != SceneType.None )
|
|
{
|
|
LoadScene( _activeScene.ToLoad );
|
|
}
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
_instance = this;
|
|
DOTween.Init();
|
|
|
|
if ( _debugMode )
|
|
{
|
|
StartCoroutine( DebugCR() );
|
|
}
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
LoadScene( SceneType.BootSequence );
|
|
}
|
|
|
|
public static void LoadScene( SceneType scene )
|
|
{
|
|
if ( _instance.IsLoading )
|
|
{
|
|
Debug.LogError( "[Bootstrap]: couldn't load scene: another load operation already active." );
|
|
return;
|
|
}
|
|
|
|
_instance._currentSceneLoad = _instance.StartCoroutine( _instance.LoadSceneCR( scene ) );
|
|
}
|
|
|
|
public static void LoadLevel( LevelDescriptor descriptor )
|
|
{
|
|
_instance._toLoadLevels.Enqueue( descriptor );
|
|
}
|
|
|
|
private IEnumerator DebugCR()
|
|
{
|
|
LoadState loadState = LoadState.COUNT;
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
while ( true )
|
|
{
|
|
if ( _loadState != loadState )
|
|
{
|
|
loadState = _loadState;
|
|
sb.Clear();
|
|
|
|
sb.Append( "LoadState::" );
|
|
sb.AppendLine( _loadState.ToString() );
|
|
|
|
if ( _activeSceneName != null )
|
|
{
|
|
sb.AppendFormat( "S: {0} ({1})", _activeSceneType, _activeSceneName );
|
|
}
|
|
else
|
|
{
|
|
sb.AppendFormat( "S: [none]" );
|
|
}
|
|
|
|
sb.Append( "\n" );
|
|
|
|
if ( (_loadedLevels?.Count ?? 0) > 0 )
|
|
{
|
|
sb.Append( "L: " );
|
|
for ( int i = 0; i < _loadedLevels.Count; ++i )
|
|
{
|
|
sb.Append( _loadedLevels[ i ].LevelScene );
|
|
if ( i != _loadedLevels.Count - 1 )
|
|
{
|
|
sb.Append( ", " );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sb.AppendFormat( "L: [none]" );
|
|
}
|
|
|
|
sb.Append( "\n" );
|
|
sb.AppendFormat( "Load: {0} ({1} queued)\n", _levelLoadAsync?.isDone ?? true ? "idle" : "busy" , _toLoadLevels.Count );
|
|
sb.AppendFormat( "Unload: {0} ({1} queued)\n", _levelUnloadAsync?.isDone ?? true ? "idle" : "busy" , _toUnloadLevels.Count );
|
|
|
|
_debugDisplay.SetText( sb.ToString() );
|
|
}
|
|
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
private bool ProcessLevelUnloads()
|
|
{
|
|
if (_levelUnloadAsync?.isDone ?? true)
|
|
{
|
|
while (true)
|
|
{
|
|
if ( _toUnloadLevels.Count == 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
var target = _toUnloadLevels.Dequeue();
|
|
if ( _loadedLevels.Contains( target ) == false )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_levelUnloadAsync = SceneManager.UnloadSceneAsync( target.LevelScene );
|
|
_loadedLevels.Remove( target );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return (_levelUnloadAsync?.isDone ?? true) && _toUnloadLevels.Count == 0;
|
|
}
|
|
|
|
private bool ProcessLevelLoads()
|
|
{
|
|
if (_levelLoadAsync?.isDone ?? true)
|
|
{
|
|
while (true)
|
|
{
|
|
if ( _toLoadLevels.Count == 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
var target = _toLoadLevels.Dequeue();
|
|
if ( _loadedLevels.Contains( target ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_levelLoadAsync = SceneManager.LoadSceneAsync( target.LevelScene, LoadSceneMode.Additive );
|
|
_loadedLevels.Add( target );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return (_levelLoadAsync?.isDone ?? true) && _toLoadLevels.Count == 0;
|
|
}
|
|
|
|
private IEnumerator LoadSceneCR( SceneType scene )
|
|
{
|
|
AsyncOperation asyncOp;
|
|
|
|
// fade out
|
|
_loadState = LoadState.FadeOut;
|
|
_fader.CrossFadeAlpha( 1f, _config.TransitionDuration, true );
|
|
yield return new WaitForSecondsRealtime( _config.TransitionDuration );
|
|
|
|
// unload unity's scenes
|
|
_loadState = LoadState.UnityUnload;
|
|
|
|
if ( _activeSceneName != null )
|
|
{
|
|
// unload main scene
|
|
asyncOp = SceneManager.UnloadSceneAsync( _activeSceneName );
|
|
|
|
while ( asyncOp.isDone == false )
|
|
yield return null;
|
|
|
|
_activeScene = null;
|
|
_activeSceneName = null;
|
|
}
|
|
|
|
// cleanup
|
|
_loadState = LoadState.Cleanup;
|
|
|
|
if ( scene == SceneType.Gameplay )
|
|
{
|
|
_toUnloadLevels.Clear();
|
|
_toLoadLevels.Clear();
|
|
_loadedLevels.Clear();
|
|
_toLoadLevels.Enqueue( _config.StartLevel );
|
|
}
|
|
else
|
|
{
|
|
_toLoadLevels.Clear();
|
|
|
|
for ( int i = 0; i < _loadedLevels.Count; ++i )
|
|
{
|
|
if ( _toUnloadLevels.Contains( _loadedLevels[ i ] ) == false )
|
|
{
|
|
_toUnloadLevels.Enqueue( _loadedLevels[ i ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
// 6 collections, as that is the deepest unity hierarchies go.
|
|
for ( int i = 0; i < 6; ++i )
|
|
{
|
|
GC.Collect();
|
|
yield return null;
|
|
}
|
|
|
|
// unity loading
|
|
_loadState = LoadState.UnityLoad;
|
|
_activeSceneType = scene;
|
|
_activeSceneName = _config.GetScene( scene );
|
|
|
|
asyncOp = SceneManager.LoadSceneAsync( _activeSceneName, LoadSceneMode.Additive );
|
|
|
|
while ( asyncOp.isDone == false )
|
|
yield return null;
|
|
|
|
// setup
|
|
_loadState = LoadState.SceneSetup;
|
|
|
|
_activeScene = FindObjectOfType<SceneLoader>();
|
|
|
|
while ( _activeScene.LoadComplete == false )
|
|
yield return null;
|
|
|
|
// sync
|
|
_loadState = LoadState.Sync;
|
|
while ( ProcessLevelUnloads() == false )
|
|
{
|
|
yield return null;
|
|
}
|
|
|
|
while ( ProcessLevelLoads() == false )
|
|
{
|
|
yield return null;
|
|
}
|
|
|
|
// fade in
|
|
_loadState = LoadState.FadeIn;
|
|
_fader.CrossFadeAlpha( 0f, _config.TransitionDuration, true );
|
|
yield return new WaitForSecondsRealtime( _config.TransitionDuration );
|
|
|
|
yield return null;
|
|
_loadState = LoadState.Idle;
|
|
_currentSceneLoad = null;
|
|
}
|
|
|
|
|
|
private void Update()
|
|
{
|
|
if ( !IsLoading )
|
|
{
|
|
QueryLoadState();
|
|
|
|
if ( _activeSceneType == SceneType.Gameplay )
|
|
{
|
|
if ( ProcessLevelUnloads() )
|
|
{
|
|
ProcessLevelLoads();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |