Files
VOIDRPG/Assets/90 - Scripts/Generic/GameLoopManager.cs
T
2026-07-09 21:33:33 +02:00

197 lines
7.1 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using VOID.Generic.Navigation;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Player;
using VOID.Generic.SaveLoad;
using VOID.UserInterface.LoadingScreen;
using VOID.Generic.Util;
using VOID.Generic.World;
namespace VOID.Generic
{
public class GameLoopManager : MonoBehaviour
{
public static GameLoopManager current { get; private set; }
[SerializeField] private SceneReference _mainMenuScene;
[SerializeField] private SceneReference _LoadingScreenScene;
[SerializeField] private SceneReference _lobbyScene;
[SerializeField] private SceneReference _worldGeneratorScene;
private void Awake()
{
if (current)
{
Debug.LogWarning("Instance of " + nameof(GameLoopManager) + " already exists!");
Destroy(this);
return;
}
// Only one instance for whole game, this should manage scene changing nad using all other managers
// DontDestroyOnLoad works only on root game object so we need to unparent it
current = this;
current.transform.parent = null;
DontDestroyOnLoad(this);
}
private void Start()
{
// Only for dev/debug purpose - start instantly current scene
if (SceneManager.GetActiveScene().path != _mainMenuScene)
{
StartCoroutine(InitializeDebugScene());
return;
}
StartCoroutine(InitializeMainMenu());
}
private IEnumerator InitializeDebugScene()
{
// wait for other things
yield return new WaitForFixedUpdate();
// So far only thing needed for dev/debug scene to work is initialize player's units
if (PlayerController.current.mainUnit)
{
PlayerController.current.SetMainUnit(PlayerController.current.mainUnit);
PlayerController.current.SetMainUnitActive();
CameraManager.current.stickTo = PlayerController.current.mainUnit.transform;
}
if (PlayerController.current.subUnit)
{
PlayerController.current.SetSubUnit(PlayerController.current.subUnit);
}
// Create (if missing) profile and load it
PlayerProfileManager.current.SelectProfile(PlayerProfileManager.current.GetDefaultProfile());
}
private IEnumerator InitializeMainMenu()
{
// wait for other things
yield return new WaitForFixedUpdate();
// Load last profile if exists
var defaultProfile = PlayerProfileManager.current.GetDefaultProfile();
if (defaultProfile != null) PlayerProfileManager.current.SelectProfile(defaultProfile);
LoadMainMenuAfter().ForEach(action => action.Invoke());
}
#region BeforeLoading
public void LoadMainMenu()
{
StartCoroutine(LoadScene(_mainMenuScene, LoadMainMenuAfter()));
}
public void LoadLobby()
{
StartCoroutine(LoadScene(_lobbyScene, LoadLobbyAfter()));
}
public void LoadGenerator(UnitObject playerUnit)
{
// Dont destroy selected avatar by player on scene change, to do that we need to remove its PARENT!
playerUnit.gameObject.transform.parent = null;
DontDestroyOnLoad(playerUnit.gameObject);
StartCoroutine(LoadScene(_worldGeneratorScene, LoadGeneratorAfter(playerUnit)));
}
#endregion
#region LoadingScreen
private IEnumerator LoadScene(SceneReference sceneReference, List<Action> onLoadAfter)
{
// Load LoadingScene
// Reset loading progress
// Previous scene will be automatically unloaded
var loadAsync = SceneManager.LoadSceneAsync(_LoadingScreenScene, LoadSceneMode.Single);
yield return new WaitUntil(() => loadAsync.isDone);
LoadingScreenUI.current.SetProgress(0f);
yield return new WaitForEndOfFrame();
// Load wanted scene
// Important - make that scene active before any Awake() execute!
var sceneAsync = SceneManager.LoadSceneAsync(sceneReference, LoadSceneMode.Additive);
sceneAsync.completed += _ => SceneManager.SetActiveScene(SceneManager.GetSceneByPath(sceneReference));
yield return new WaitUntil(() => sceneAsync.isDone);
LoadingScreenUI.current.SetProgress(0.2f);
yield return new WaitForEndOfFrame();
// Additional steps to do before fully changing to next scene
if (onLoadAfter.Count > 0)
{
var progressPerAction = 0.8f / onLoadAfter.Count;
for (var i = 0; i < onLoadAfter.Count; i++)
{
onLoadAfter[i].Invoke();
LoadingScreenUI.current.SetProgress(0.2f + (i+1) * progressPerAction);
yield return new WaitForEndOfFrame();
}
}
// Set progress to 100% + TODO: wait 1 second so we won't be too "fast"
LoadingScreenUI.current.SetProgress(1f);
yield return new WaitForSecondsRealtime(1f);
// Change to next scene + remove loading screen
SceneManager.UnloadSceneAsync(_LoadingScreenScene);
}
#endregion
#region AfterLoading
private List<Action> LoadGeneratorAfter(UnitObject playerUnit)
{
return new List<Action> { GenerateWorld, GenerateNavMesh, InitializePlayer };
void GenerateWorld()
{
// Generate world
var seed = RandomUtil.RandomInteger(0, int.MaxValue);
WorldGeneratorManager.current.Generate(150, seed, true);
Debug.Log("World Generated with seed: " + seed);
}
void GenerateNavMesh()
{
// Build nav mesh for whole map
NavigationManager.current.BuildNavMesh();
}
void InitializePlayer()
{
// initialize managers with selected player avatar
playerUnit.transform.position = new Vector3(0, 0, 0);
PlayerController.current.SetMainUnit(playerUnit);
PlayerController.current.SetMainUnitActive();
CameraManager.current.stickTo = playerUnit.transform;
// Make avatar destroyable again on scene change
SceneManager.MoveGameObjectToScene(playerUnit.gameObject, SceneManager.GetActiveScene());
}
}
private List<Action> LoadMainMenuAfter()
{
return new List<Action>() { };
}
private List<Action> LoadLobbyAfter()
{
return new List<Action>() { };
}
#endregion
}
}