diff --git a/Assets/Scenes/Startup.unity b/Assets/Scenes/Startup.unity
index 208e4e9..ff9f072 100644
--- a/Assets/Scenes/Startup.unity
+++ b/Assets/Scenes/Startup.unity
@@ -160,6 +160,7 @@ MonoBehaviour:
_scenePath: Assets/Scenes/Match.unity
_transition: {fileID: 686083401}
_inputManager: {fileID: 1379932018}
+ _transitionDuration: 0.5
--- !u!4 &437329616
Transform:
m_ObjectHideFlags: 0
@@ -353,6 +354,11 @@ PrefabInstance:
propertyPath: m_Name
value: Transition Canvas
objectReference: {fileID: 0}
+ - target: {fileID: 7593389556534962451, guid: 8c44d27ccf79c4f37a77e8dc1c3e8e2e,
+ type: 3}
+ propertyPath: m_Enabled
+ value: 0
+ objectReference: {fileID: 0}
- target: {fileID: 7593389556534962460, guid: 8c44d27ccf79c4f37a77e8dc1c3e8e2e,
type: 3}
propertyPath: m_Pivot.x
diff --git a/Assets/Scripts/Managers/GameManager.cs b/Assets/Scripts/Managers/GameManager.cs
index 520e8fe..b84fbb6 100644
--- a/Assets/Scripts/Managers/GameManager.cs
+++ b/Assets/Scripts/Managers/GameManager.cs
@@ -39,6 +39,10 @@ private enum GamePart
[SerializeField] private SceneReference _matchScene;
[SerializeField] private CanvasFader _transition;
[SerializeField] private InputManager _inputManager;
+ ///
+ /// The duration of <see cref="UI.Screen"/> transitions in the UI.
+ ///
+ [SerializeField, Range(0.1f, 5f)] private float _transitionDuration;
#endregion
@@ -47,11 +51,6 @@ private enum GamePart
private MenuManager _menuManager;
private MatchManager _matchManager;
- ///
- /// The duration of transitions in the UI.
- ///
- private const float TransitionDuration = 1f;
-
///
/// The currently loaded scene.
///
@@ -82,7 +81,8 @@ private async void Start()
{
try
{
- await LoadMenuAsync();
+ Input.backButtonLeavesApp = true;
+ await LoadMenuAsync(false);
_inputManager.OnReturn += HandleReturn;
}
catch (OperationCanceledException)
@@ -96,6 +96,11 @@ private void OnDestroy()
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
_inputManager.OnReturn -= HandleReturn;
+ if (_menuManager)
+ {
+ _menuManager.OnReturnToMainMenu -= HandleReturnToMainMenu;
+ _menuManager.OnEnterMenu -= HandleEnterSubmenu;
+ }
}
#endregion
@@ -125,9 +130,12 @@ private async void HandleReturn()
await _menuManager.ReturnAsync(token);
break;
case GamePart.Match:
- var matchEnd = _matchManager.StopMatchAsync(TransitionDuration * 0.9f, token);
- var loadMenu = LoadMenuAsync();
+ var matchEnd = _matchManager.StopMatchAsync(_transitionDuration * 0.9f, token);
+ var loadMenu = LoadMenuAsync(true);
await UniTask.WhenAll(matchEnd, loadMenu);
+ // Wait for the loading to set this to true, otherwise the event system might pick up the
+ // back button press right away (within the same frame), effectively quitting the application.
+ Input.backButtonLeavesApp = true;
break;
default:
throw new NotImplementedException($"Game part not implemented: {_part}");
@@ -175,19 +183,42 @@ private async void HandleRestartMatch()
}
}
+ ///
+ /// Handles the event of entering s submenu.
+ ///
+ private void HandleEnterSubmenu()
+ {
+ Input.backButtonLeavesApp = false;
+ }
+
+ ///
+ /// Handles the event of going back to the main menu, from a submenu.
+ ///
+ private void HandleReturnToMainMenu()
+ {
+ Input.backButtonLeavesApp = true;
+ }
+
#endregion
#region Private
- ///
+ ///
/// Loads the menu scene.
///
+ /// Whether the transition animation should be played when loading the menu.
/// A task to be awaited which represents the loading.
- private async UniTask LoadMenuAsync()
+ private async UniTask LoadMenuAsync(bool transition)
{
+ if (transition)
+ await StartTransitionAsync();
_menuManager = await LoadManagedSceneAsync(_menuScene);
_menuManager.OnStartMatch += HandleStartMatch;
+ _menuManager.OnReturnToMainMenu += HandleReturnToMainMenu;
+ _menuManager.OnEnterMenu += HandleEnterSubmenu;
_part = GamePart.Menu;
+ if (transition)
+ await EndTransitionAsync();
}
///
@@ -196,26 +227,43 @@ private async UniTask LoadMenuAsync()
/// The settings of the match to be started.
private async UniTask StartMatch(MatchSettings settings)
{
+ await StartTransitionAsync();
_matchManager = await LoadManagedSceneAsync(_matchScene);
_matchManager.OnLeaveRequest += HandleReturn;
_matchManager.OnRestartRequest += HandleRestartMatch;
_part = GamePart.Match;
+ await EndTransitionAsync();
await _matchManager.StartMatchAsync(settings, _cancellationTokenSource.Token);
}
+ ///
+ /// Starts a scene transition.
+ ///
+ private async UniTask StartTransitionAsync()
+ {
+ _loading = true;
+ await _transition.FadeInAsync(_transitionDuration, _cancellationTokenSource.Token);
+ }
+
+ ///
+ /// End a scene transition.
+ ///
+ private async UniTask EndTransitionAsync()
+ {
+ await _transition.FadeOutAsync(_transitionDuration, _cancellationTokenSource.Token);
+ _loading = false;
+ }
+
///
/// Loads a scene that contains a manager asynchronously.
///
/// The scene to be loaded.
/// The type of the manager to be fetched in the scene.
/// A task to be awaited which represents the loading. Its value is the scene's manager.
- /// Thrown if the given does not contain a manager of type
+ /// Thrown if the given wasn't loaded.
/// The type of the manager in the scene.
private async UniTask LoadManagedSceneAsync(SceneReference scene) where T : MonoBehaviour
{
- _loading = true;
- var token = _cancellationTokenSource.Token;
- await _transition.FadeInAsync(TransitionDuration, token);
if (_scene != null) // In some cases (e.g. leading menu), there is nothing to unload.
await SceneManager.UnloadSceneAsync(_scene.Value);
await SceneManager.LoadSceneAsync(scene, LoadSceneMode.Additive);
@@ -224,11 +272,7 @@ private async UniTask LoadManagedSceneAsync(SceneReference scene) where T
throw new Exception($"Managed scene wasn't loaded ({typeof(T)}).");
SceneManager.SetActiveScene(_scene.Value);
- var manager = FindAnyObjectByType();
- await _transition.FadeOutAsync(TransitionDuration, token);
- _loading = false;
-
- return manager;
+ return FindAnyObjectByType();
}
#endregion
diff --git a/Assets/Scripts/Menu/MenuManager.cs b/Assets/Scripts/Menu/MenuManager.cs
index 9d9962d..e9e49a5 100644
--- a/Assets/Scripts/Menu/MenuManager.cs
+++ b/Assets/Scripts/Menu/MenuManager.cs
@@ -22,6 +22,16 @@ internal class MenuManager : MonoBehaviour
///
internal event Action OnStartMatch;
+ ///
+ /// Invoked whenever the application leaves a submenu and goes back to the main menu.
+ ///
+ internal event Action OnEnterMenu;
+
+ ///
+ /// Invoked whenever the application goes to a submenu.
+ ///
+ internal event Action OnReturnToMainMenu;
+
#endregion
#region Serialized fields
@@ -38,6 +48,7 @@ internal class MenuManager : MonoBehaviour
#endregion
private readonly CancellationTokenSource _cancellationTokenSource = new();
+
#region Fields
private Screen _currentScreen;
@@ -78,6 +89,7 @@ private void OnDestroy()
private async void HandleSelectNewMatch()
{
+ OnEnterMenu?.Invoke();
try
{
await TransitionToAsync(_playScreen);
@@ -90,6 +102,7 @@ private async void HandleSelectNewMatch()
private async void HandleSelectSettings()
{
+ OnEnterMenu?.Invoke();
try
{
await TransitionToAsync(_settingsScreen);
@@ -102,6 +115,7 @@ private async void HandleSelectSettings()
private async void HandleSelectCredits()
{
+ OnEnterMenu?.Invoke();
try
{
await TransitionToAsync(_creditsScreen);
@@ -110,7 +124,6 @@ private async void HandleSelectCredits()
{
Debug.Log("Credits selection handling stopped because the operation was cancelled.");
}
-
}
private void StartMatch(MatchSettings settings)
@@ -147,14 +160,14 @@ internal async UniTask ReturnAsync(CancellationToken token)
private async UniTask ReturnMenuAsync(CancellationToken token)
{
+ if (_currentScreen == null)
+ return;
+
await _transition.FadeInAsync(_transitionDuration / 2f, token);
-
- if (_currentScreen != null)
- _currentScreen.Hide();
-
+ _currentScreen.Hide();
_currentScreen = null;
-
await _transition.FadeOutAsync(_transitionDuration / 2f, token);
+ OnReturnToMainMenu?.Invoke();
}
///
diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset
index f3f3fb7..296935f 100644
--- a/ProjectSettings/ProjectSettings.asset
+++ b/ProjectSettings/ProjectSettings.asset
@@ -135,7 +135,7 @@ PlayerSettings:
vulkanEnableLateAcquireNextImage: 0
vulkanEnableCommandBufferRecycling: 1
loadStoreDebugModeEnabled: 0
- bundleVersion: 1.1.0
+ bundleVersion: 1.1.1
preloadedAssets: []
metroInputSource: 0
wsaTransparentSwapchain: 0
@@ -163,7 +163,7 @@ PlayerSettings:
iPhone: 0
tvOS: 0
overrideDefaultApplicationIdentifier: 1
- AndroidBundleVersionCode: 8
+ AndroidBundleVersionCode: 9
AndroidMinSdkVersion: 22
AndroidTargetSdkVersion: 33
AndroidPreferredInstallLocation: 1