232 lines
6.7 KiB
C#
Raw Normal View History

2026-05-06 15:07:56 +02:00
using UnityEngine;
using DuloGames.UI.Tweens;
namespace DuloGames.UI
{
/// <summary>
/// The black overlay used behind windows such as the game menu.
/// </summary>
[ExecuteInEditMode, RequireComponent(typeof(CanvasGroup))]
public class UIBlackOverlay : MonoBehaviour {
private CanvasGroup m_CanvasGroup;
private int m_WindowsCount = 0;
private bool m_Transitioning = false;
// Tween controls
[System.NonSerialized]
private readonly TweenRunner<FloatTween> m_FloatTweenRunner;
// Called by Unity prior to deserialization,
// should not be called by users
protected UIBlackOverlay()
{
if (this.m_FloatTweenRunner == null)
this.m_FloatTweenRunner = new TweenRunner<FloatTween>();
this.m_FloatTweenRunner.Init(this);
}
protected void Awake()
{
this.m_CanvasGroup = this.gameObject.GetComponent<CanvasGroup>();
this.m_CanvasGroup.interactable = false;
this.m_CanvasGroup.blocksRaycasts = false;
}
protected void Start()
{
// Non interactable
this.m_CanvasGroup.interactable = false;
// Hide the overlay
this.Hide();
}
protected void OnEnable()
{
// Hide the overlay
if (!Application.isPlaying)
this.Hide();
}
/// <summary>
/// Instantly show the overlay.
/// </summary>
public void Show()
{
// Show the overlay
this.SetAlpha(1f);
// Toggle block raycast on
this.m_CanvasGroup.blocksRaycasts = true;
}
/// <summary>
/// Instantly hide the overlay.
/// </summary>
public void Hide()
{
// Hide the overlay
this.SetAlpha(0f);
// Toggle block raycast off
this.m_CanvasGroup.blocksRaycasts = false;
}
/// <summary>
/// If the overlay is enabled and active in the hierarchy.
/// </summary>
/// <returns></returns>
public bool IsActive()
{
return (this.enabled && this.gameObject.activeInHierarchy);
}
/// <summary>
/// If the overlay is visible at all (alpha > 0f).
/// </summary>
/// <returns></returns>
public bool IsVisible()
{
return (this.m_CanvasGroup.alpha > 0f);
}
/// <summary>
/// This method is hooked to the window on transition begin event.
/// </summary>
/// <param name="window">The window.</param>
/// <param name="state">The window visual state that we are transitioning to.</param>
/// <param name="instant">If the transition is instant or not.</param>
public void OnTransitionBegin(UIWindow window, UIWindow.VisualState state, bool instant)
{
if (!this.IsActive() || window == null)
return;
// Check if we are receiving hide event and we are not showing the overlay to begin with, return
if (state == UIWindow.VisualState.Hidden && !this.IsVisible())
return;
// Prepare transition duration
float duration = (instant) ? 0f : window.transitionDuration;
TweenEasing easing = window.transitionEasing;
// Showing a window
if (state == UIWindow.VisualState.Shown)
{
// Increase the window count so we know when to hide the overlay
this.m_WindowsCount += 1;
// Check if the overlay is already visible
if (this.IsVisible() && !this.m_Transitioning)
{
// Bring the window forward
UIUtility.BringToFront(window.gameObject);
// Break
return;
}
// Bring the overlay forward
UIUtility.BringToFront(this.gameObject);
// Bring the window forward
UIUtility.BringToFront(window.gameObject);
// Transition
this.StartAlphaTween(1f, duration, easing);
// Toggle block raycast on
this.m_CanvasGroup.blocksRaycasts = true;
}
// Hiding a window
else
{
// Decrease the window count
this.m_WindowsCount -= 1;
// Never go below 0
if (this.m_WindowsCount < 0)
this.m_WindowsCount = 0;
// Check if we still have windows using the overlay
if (this.m_WindowsCount > 0)
return;
// Transition
this.StartAlphaTween(0f, duration, easing);
// Toggle block raycast on
this.m_CanvasGroup.blocksRaycasts = false;
}
}
private void StartAlphaTween(float targetAlpha, float duration, TweenEasing easing)
{
if (this.m_CanvasGroup == null)
return;
// Check if currently transitioning
if (this.m_Transitioning)
{
this.m_FloatTweenRunner.StopTween();
}
if (duration == 0f || !Application.isPlaying)
{
this.SetAlpha(targetAlpha);
}
else
{
this.m_Transitioning = true;
var floatTween = new FloatTween { duration = duration, startFloat = this.m_CanvasGroup.alpha, targetFloat = targetAlpha };
floatTween.AddOnChangedCallback(SetAlpha);
floatTween.ignoreTimeScale = true;
floatTween.easing = easing;
floatTween.AddOnFinishCallback(OnTweenFinished);
this.m_FloatTweenRunner.StartTween(floatTween);
}
}
public void SetAlpha(float alpha)
{
if (this.m_CanvasGroup != null)
this.m_CanvasGroup.alpha = alpha;
}
protected void OnTweenFinished()
{
this.m_Transitioning = false;
}
/// <summary>
/// Gets the black overlay based on relative game object. (In case we have multiple canvases.)
/// </summary>
/// <param name="relativeGameObject">The relative game object.</param>
/// <returns>The black overlay component.</returns>
public static UIBlackOverlay GetOverlay(GameObject relativeGameObject)
{
// Find the black overlay in the current canvas
Canvas canvas = UIUtility.FindInParents<Canvas>(relativeGameObject);
if (canvas != null)
{
// Try finding an overlay in the canvas
UIBlackOverlay overlay = canvas.gameObject.GetComponentInChildren<UIBlackOverlay>();
if (overlay != null)
return overlay;
// In case no overlay is found instantiate one
if (UIBlackOverlayManager.Instance != null && UIBlackOverlayManager.Instance.prefab != null)
return UIBlackOverlayManager.Instance.Create(canvas.transform);
}
return null;
}
}
}