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;
///
/// 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);
PlayerManager.LocalMatchJoinPlayers(MatchManager.ArenaProperties.minPlayerCount);
// TODO: This is in place of a character choosing menu etc.
foreach (Player p in PlayerManager.localPlayers)
{
CharacterManager.AssignShipFixed(p);
Log.Debug($"Ship: {p.character.name} assigned to player: {p.playerNumber}.");
}
MatchManager.StartCharacterSelect();
MatchManager.SpawnCharacters(PlayerManager.localPlayers);
UIManager.StartManagingMatchUI();
UIManager.AssignHUDElementsToPlayers(PlayerManager.localPlayers);
ControlsManager.AssignControlsToPlayers(PlayerManager.localPlayers);
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);
PlayerManager.LocalMatchJoinPlayers(MatchManager.ArenaProperties.minPlayerCount);
foreach (Player p in PlayerManager.localPlayers)
{
CharacterManager.AssignShipFixed(p);
Log.Debug($"Ship: {p.character.name} assigned to player: {p.playerNumber}.");
}
MatchManager.StartCharacterSelect();
MatchManager.SpawnCharacters(PlayerManager.localPlayers);
UIManager.StartManagingMatchUI();
UIManager.AssignHUDElementsToPlayers(PlayerManager.localPlayers);
ControlsManager.AssignControlsToPlayers(PlayerManager.localPlayers);
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; }
}
}