Files
Master-Arbeit-Tom-Hempel/Unity-Master/Assets/Convai/Scripts/Runtime/Addons/NotificationSystem/UINotificationController.cs
2025-09-21 22:42:26 +02:00

317 lines
14 KiB
C#

using System.Collections;
using System.Collections.Generic;
using Convai.Scripts.Runtime.UI;
using UnityEngine;
namespace Convai.Scripts.Runtime.Addons
{
/// <summary>
/// This class is responsible for controlling the UI notifications in the game.
/// It handles the creation, activation, deactivation, and animation of notifications.
/// </summary>
public class UINotificationController : MonoBehaviour
{
/// <summary>
/// Maximum number of notifications that can be displayed at the same time.
/// </summary>
private const int MAX_NUMBER_OF_NOTIFICATION_AT_SAME_TIME = 3;
/// <summary>
/// References to the UI notification prefab and other necessary components.
/// </summary>
[Header("References")] [SerializeField]
private UINotification _uiNotificationPrefab;
/// <summary>
/// Spacing between Notifications
/// </summary>
[Header("Configurations")] [SerializeField]
private int _spacing = 100;
/// <summary>
/// Position for Active Notification
/// </summary>
[Tooltip("Starting position for the first notification; Y value adjusts sequentially for each subsequent notification.")] [SerializeField]
private Vector2 _activeNotificationPos;
/// <summary>
/// Position for Deactivated Notification
/// </summary>
[SerializeField] private Vector2 _deactivatedNotificationPos;
[Header("UI Notification Animation Values")] [SerializeField]
private float _activeDuration = 4f;
[SerializeField] private float _slipDuration = 0.3f;
[SerializeField] private float _delay = 0.3f;
[SerializeField] private AnimationCurve _slipAnimationCurve;
private readonly float _fadeInDuration = 0.35f;
private readonly float _fadeOutDuration = 0.2f;
/// <summary>
/// List to keep track of the order in which pending notifications were requested.
/// </summary>
private readonly List<SONotification> _pendingNotificationsOrder = new();
private Queue<UINotification> _activeUINotifications;
private CanvasGroup _canvasGroup;
private Queue<UINotification> _deactivatedUINotifications;
private FadeCanvas _fadeCanvas;
/// <summary>
/// Flag indicating whether a UI notification movement animation is currently in progress.
/// Used to prevent overlapping animation coroutines for UI notifications.
/// </summary>
private bool _isNotificationAnimationInProgress;
/// <summary>
/// Awake is called when the script instance is being loaded.
/// It is used to initialize any variables or game state before the game starts.
/// </summary>
private void Awake()
{
// Get necessary components and initialize UI notifications.
_canvasGroup = GetComponent<CanvasGroup>();
_fadeCanvas = GetComponent<FadeCanvas>();
InitializeUINotifications();
}
/// <summary>
/// This function is called when the object becomes enabled and active.
/// It is used to subscribe to the OnNotificationRequested event.
/// </summary>
private void OnEnable()
{
NotificationSystemHandler.Instance.OnNotificationRequested += NotificationSystemHandler_OnNotificationRequested;
}
/// <summary>
/// This function is called when the behaviour becomes disabled or inactive.
/// It is used to unsubscribe from the OnNotificationRequested event.
/// </summary>
private void OnDisable()
{
NotificationSystemHandler.Instance.OnNotificationRequested -= NotificationSystemHandler_OnNotificationRequested;
}
/// <summary>
/// Handles a new notification request by adding it to the order list and attempting to initialize it.
/// If a notification animation is already in progress, waits for it to complete before processing the new request.
/// </summary>
/// <param name="SONotification">The requested SONotification to be processed.</param>
private void NotificationSystemHandler_OnNotificationRequested(SONotification SONotification)
{
// Add the requested notification to the order list and try to initialize it.
_pendingNotificationsOrder.Add(SONotification);
// If a notification animation is already in progress, wait for it to complete before processing the new request.
if (_isNotificationAnimationInProgress) return;
// If initialization fails, return
if (TryInitializeNewNotification(SONotification, out UINotification uiNotification) == false) return;
// Start the coroutine for UI notification animations
StartNotificationUICoroutine(uiNotification);
}
/// <summary>
/// This function is used to initialize the UI notifications.
/// It initializes the queues for active and deactivated UI notifications and instantiates and enqueues deactivated UI
/// notifications.
/// </summary>
private void InitializeUINotifications()
{
// Initialize the queues for active and deactivated UI notifications.
_activeUINotifications = new Queue<UINotification>();
_deactivatedUINotifications = new Queue<UINotification>();
// Instantiate and enqueue deactivated UI notifications.
for (int i = 0; i < MAX_NUMBER_OF_NOTIFICATION_AT_SAME_TIME; i++)
{
UINotification uiNotification = Instantiate(_uiNotificationPrefab, transform);
// Initialize Position
uiNotification.NotificationRectTransform.anchoredPosition = _deactivatedNotificationPos;
_deactivatedUINotifications.Enqueue(uiNotification);
}
}
/// <summary>
/// Attempts to initialize a new UI notification using the provided SONotification.
/// Tries to get an available UI notification and initializes it with the given SONotification.
/// </summary>
/// <param name="SONotification">The SONotification to be used for initializing the UI notification.</param>
/// <param name="uiNotification">The initialized UINotification if successful, otherwise null.</param>
/// <returns>True if initialization is successful, false otherwise.</returns>
private bool TryInitializeNewNotification(SONotification SONotification, out UINotification uiNotification)
{
// Try to get an available UI notification and initialize it with the given SONotification.
uiNotification = GetAvailableUINotification();
if (uiNotification == null) return false;
uiNotification.Initialize(SONotification);
return true;
}
/// <summary>
/// Initiates the coroutine for UI notification animations and adds the notification to the active queue.
/// </summary>
/// <param name="uiNotification">The UINotification to be animated and added to the active queue.</param>
private void StartNotificationUICoroutine(UINotification uiNotification)
{
// Define additional delay for smoother notification end transition
float extraDelayForNotificationEndTransition = 0.5f;
// Calculate the total duration including fadeIn, activeDuration, slipDuration (for both activation and deactivation), delay, and extra delay
float totalAnimationDuration = _fadeInDuration + _activeDuration + 2 * _slipDuration + _delay + extraDelayForNotificationEndTransition;
// Start the fade animation for the canvas group
_fadeCanvas.StartFadeInFadeOutWithGap(_canvasGroup, _fadeInDuration, _fadeOutDuration, totalAnimationDuration);
// Enqueue the notification to the active queue
_activeUINotifications.Enqueue(uiNotification);
// Start the coroutine for individual UI notification animations
StartCoroutine(StartNotificationUI(uiNotification));
}
/// <summary>
/// Coroutine for managing the lifecycle of a UI notification, including its activation, display duration, and
/// deactivation.
/// </summary>
/// <param name="uiNotification">The UINotification to be managed.</param>
private IEnumerator StartNotificationUI(UINotification uiNotification)
{
// Remove the notification from the pending list
int firstIndex = 0;
_pendingNotificationsOrder.RemoveAt(firstIndex);
// Move to the active position
yield return MoveUINotificationToActivePosition(uiNotification);
// Wait for the active duration
yield return new WaitForSeconds(_activeDuration);
UpdateUINotificationPositions();
// Move to the hidden position
yield return MoveUINotificationToHiddenPosition(uiNotification);
// Deactivate the UI notification, update positions, and check for pending notifications.
DeactivateAndEnqueueUINotification(uiNotification);
// If there are pending notifications, initialize and start a new one
if (AreTherePendingNotifications()) TryInitializeAndStartNewNotification();
// Update UI notification positions after the lifecycle is complete
UpdateUINotificationPositions();
}
/// <summary>
/// Moves the UI notification to its active position.
/// </summary>
private IEnumerator MoveUINotificationToActivePosition(UINotification uiNotification)
{
float targetY = _activeNotificationPos.y - _spacing * (_activeUINotifications.Count - 1);
Vector2 targetPos = new(_activeNotificationPos.x, targetY);
yield return StartCoroutine(MoveUINotificationToTargetPos(uiNotification, targetPos));
}
/// <summary>
/// Moves the UI notification to its hidden position.
/// </summary>
private IEnumerator MoveUINotificationToHiddenPosition(UINotification uiNotification)
{
Vector2 targetPos = _deactivatedNotificationPos;
yield return StartCoroutine(MoveUINotificationToTargetPos(uiNotification, targetPos));
}
/// <summary>
/// Deactivates the UI notification, updates positions, and enqueues it for later use.
/// </summary>
private void DeactivateAndEnqueueUINotification(UINotification uiNotification)
{
uiNotification.SetActive(false);
_activeUINotifications.Dequeue();
_deactivatedUINotifications.Enqueue(uiNotification);
UpdateUINotificationPositions();
}
/// <summary>
/// Checks if there are pending notifications and initializes and starts a new one if available.
/// </summary>
private void TryInitializeAndStartNewNotification()
{
if (TryInitializeNewNotification(_pendingNotificationsOrder[0], out UINotification newUiNotification)) StartNotificationUICoroutine(newUiNotification);
}
/// <summary>
/// Smoothly moves the UI notification to the target position over a specified duration.
/// </summary>
/// <param name="uiNotification">The UINotification to be moved.</param>
/// <param name="targetPos">The target position to move the UINotification to.</param>
private IEnumerator MoveUINotificationToTargetPos(UINotification uiNotification, Vector2 targetPos)
{
// Set flag to indicate that a notification animation is in progress
_isNotificationAnimationInProgress = true;
float elapsedTime = 0f;
Vector2 startPos = uiNotification.NotificationRectTransform.anchoredPosition;
// Move the UI notification smoothly to the target position over the specified duration
while (elapsedTime <= _slipDuration + _delay)
{
elapsedTime += Time.deltaTime;
float percent = Mathf.Clamp01(elapsedTime / _slipDuration);
float curvePercent = _slipAnimationCurve.Evaluate(percent);
uiNotification.NotificationRectTransform.anchoredPosition = Vector2.Lerp(startPos, targetPos, curvePercent);
yield return null;
}
// Reset the flag once the animation is complete
_isNotificationAnimationInProgress = false;
}
/// <summary>
/// Updates the positions of active UI notifications along the Y-axis.
/// </summary>
private void UpdateUINotificationPositions()
{
float targetX = _activeNotificationPos.x;
float targetY = _activeNotificationPos.y;
// Iterate through active UI notifications and move them to their respective positions
foreach (UINotification activeUINotification in _activeUINotifications)
{
Vector2 targetPos = new(targetX, targetY);
StartCoroutine(MoveUINotificationToTargetPos(activeUINotification, targetPos));
targetY -= _spacing;
}
}
/// <summary>
/// Gets an available UI notification from the deactivated queue.
/// </summary>
/// <returns>The available UI notification, or null if the deactivated queue is empty.</returns>
private UINotification GetAvailableUINotification()
{
// Check if there are available deactivated UI notifications
if (_deactivatedUINotifications.Count == 0) return null;
// Dequeue and return an available UI notification
return _deactivatedUINotifications.Dequeue();
}
/// <summary>
/// Checks if there are pending notifications in the order list.
/// </summary>
/// <returns>True if there are pending notifications, false otherwise.</returns>
private bool AreTherePendingNotifications()
{
// Check if there are any pending notifications in the order list
return _pendingNotificationsOrder.Count >= 1;
}
}
}