507 lines
16 KiB
C#
507 lines
16 KiB
C#
using System;
|
|
using System.Collections;
|
|
using DG.Tweening;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.UI;
|
|
using Object = UnityEngine.Object;
|
|
|
|
namespace Bansonic
|
|
{
|
|
public static class gTransition
|
|
{
|
|
private const string PrefabResourcePath = "gTransitionPrefab";
|
|
|
|
public static bool IsBusy
|
|
{
|
|
get
|
|
{
|
|
BansonicSceneTransitionOverlay instance = BansonicSceneTransitionOverlay.EnsureInstance(PrefabResourcePath);
|
|
return instance != null && instance.IsBusy;
|
|
}
|
|
}
|
|
|
|
public static void FadeOut(float duration = 0.25f)
|
|
{
|
|
BansonicSceneTransitionOverlay instance = BansonicSceneTransitionOverlay.EnsureInstance(PrefabResourcePath);
|
|
if (instance == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
instance.FadeOut(duration);
|
|
}
|
|
|
|
public static void FadeIn(float duration = 0.25f)
|
|
{
|
|
BansonicSceneTransitionOverlay instance = BansonicSceneTransitionOverlay.EnsureInstance(PrefabResourcePath);
|
|
if (instance == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
instance.FadeIn(duration);
|
|
}
|
|
|
|
public static bool LoadScene(
|
|
string sceneName,
|
|
LoadSceneMode mode = LoadSceneMode.Single,
|
|
float fadeOutDuration = 0.25f,
|
|
float fadeInDuration = 0.25f,
|
|
float holdBlackDuration = 0f,
|
|
Action onComplete = null)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(sceneName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
BansonicSceneTransitionOverlay instance = BansonicSceneTransitionOverlay.EnsureInstance(PrefabResourcePath);
|
|
if (instance == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return instance.BeginLoadScene(sceneName, mode, fadeOutDuration, fadeInDuration, holdBlackDuration, onComplete);
|
|
}
|
|
|
|
public static bool ReloadCurrentScene(
|
|
float fadeOutDuration = 0.25f,
|
|
float fadeInDuration = 0.25f,
|
|
float holdBlackDuration = 0f,
|
|
Action onComplete = null)
|
|
{
|
|
Scene activeScene = SceneManager.GetActiveScene();
|
|
if (!activeScene.IsValid() || string.IsNullOrWhiteSpace(activeScene.name))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return LoadScene(activeScene.name, LoadSceneMode.Single, fadeOutDuration, fadeInDuration, holdBlackDuration, onComplete);
|
|
}
|
|
|
|
public static bool Run(
|
|
IEnumerator routine,
|
|
float fadeOutDuration = 0.25f,
|
|
float fadeInDuration = 0.25f,
|
|
float holdBlackDuration = 0f,
|
|
Action onComplete = null)
|
|
{
|
|
if (routine == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
BansonicSceneTransitionOverlay instance = BansonicSceneTransitionOverlay.EnsureInstance(PrefabResourcePath);
|
|
if (instance == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return instance.BeginWrappedRoutine(routine, fadeOutDuration, fadeInDuration, holdBlackDuration, onComplete);
|
|
}
|
|
}
|
|
|
|
public sealed class BansonicSceneTransitionOverlay : MonoBehaviour
|
|
{
|
|
private const string RuntimeObjectName = "BansonicSceneTransitionOverlay_Runtime";
|
|
|
|
[SerializeField] private gTransBlack transitionBlack;
|
|
[SerializeField] private CanvasGroup canvasGroup;
|
|
[SerializeField] private Image blackoutImage;
|
|
|
|
private Tween currentTween;
|
|
private Coroutine transitionRoutine;
|
|
|
|
public bool IsBusy
|
|
{
|
|
get { return transitionRoutine != null; }
|
|
}
|
|
|
|
public static BansonicSceneTransitionOverlay EnsureInstance(string prefabResourcePath)
|
|
{
|
|
BansonicSceneTransitionOverlay existing = FindExisting();
|
|
if (existing != null)
|
|
{
|
|
existing.EnsureSetup();
|
|
return existing;
|
|
}
|
|
|
|
GameObject prefab = Resources.Load<GameObject>(prefabResourcePath);
|
|
if (prefab != null)
|
|
{
|
|
GameObject prefabInstance = Object.Instantiate(prefab);
|
|
prefabInstance.name = prefab.name;
|
|
Object.DontDestroyOnLoad(prefabInstance);
|
|
BansonicSceneTransitionOverlay overlayFromPrefab = prefabInstance.GetComponent<BansonicSceneTransitionOverlay>();
|
|
if (overlayFromPrefab == null)
|
|
{
|
|
overlayFromPrefab = prefabInstance.AddComponent<BansonicSceneTransitionOverlay>();
|
|
}
|
|
if (overlayFromPrefab != null)
|
|
{
|
|
overlayFromPrefab.EnsureSetup();
|
|
}
|
|
return overlayFromPrefab;
|
|
}
|
|
|
|
return CreateRuntimeInstance();
|
|
}
|
|
|
|
public void FadeOut(float duration)
|
|
{
|
|
EnsureSetup();
|
|
StartSingleTransition(true, duration);
|
|
}
|
|
|
|
public void FadeIn(float duration)
|
|
{
|
|
EnsureSetup();
|
|
StartSingleTransition(false, duration);
|
|
}
|
|
|
|
public bool BeginLoadScene(
|
|
string sceneName,
|
|
LoadSceneMode mode,
|
|
float fadeOutDuration,
|
|
float fadeInDuration,
|
|
float holdBlackDuration,
|
|
Action onComplete)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(sceneName) || IsBusy)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
transitionRoutine = StartCoroutine(LoadSceneRoutine(sceneName, mode, fadeOutDuration, fadeInDuration, holdBlackDuration, onComplete));
|
|
return true;
|
|
}
|
|
|
|
public bool BeginWrappedRoutine(
|
|
IEnumerator routine,
|
|
float fadeOutDuration,
|
|
float fadeInDuration,
|
|
float holdBlackDuration,
|
|
Action onComplete)
|
|
{
|
|
if (routine == null || IsBusy)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
transitionRoutine = StartCoroutine(WrappedRoutine(routine, fadeOutDuration, fadeInDuration, holdBlackDuration, onComplete));
|
|
return true;
|
|
}
|
|
|
|
private static BansonicSceneTransitionOverlay FindExisting()
|
|
{
|
|
BansonicSceneTransitionOverlay[] all = FindObjectsByType<BansonicSceneTransitionOverlay>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
|
return all != null && all.Length > 0 ? all[0] : null;
|
|
}
|
|
|
|
private static BansonicSceneTransitionOverlay CreateRuntimeInstance()
|
|
{
|
|
GameObject root = new GameObject(
|
|
RuntimeObjectName,
|
|
typeof(RectTransform),
|
|
typeof(Canvas),
|
|
typeof(CanvasScaler),
|
|
typeof(GraphicRaycaster),
|
|
typeof(CanvasGroup),
|
|
typeof(Image),
|
|
typeof(BansonicSceneTransitionOverlay));
|
|
|
|
RectTransform rect = root.GetComponent<RectTransform>();
|
|
rect.anchorMin = Vector2.zero;
|
|
rect.anchorMax = Vector2.one;
|
|
rect.offsetMin = Vector2.zero;
|
|
rect.offsetMax = Vector2.zero;
|
|
rect.pivot = new Vector2(0.5f, 0.5f);
|
|
|
|
Canvas canvas = root.GetComponent<Canvas>();
|
|
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
canvas.sortingOrder = short.MaxValue;
|
|
canvas.pixelPerfect = false;
|
|
|
|
CanvasScaler scaler = root.GetComponent<CanvasScaler>();
|
|
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
|
scaler.referenceResolution = new Vector2(1920f, 1080f);
|
|
scaler.matchWidthOrHeight = 0.5f;
|
|
|
|
Image image = root.GetComponent<Image>();
|
|
image.color = Color.black;
|
|
image.raycastTarget = true;
|
|
|
|
CanvasGroup group = root.GetComponent<CanvasGroup>();
|
|
group.alpha = 0f;
|
|
group.interactable = false;
|
|
group.blocksRaycasts = false;
|
|
|
|
Object.DontDestroyOnLoad(root);
|
|
|
|
BansonicSceneTransitionOverlay overlay = root.GetComponent<BansonicSceneTransitionOverlay>();
|
|
overlay.canvasGroup = group;
|
|
overlay.blackoutImage = image;
|
|
overlay.EnsureSetup();
|
|
return overlay;
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
EnsureSetup();
|
|
Object.DontDestroyOnLoad(gameObject);
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
KillTween();
|
|
}
|
|
|
|
private void EnsureSetup()
|
|
{
|
|
if (transitionBlack == null)
|
|
{
|
|
transitionBlack = GetComponent<gTransBlack>();
|
|
if (transitionBlack == null)
|
|
{
|
|
transitionBlack = GetComponentInChildren<gTransBlack>(true);
|
|
}
|
|
}
|
|
|
|
if (canvasGroup == null)
|
|
{
|
|
if (transitionBlack != null && transitionBlack.CanvasGroup != null)
|
|
{
|
|
canvasGroup = transitionBlack.CanvasGroup;
|
|
}
|
|
else
|
|
{
|
|
canvasGroup = GetComponent<CanvasGroup>();
|
|
if (canvasGroup == null)
|
|
{
|
|
canvasGroup = GetComponentInChildren<CanvasGroup>(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (blackoutImage == null)
|
|
{
|
|
blackoutImage = GetComponent<Image>();
|
|
if (blackoutImage == null)
|
|
{
|
|
blackoutImage = GetComponentInChildren<Image>(true);
|
|
}
|
|
}
|
|
|
|
Canvas canvas = transitionBlack != null && transitionBlack.Canvas != null
|
|
? transitionBlack.Canvas
|
|
: GetComponentInChildren<Canvas>(true);
|
|
if (canvas != null)
|
|
{
|
|
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
|
{
|
|
canvas.sortingOrder = short.MaxValue;
|
|
}
|
|
}
|
|
|
|
if (transitionBlack != null)
|
|
{
|
|
transitionBlack.RefreshCanvasCamera();
|
|
}
|
|
|
|
if (blackoutImage != null)
|
|
{
|
|
blackoutImage.color = Color.black;
|
|
blackoutImage.raycastTarget = true;
|
|
}
|
|
|
|
if (canvasGroup != null)
|
|
{
|
|
canvasGroup.alpha = Mathf.Clamp01(canvasGroup.alpha);
|
|
if (!IsBusy)
|
|
{
|
|
canvasGroup.alpha = 0f;
|
|
}
|
|
|
|
if (canvasGroup.alpha <= 0f)
|
|
{
|
|
canvasGroup.interactable = false;
|
|
canvasGroup.blocksRaycasts = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void StartSingleTransition(bool fadeToBlack, float duration)
|
|
{
|
|
if (transitionRoutine != null)
|
|
{
|
|
StopCoroutine(transitionRoutine);
|
|
transitionRoutine = null;
|
|
}
|
|
|
|
KillTween();
|
|
EnsureSetup();
|
|
gameObject.SetActive(true);
|
|
|
|
if (canvasGroup == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float safeDuration = Mathf.Max(0f, duration);
|
|
float targetAlpha = fadeToBlack ? 1f : 0f;
|
|
|
|
if (fadeToBlack)
|
|
{
|
|
canvasGroup.interactable = true;
|
|
canvasGroup.blocksRaycasts = true;
|
|
}
|
|
|
|
if (safeDuration <= 0f)
|
|
{
|
|
canvasGroup.alpha = targetAlpha;
|
|
if (!fadeToBlack)
|
|
{
|
|
canvasGroup.interactable = false;
|
|
canvasGroup.blocksRaycasts = false;
|
|
}
|
|
return;
|
|
}
|
|
|
|
currentTween = canvasGroup.DOFade(targetAlpha, safeDuration)
|
|
.SetEase(Ease.Linear)
|
|
.SetUpdate(true)
|
|
.OnComplete(() =>
|
|
{
|
|
if (canvasGroup == null)
|
|
{
|
|
currentTween = null;
|
|
return;
|
|
}
|
|
|
|
if (!fadeToBlack)
|
|
{
|
|
canvasGroup.interactable = false;
|
|
canvasGroup.blocksRaycasts = false;
|
|
}
|
|
|
|
currentTween = null;
|
|
});
|
|
}
|
|
|
|
private IEnumerator LoadSceneRoutine(
|
|
string sceneName,
|
|
LoadSceneMode mode,
|
|
float fadeOutDuration,
|
|
float fadeInDuration,
|
|
float holdBlackDuration,
|
|
Action onComplete)
|
|
{
|
|
yield return WrappedRoutine(LoadSceneOperation(sceneName, mode), fadeOutDuration, fadeInDuration, holdBlackDuration, onComplete);
|
|
transitionRoutine = null;
|
|
}
|
|
|
|
private IEnumerator WrappedRoutine(
|
|
IEnumerator routine,
|
|
float fadeOutDuration,
|
|
float fadeInDuration,
|
|
float holdBlackDuration,
|
|
Action onComplete)
|
|
{
|
|
EnsureSetup();
|
|
yield return FadeRoutine(1f, Mathf.Max(0f, fadeOutDuration), true);
|
|
|
|
if (holdBlackDuration > 0f)
|
|
{
|
|
yield return new WaitForSecondsRealtime(holdBlackDuration);
|
|
}
|
|
|
|
while (routine != null && routine.MoveNext())
|
|
{
|
|
yield return routine.Current;
|
|
}
|
|
|
|
yield return null;
|
|
yield return null;
|
|
|
|
if (transitionBlack != null)
|
|
{
|
|
transitionBlack.RefreshCanvasCamera();
|
|
}
|
|
|
|
Canvas.ForceUpdateCanvases();
|
|
|
|
yield return FadeRoutine(0f, Mathf.Max(0f, fadeInDuration), false);
|
|
|
|
onComplete?.Invoke();
|
|
transitionRoutine = null;
|
|
}
|
|
|
|
private IEnumerator LoadSceneOperation(string sceneName, LoadSceneMode mode)
|
|
{
|
|
AsyncOperation operation = SceneManager.LoadSceneAsync(sceneName, mode);
|
|
while (operation != null && !operation.isDone)
|
|
{
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
private IEnumerator FadeRoutine(float targetAlpha, float duration, bool blocking)
|
|
{
|
|
EnsureSetup();
|
|
gameObject.SetActive(true);
|
|
|
|
if (canvasGroup == null)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
KillTween();
|
|
|
|
canvasGroup.interactable = blocking;
|
|
canvasGroup.blocksRaycasts = blocking;
|
|
|
|
if (duration <= 0f)
|
|
{
|
|
canvasGroup.alpha = targetAlpha;
|
|
if (!blocking)
|
|
{
|
|
canvasGroup.interactable = false;
|
|
canvasGroup.blocksRaycasts = false;
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
bool completed = false;
|
|
currentTween = canvasGroup.DOFade(targetAlpha, duration)
|
|
.SetEase(Ease.Linear)
|
|
.SetUpdate(true)
|
|
.OnComplete(() =>
|
|
{
|
|
completed = true;
|
|
currentTween = null;
|
|
if (canvasGroup != null && !blocking)
|
|
{
|
|
canvasGroup.interactable = false;
|
|
canvasGroup.blocksRaycasts = false;
|
|
}
|
|
});
|
|
|
|
while (!completed)
|
|
{
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
private void KillTween()
|
|
{
|
|
if (currentTween != null && currentTween.IsActive())
|
|
{
|
|
currentTween.Kill(false);
|
|
}
|
|
|
|
currentTween = null;
|
|
}
|
|
}
|
|
}
|