using System; using System.Collections; using System.IO; using System.Reflection; using System.Threading.Tasks; using log4net; using log4net.Config; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using FishNet; /// /// The available scenes in the order they are in the build settings. /// public enum Scenes { GameMangement, MainMenu, InGameUI, Arena, } namespace Managers { /// /// Starts up the game and it's managers. /// Provides methods for loading additional scenes and triggering custom events when loaded. /// Orchestrates the managers for starting local and online matches. public class GameManager : MonoBehaviour { private static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); /// /// Globally accessible member to use manager with. /// public static GameManager G { get; private set; } [SerializeField] private UIManager UIManager; [SerializeField] private CharacterManager CharacterManager; [SerializeField] private ControlsManager ControlsManager; [SerializeField] private MatchManager MatchManager; [SerializeField] private StatisticsManager StatisticsManager; [SerializeField] private PlayerManager PlayerManager; [SerializeField] private AudioManager AudioManager; [SerializeField] private GameObject startCamera; public bool IsTestRun = false; public Scenes TestScene = Scenes.Arena; public event EventHandler CustomSceneLoaded; void Awake() { G = this; if (!IsTestRun) ShowStartScreen(); ConfigureLog4Net(); Log.Info("Awake"); } void Start() { InstantiateBaseManagers(); Log.Info("Space Smash Out is starting."); if (!IsTestRun) { LoadMainMenu(); } else { // Coroutine for waiting a frame and letting the managers run their Start methods StartCoroutine(SetupTestMatch(TestScene)); } } /// /// Configuration of log 4 net, before the game starts. /// void ConfigureLog4Net() { log4net.GlobalContext.Properties["LogFileName"] = $"{Application.dataPath}" + "\\Logging\\SSOLog"; var fi = new FileInfo($"{Application.dataPath}" + "\\Logging\\Log4NetConfiguration.xml"); XmlConfigurator.Configure(fi); Log.Debug("Log4Net configured."); } /// /// Instantiates the managers needed to play the game. /// void InstantiateBaseManagers() { if (UIManager != null) { UIManager = Instantiate(UIManager); Log.Info("User Interface Manager instantiated."); } else { Log.Error("User Interface Manager Prefab missing."); } if (ControlsManager != null) { ControlsManager = Instantiate(ControlsManager); Log.Info("Controls Manager instantiated."); } else { Log.Error("Controls Manager Prefab missing."); } if (MatchManager != null) { MatchManager = Instantiate(MatchManager); Log.Info("Match Manager instantiated."); } else { Log.Error("User Interface Manager Prefab missing."); } if (CharacterManager != null) { CharacterManager = Instantiate(CharacterManager); Log.Info("Character Manager instantiated."); } else { Log.Error("Character Manager Prefab missing."); } if (PlayerManager != null) { PlayerManager = Instantiate(PlayerManager); Log.Info("PlayerManager Manager instantiated."); } else { Log.Error("PlayerManager Manager Prefab missing."); } if (AudioManager != null) { AudioManager = Instantiate(AudioManager); Log.Info("PlayerManager Manager instantiated."); } else { Log.Error("PlayerManager Manager Prefab missing."); } } /// /// Starts the camera, for showing an image before the main menu is loaded. /// void ShowStartScreen() { startCamera.SetActive(true); } /// /// Sets up switching cameras once the menu is loaded /// and starts the async scene loading. /// void LoadMainMenu() { SingleUseSceneLoadedMethodCaller((args) => { SceneManager.SetActiveScene(args.SceneRef); startCamera.SetActive(false); }, Scenes.MainMenu); SceneManager.LoadSceneAsync((int)Scenes.MainMenu, LoadSceneMode.Additive); Log.Info("Main menu scene loaded."); } /// /// Initiates a local battle by giving relavant information to the responsible managers /// and starting their appropriate processes. /// /// Scene id of the chosen arena. public IEnumerator SetupLocalMatchFromMainMenu() { MatchManager.LoadMatchRules(0); MatchManager.LoadArenaProperties(0); MatchManager.AssignMatchObjects(); PlayerManager.LocalMatchJoinPlayers(MatchManager.ArenaProperties.minPlayerCount); // TODO: This is in place of a character choosing menu etc. foreach (Player p in PlayerManager.MatchPlayers) { CharacterManager.AssignShipFixed(p); Log.Debug($"Ship: {p.character.name} assigned to player: {p.playerNumber}."); } MatchManager.StartCharacterSelect(); CharacterManager.SpawnCharacters(PlayerManager.MatchPlayers); UIManager.StartManagingMatchUI(); UIManager.AssignHUDElementsToPlayers(PlayerManager.MatchPlayers); ControlsManager.AssignControlsToPlayers(PlayerManager.MatchPlayers); UIManager.StartInputPrompt(ControlsManager.unassignedPlayers); // Waits with starting the match until every player has controls while (ControlsManager.unassignedPlayers.Count > 0) { yield return null; } MatchManager.StartMatch(); } /// /// Starts a match scene without the menus or input/start prompts. /// /// The match scene which will be loaded /// public IEnumerator SetupTestMatch(Scenes scene) { yield return null; SceneManager.LoadScene((int)scene, LoadSceneMode.Additive); yield return null; SceneManager.LoadScene((int)Scenes.InGameUI, LoadSceneMode.Additive); yield return null; SceneManager.SetActiveScene(SceneManager.GetSceneByBuildIndex((int)scene)); yield return null; MatchManager.LoadMatchRules(0); MatchManager.LoadArenaProperties(0); MatchManager.AssignMatchObjects(); PlayerManager.LocalMatchJoinPlayers(MatchManager.ArenaProperties.minPlayerCount); foreach (Player p in PlayerManager.MatchPlayers) { CharacterManager.AssignShipFixed(p); Log.Debug($"Ship: {p.character.name} assigned to player: {p.playerNumber}."); } MatchManager.StartCharacterSelect(); CharacterManager.SpawnCharacters(PlayerManager.MatchPlayers); UIManager.StartManagingMatchUI(); UIManager.AssignHUDElementsToPlayers(PlayerManager.MatchPlayers); ControlsManager.AssignControlsToPlayers(PlayerManager.MatchPlayers); MatchManager.StartTestMatch(); } /// /// Initiates an online battle by giving relavant information to the responsible managers /// and starting their appropriate processes. /// /// Scene id of the chosen arena. public IEnumerator SetupOnlineMatchFromMainMenu() { MatchManager.LoadMatchRules(0); MatchManager.LoadArenaProperties(0); MatchManager.AssignMatchObjects(); // TODO: Selection for character in online play here PlayerManager.LoadOnlinePlayer(); if (InstanceFinder.IsServerStarted) CharacterManager.AssignShipFixed(PlayerManager.OnlinePlayer, 0); else if (InstanceFinder.IsClientStarted) CharacterManager.AssignShipFixed(PlayerManager.OnlinePlayer, 1); Log.Debug($"Ship: {PlayerManager.OnlinePlayer.character.name} assigned to online player."); CharacterManager.SpawnOnlineCharacter(PlayerManager.OnlinePlayer); UIManager.StartManagingMatchUI(); UIManager.AssignHUDElementsToPlayers(PlayerManager.MatchPlayers); ControlsManager.AssignControlsToPlayers(PlayerManager.MatchPlayers); UIManager.StartInputPrompt(ControlsManager.unassignedPlayers); // Waits with starting the match until every player has controls while (ControlsManager.unassignedPlayers.Count > 0) { yield return null; } MatchManager.StartTestMatch(); } /// /// Triggers a method once, when a specific scene was loaded. /// /// The method which will be executed /// once the scene is loaded. /// The scene which will trigger the method when loaded. public void SingleUseSceneLoadedMethodCaller(CustomOnSceneLoaded method, Scenes sceneEnum) { // This is used because SceneManager.sceneLoaded only passes ref and loadMode. // I want add the sceneEnum to this, to check if the the sceneLoaded event // refers to the same scene as passed to this method. UnityAction OnSceneLoaded = (sceneRef, loadMode) => { CustomSceneLoaded.Invoke(null, new SceneLoadEventArgs(sceneEnum, sceneRef)); }; EventHandler handler = null; handler = (invoker, args) => { // Thanks to the CustomSceneLoaded event I can check this here. if (args.SceneRef.buildIndex == (int)args.SceneEnum) { method(args); } else { Log.Warn("Should have loaded scene: " + args.SceneEnum.ToString() + " but the most recently loaded scene is: " + args.SceneRef.name + ". Be careful of loading many scenes at once from different scripts!"); } CustomSceneLoaded -= handler; SceneManager.sceneLoaded -= OnSceneLoaded; }; CustomSceneLoaded += handler; SceneManager.sceneLoaded += OnSceneLoaded; } } /// /// Custom delegate which can be executed after scene load. /// /// Data of the loaded scene public delegate void CustomOnSceneLoaded(SceneLoadEventArgs args); public class SceneLoadEventArgs : EventArgs { public SceneLoadEventArgs(Scenes sceneEnum, Scene sceneRef) { SceneEnum = sceneEnum; SceneRef = sceneRef; } public Scenes SceneEnum { get; set; } public Scene SceneRef { get; set; } } }