First commit for private source control. Older commits available on Github.
This commit is contained in:
685
Assets/Scripts/Game/Cinematics/BaseCinematic.cs
Normal file
685
Assets/Scripts/Game/Cinematics/BaseCinematic.cs
Normal file
@@ -0,0 +1,685 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using BriarQueen.Framework.Coordinators.Events;
|
||||
using BriarQueen.Framework.Events.UI;
|
||||
using BriarQueen.Framework.Managers.Input;
|
||||
using BriarQueen.Game.Cinematics.Data;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using PrimeTween;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.UI;
|
||||
using VContainer;
|
||||
|
||||
namespace BriarQueen.Game.Cinematics
|
||||
{
|
||||
public abstract class BaseCinematic : MonoBehaviour
|
||||
{
|
||||
[Header("Cinematic Settings")]
|
||||
[SerializeField]
|
||||
protected List<CinematicPanel> _cinematicPanels = new();
|
||||
|
||||
[Header("Crossfade Settings")]
|
||||
[Tooltip("Max crossfade duration. Actual fade is clamped so EACH panel still lasts exactly DisplayTime.")]
|
||||
[SerializeField]
|
||||
[Min(0f)]
|
||||
protected float _maxCrossfadeDuration = 0.75f;
|
||||
|
||||
[Header("Subtitles")]
|
||||
[SerializeField]
|
||||
protected bool _logSubtitleTimingWarnings = true;
|
||||
|
||||
[Tooltip("If true, clears subtitle text when a panel ends (recommended for VO sync).")]
|
||||
[SerializeField]
|
||||
protected bool _clearSubtitlesBetweenPanels = true;
|
||||
|
||||
[Header("UI Elements")]
|
||||
[SerializeField]
|
||||
protected CanvasGroup _cinematicCanvasGroup;
|
||||
|
||||
[SerializeField]
|
||||
protected TextMeshProUGUI _cinematicText;
|
||||
|
||||
[SerializeField]
|
||||
protected TextMeshProUGUI _skipText;
|
||||
|
||||
[Header("Cinematic Images (Two Layers For Crossfade)")]
|
||||
[SerializeField]
|
||||
protected Image _imageA;
|
||||
|
||||
[SerializeField]
|
||||
protected Image _imageB;
|
||||
|
||||
[Header("Input")]
|
||||
[SerializeField]
|
||||
protected string _skipActionName = "Cancel";
|
||||
|
||||
[Header("Tween Settings")]
|
||||
[SerializeField]
|
||||
protected float _canvasFadeInDuration = 0.25f;
|
||||
|
||||
[SerializeField]
|
||||
protected float _canvasFadeOutDuration = 0.25f;
|
||||
|
||||
[SerializeField]
|
||||
protected float _finalImageFadeOutDuration = 0.25f;
|
||||
|
||||
[Header("Skip Text Tween (Yoyo Alpha)")]
|
||||
[SerializeField]
|
||||
protected bool _animateSkipText = true;
|
||||
|
||||
[SerializeField]
|
||||
[Min(0.05f)]
|
||||
protected float _skipTextHalfCycleSeconds = 0.75f;
|
||||
|
||||
[SerializeField]
|
||||
[Range(0f, 1f)]
|
||||
protected float _skipTextMinAlpha = 0.25f;
|
||||
|
||||
[SerializeField]
|
||||
[Range(0f, 1f)]
|
||||
protected float _skipTextMaxAlpha = 1f;
|
||||
|
||||
[SerializeField]
|
||||
protected Ease _skipTextEase = Ease.InOutSine;
|
||||
|
||||
[Header("Test/Debug (No Injection Needed)")]
|
||||
[Tooltip("If true, Escape will skip even when InputManager isn't injected (handy for editor tests).")]
|
||||
[SerializeField]
|
||||
private bool _allowKeyboardEscapeSkipFallback = true;
|
||||
|
||||
private CancellationTokenSource _cinematicCts;
|
||||
private bool _endingStarted;
|
||||
private bool _isPlaying;
|
||||
private bool _skipRequested;
|
||||
|
||||
private Sequence _skipTextSequence;
|
||||
|
||||
protected EventCoordinator EventCoordinator;
|
||||
protected InputManager InputManager;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
if (_cinematicCanvasGroup != null)
|
||||
_cinematicCanvasGroup.alpha = 0f;
|
||||
|
||||
SetImageAlpha(_imageA, 0f);
|
||||
SetImageAlpha(_imageB, 0f);
|
||||
|
||||
ClearSubtitleText();
|
||||
StopSkipTextTween();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_isPlaying) return;
|
||||
if (!_allowKeyboardEscapeSkipFallback) return;
|
||||
if (InputManager != null) return;
|
||||
|
||||
var kb = Keyboard.current;
|
||||
if (kb != null && kb.escapeKey.wasPressedThisFrame)
|
||||
RequestSkip();
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
BindSkipAction();
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
UnbindSkipAction();
|
||||
CancelAndDisposeCts();
|
||||
StopSkipTextTween();
|
||||
_isPlaying = false;
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
UnbindSkipAction();
|
||||
CancelAndDisposeCts();
|
||||
StopSkipTextTween();
|
||||
}
|
||||
|
||||
protected async UniTask PlayCinematic(CancellationToken destroyToken)
|
||||
{
|
||||
if (_cinematicCanvasGroup == null || _imageA == null || _imageB == null)
|
||||
{
|
||||
Debug.LogWarning("[BaseCinematic] Missing CanvasGroup or Images.");
|
||||
return;
|
||||
}
|
||||
|
||||
_isPlaying = true;
|
||||
|
||||
if (_cinematicPanels == null || _cinematicPanels.Count == 0)
|
||||
{
|
||||
await EndCinematicFlow();
|
||||
return;
|
||||
}
|
||||
|
||||
CancelAndDisposeCts();
|
||||
_cinematicCts = CancellationTokenSource.CreateLinkedTokenSource(destroyToken);
|
||||
var playbackToken = _cinematicCts.Token;
|
||||
|
||||
_skipRequested = false;
|
||||
_endingStarted = false;
|
||||
|
||||
_cinematicCanvasGroup.blocksRaycasts = false;
|
||||
_cinematicCanvasGroup.interactable = false;
|
||||
_cinematicCanvasGroup.alpha = 0f;
|
||||
|
||||
ClearSubtitleText();
|
||||
SetImageAlpha(_imageA, 0f);
|
||||
SetImageAlpha(_imageB, 0f);
|
||||
|
||||
StartSkipTextTweenIfNeeded();
|
||||
|
||||
await FadeCanvasGroup(_cinematicCanvasGroup, 0f, 1f, _canvasFadeInDuration, destroyToken);
|
||||
|
||||
_cinematicCanvasGroup.blocksRaycasts = true;
|
||||
_cinematicCanvasGroup.interactable = true;
|
||||
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < _cinematicPanels.Count; i++)
|
||||
{
|
||||
if (_skipRequested)
|
||||
break;
|
||||
|
||||
var panel = GetPanel(i);
|
||||
if (panel == null || panel.CinematicImage == null)
|
||||
continue;
|
||||
|
||||
var currentTime = Mathf.Max(0f, panel.DisplayTime);
|
||||
var nextTime = i + 1 < _cinematicPanels.Count
|
||||
? Mathf.Max(0f, GetPanel(i + 1)?.DisplayTime ?? 0f)
|
||||
: 0f;
|
||||
|
||||
var isFirst = i == 0;
|
||||
var isLast = i == _cinematicPanels.Count - 1;
|
||||
|
||||
await RunPanelWithSubtitles(i, currentTime, nextTime, isFirst, isLast, playbackToken);
|
||||
|
||||
if (_clearSubtitlesBetweenPanels && !isLast)
|
||||
ClearSubtitleText();
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
var safeExitToken = this.GetCancellationTokenOnDestroy();
|
||||
|
||||
await FadeOutAtEnd(safeExitToken);
|
||||
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(2f), DelayType.UnscaledDeltaTime,
|
||||
cancellationToken: safeExitToken);
|
||||
|
||||
await EndCinematicFlow();
|
||||
}
|
||||
|
||||
protected virtual UniTask OnCinematicEnd()
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
protected void RequestSkip()
|
||||
{
|
||||
Debug.Log("Skip Requested");
|
||||
|
||||
if (_skipRequested || _endingStarted)
|
||||
return;
|
||||
|
||||
_skipRequested = true;
|
||||
|
||||
if (_cinematicCts != null && !_cinematicCts.IsCancellationRequested)
|
||||
try
|
||||
{
|
||||
_cinematicCts.Cancel();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void BindSkipAction()
|
||||
{
|
||||
if (InputManager == null)
|
||||
return;
|
||||
|
||||
InputManager.BindPauseForSkip(OnSkipPerformed);
|
||||
|
||||
if (_skipText != null)
|
||||
{
|
||||
var inputType = InputManager.DeviceInputType;
|
||||
switch (inputType)
|
||||
{
|
||||
case DeviceInputType.KeyboardAndMouse:
|
||||
_skipText.text = "Press Escape to Skip.";
|
||||
break;
|
||||
case DeviceInputType.XboxController:
|
||||
_skipText.text = "Press B to Skip.";
|
||||
break;
|
||||
case DeviceInputType.PlaystationController:
|
||||
_skipText.text = "Press Circle to Skip.";
|
||||
break;
|
||||
case DeviceInputType.SwitchProController:
|
||||
_skipText.text = "Press B to Skip.";
|
||||
break;
|
||||
default:
|
||||
_skipText.text = "Press Cancel to Skip.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void UnbindSkipAction()
|
||||
{
|
||||
if (InputManager == null)
|
||||
return;
|
||||
|
||||
InputManager.ResetPauseBind(OnSkipPerformed);
|
||||
}
|
||||
|
||||
private void OnSkipPerformed(InputAction.CallbackContext _)
|
||||
{
|
||||
RequestSkip();
|
||||
}
|
||||
|
||||
private void CancelAndDisposeCts()
|
||||
{
|
||||
if (_cinematicCts == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_cinematicCts.Cancel();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
_cinematicCts.Dispose();
|
||||
_cinematicCts = null;
|
||||
}
|
||||
|
||||
private async UniTask EndCinematicFlow()
|
||||
{
|
||||
if (_endingStarted)
|
||||
return;
|
||||
|
||||
_endingStarted = true;
|
||||
_isPlaying = false;
|
||||
|
||||
StopSkipTextTween();
|
||||
ClearSubtitleText();
|
||||
|
||||
await OnCinematicEnd();
|
||||
}
|
||||
|
||||
private void StartSkipTextTweenIfNeeded()
|
||||
{
|
||||
if (!_animateSkipText || _skipText == null)
|
||||
return;
|
||||
|
||||
StopSkipTextTween();
|
||||
|
||||
var c = _skipText.color;
|
||||
c.a = Mathf.Clamp01(_skipTextMaxAlpha);
|
||||
_skipText.color = c;
|
||||
|
||||
_skipTextSequence = Sequence.Create(
|
||||
useUnscaledTime: true,
|
||||
cycleMode: Sequence.SequenceCycleMode.Yoyo,
|
||||
cycles: -1
|
||||
)
|
||||
.Group(Tween.Alpha(_skipText, new TweenSettings<float>
|
||||
{
|
||||
startValue = _skipTextMaxAlpha,
|
||||
endValue = _skipTextMinAlpha,
|
||||
settings = new TweenSettings
|
||||
{
|
||||
duration = _skipTextHalfCycleSeconds,
|
||||
ease = _skipTextEase,
|
||||
useUnscaledTime = true
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void StopSkipTextTween()
|
||||
{
|
||||
if (_skipTextSequence.isAlive)
|
||||
_skipTextSequence.Stop();
|
||||
|
||||
_skipTextSequence = default;
|
||||
}
|
||||
|
||||
private CinematicPanel GetPanel(int index)
|
||||
{
|
||||
if (_cinematicPanels == null) return null;
|
||||
if (index < 0 || index >= _cinematicPanels.Count) return null;
|
||||
return _cinematicPanels[index];
|
||||
}
|
||||
|
||||
private async UniTask RunPanelWithSubtitles(int panelIndex, float currentTime, float nextTime, bool isFirst,
|
||||
bool isLast, CancellationToken token)
|
||||
{
|
||||
using var panelCts = CancellationTokenSource.CreateLinkedTokenSource(token);
|
||||
var panelToken = panelCts.Token;
|
||||
|
||||
await UniTask.WhenAll(
|
||||
ShowPanelExact(panelIndex, currentTime, nextTime, isFirst, isLast, panelToken),
|
||||
RunSubtitlesForPanel(panelIndex, currentTime, panelToken)
|
||||
);
|
||||
}
|
||||
|
||||
private async UniTask RunSubtitlesForPanel(int panelIndex, float panelTime, CancellationToken token)
|
||||
{
|
||||
if (_cinematicText == null)
|
||||
return;
|
||||
|
||||
var panel = GetPanel(panelIndex);
|
||||
if (panel == null)
|
||||
return;
|
||||
|
||||
var subs = panel.Subtitles;
|
||||
if (subs == null || subs.Count == 0)
|
||||
{
|
||||
ClearSubtitleText();
|
||||
if (panelTime > 0f)
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(panelTime), DelayType.UnscaledDeltaTime,
|
||||
cancellationToken: token);
|
||||
return;
|
||||
}
|
||||
|
||||
var sum = 0f;
|
||||
for (var i = 0; i < subs.Count; i++)
|
||||
{
|
||||
if (subs[i].Equals(default))
|
||||
continue;
|
||||
|
||||
sum += Mathf.Max(0f, subs[i].DisplayTime);
|
||||
}
|
||||
|
||||
if (_logSubtitleTimingWarnings && sum > panelTime + 0.001f)
|
||||
Debug.LogWarning(
|
||||
$"[BaseCinematic] Panel '{panel.name}' subtitle times sum to {sum:0.###}s but panel DisplayTime is {panelTime:0.###}s. Subtitles will be cut short.");
|
||||
|
||||
var remaining = panelTime;
|
||||
|
||||
for (var i = 0; i < subs.Count; i++)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var sub = subs[i];
|
||||
if (sub.Equals(default))
|
||||
continue;
|
||||
|
||||
var subTime = Mathf.Max(0f, sub.DisplayTime);
|
||||
if (subTime <= 0f)
|
||||
continue;
|
||||
|
||||
var actual = Mathf.Min(subTime, remaining);
|
||||
if (actual <= 0f)
|
||||
break;
|
||||
|
||||
_cinematicText.text = sub.Subtitle ?? string.Empty;
|
||||
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(actual), DelayType.UnscaledDeltaTime,
|
||||
cancellationToken: token);
|
||||
|
||||
remaining -= actual;
|
||||
if (remaining <= 0f)
|
||||
break;
|
||||
}
|
||||
|
||||
if (remaining > 0f)
|
||||
{
|
||||
_cinematicText.text = string.Empty;
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(remaining), DelayType.UnscaledDeltaTime,
|
||||
cancellationToken: token);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearSubtitleText()
|
||||
{
|
||||
if (_cinematicText != null)
|
||||
_cinematicText.text = string.Empty;
|
||||
}
|
||||
|
||||
private async UniTask ShowPanelExact(int panelIndex, float currentTime, float nextTime, bool isFirst,
|
||||
bool isLast, CancellationToken token)
|
||||
{
|
||||
var panel = GetPanel(panelIndex);
|
||||
if (panel == null || panel.CinematicImage == null)
|
||||
return;
|
||||
|
||||
var boundaryFade = 0f;
|
||||
if (!isLast)
|
||||
boundaryFade = Mathf.Min(_maxCrossfadeDuration, currentTime, nextTime);
|
||||
|
||||
var introFade = 0f;
|
||||
|
||||
if (isFirst)
|
||||
{
|
||||
introFade = isLast
|
||||
? Mathf.Min(_maxCrossfadeDuration, currentTime)
|
||||
: Mathf.Min(_maxCrossfadeDuration, currentTime, nextTime);
|
||||
|
||||
_imageA.sprite = panel.CinematicImage;
|
||||
|
||||
SetImageAlpha(_imageA, 0f);
|
||||
SetImageAlpha(_imageB, 0f);
|
||||
|
||||
if (introFade > 0f)
|
||||
await FadeImage(_imageA, 0f, 1f, introFade, token);
|
||||
else
|
||||
SetImageAlpha(_imageA, 1f);
|
||||
|
||||
var hold = Mathf.Max(0f, currentTime - introFade - boundaryFade);
|
||||
if (hold > 0f)
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(hold), DelayType.UnscaledDeltaTime,
|
||||
cancellationToken: token);
|
||||
|
||||
if (!isLast && boundaryFade > 0f)
|
||||
await CrossfadeToNext(panelIndex + 1, boundaryFade, token);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var holdTime = Mathf.Max(0f, currentTime - boundaryFade);
|
||||
if (holdTime > 0f)
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(holdTime), DelayType.UnscaledDeltaTime,
|
||||
cancellationToken: token);
|
||||
|
||||
if (!isLast && boundaryFade > 0f)
|
||||
await CrossfadeToNext(panelIndex + 1, boundaryFade, token);
|
||||
}
|
||||
|
||||
private async UniTask CrossfadeToNext(int nextPanelIndex, float duration, CancellationToken token)
|
||||
{
|
||||
var nextPanel = GetPanel(nextPanelIndex);
|
||||
if (nextPanel == null || nextPanel.CinematicImage == null)
|
||||
return;
|
||||
|
||||
var current = GetMoreVisible(_imageA, _imageB);
|
||||
var next = current == _imageA ? _imageB : _imageA;
|
||||
|
||||
next.sprite = nextPanel.CinematicImage;
|
||||
|
||||
var currentAlpha = GetImageAlpha(current);
|
||||
|
||||
SetImageAlpha(next, 0f);
|
||||
|
||||
var seq = Sequence.Create(useUnscaledTime: true)
|
||||
.Group(Tween.Alpha(current, new TweenSettings<float>
|
||||
{
|
||||
startValue = currentAlpha,
|
||||
endValue = 0f,
|
||||
settings = new TweenSettings { duration = duration, ease = Ease.Linear, useUnscaledTime = true }
|
||||
}))
|
||||
.Group(Tween.Alpha(next, new TweenSettings<float>
|
||||
{
|
||||
startValue = 0f,
|
||||
endValue = 1f,
|
||||
settings = new TweenSettings { duration = duration, ease = Ease.Linear, useUnscaledTime = true }
|
||||
}));
|
||||
|
||||
await seq.ToUniTask(cancellationToken: token);
|
||||
}
|
||||
|
||||
private async UniTask FadeOutAtEnd(CancellationToken token)
|
||||
{
|
||||
StopSkipTextTween();
|
||||
|
||||
if (EventCoordinator != null)
|
||||
{
|
||||
if (_cinematicCanvasGroup != null)
|
||||
{
|
||||
_cinematicCanvasGroup.blocksRaycasts = false;
|
||||
_cinematicCanvasGroup.interactable = false;
|
||||
_cinematicCanvasGroup.alpha = 1f;
|
||||
}
|
||||
|
||||
EventCoordinator.PublishImmediate(new FadeEvent(false, 1f));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var img = GetMoreVisible(_imageA, _imageB);
|
||||
var imgFrom = GetImageAlpha(img);
|
||||
|
||||
if ((_cinematicCanvasGroup == null || _cinematicCanvasGroup.alpha <= 0.001f) && imgFrom <= 0.001f)
|
||||
return;
|
||||
|
||||
var canvasFrom = _cinematicCanvasGroup != null ? _cinematicCanvasGroup.alpha : 0f;
|
||||
|
||||
var imgDur = Mathf.Max(0f, _finalImageFadeOutDuration);
|
||||
var canvasDur = Mathf.Max(0f, _canvasFadeOutDuration);
|
||||
|
||||
var seq = Sequence.Create(useUnscaledTime: true);
|
||||
|
||||
if (img != null && imgDur > 0f && imgFrom > 0.001f)
|
||||
seq.Group(Tween.Alpha(img, new TweenSettings<float>
|
||||
{
|
||||
startValue = imgFrom,
|
||||
endValue = 0f,
|
||||
settings = new TweenSettings { duration = imgDur, ease = Ease.OutQuad, useUnscaledTime = true }
|
||||
}));
|
||||
else if (img != null) SetImageAlpha(img, 0f);
|
||||
|
||||
if (_cinematicCanvasGroup != null && canvasDur > 0f && canvasFrom > 0.001f)
|
||||
{
|
||||
_cinematicCanvasGroup.blocksRaycasts = false;
|
||||
_cinematicCanvasGroup.interactable = false;
|
||||
|
||||
seq.Group(Tween.Alpha(_cinematicCanvasGroup, new TweenSettings<float>
|
||||
{
|
||||
startValue = canvasFrom,
|
||||
endValue = 0f,
|
||||
settings = new TweenSettings { duration = canvasDur, ease = Ease.OutQuad, useUnscaledTime = true }
|
||||
}));
|
||||
}
|
||||
else if (_cinematicCanvasGroup != null)
|
||||
{
|
||||
_cinematicCanvasGroup.alpha = 0f;
|
||||
_cinematicCanvasGroup.blocksRaycasts = false;
|
||||
_cinematicCanvasGroup.interactable = false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (seq.isAlive)
|
||||
await seq.ToUniTask(cancellationToken: token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (img != null) SetImageAlpha(img, 0f);
|
||||
if (_cinematicCanvasGroup != null) _cinematicCanvasGroup.alpha = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private static async UniTask FadeCanvasGroup(CanvasGroup group, float from, float to, float duration,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (group == null)
|
||||
return;
|
||||
|
||||
if (duration <= 0f)
|
||||
{
|
||||
group.alpha = to;
|
||||
return;
|
||||
}
|
||||
|
||||
group.alpha = from;
|
||||
|
||||
var seq = Sequence.Create(useUnscaledTime: true)
|
||||
.Group(Tween.Alpha(group, new TweenSettings<float>
|
||||
{
|
||||
startValue = from,
|
||||
endValue = to,
|
||||
settings = new TweenSettings { duration = duration, ease = Ease.OutQuad, useUnscaledTime = true }
|
||||
}));
|
||||
|
||||
await seq.ToUniTask(cancellationToken: token);
|
||||
}
|
||||
|
||||
private static async UniTask FadeImage(Image image, float from, float to, float duration,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (image == null)
|
||||
return;
|
||||
|
||||
if (duration <= 0f)
|
||||
{
|
||||
SetImageAlpha(image, to);
|
||||
return;
|
||||
}
|
||||
|
||||
SetImageAlpha(image, from);
|
||||
|
||||
var seq = Sequence.Create(useUnscaledTime: true)
|
||||
.Group(Tween.Alpha(image, new TweenSettings<float>
|
||||
{
|
||||
startValue = from,
|
||||
endValue = to,
|
||||
settings = new TweenSettings { duration = duration, ease = Ease.OutQuad, useUnscaledTime = true }
|
||||
}));
|
||||
|
||||
await seq.ToUniTask(cancellationToken: token);
|
||||
}
|
||||
|
||||
private static float GetImageAlpha(Image image)
|
||||
{
|
||||
if (image == null)
|
||||
return 0f;
|
||||
return image.color.a;
|
||||
}
|
||||
|
||||
private static void SetImageAlpha(Image image, float alpha)
|
||||
{
|
||||
if (image == null)
|
||||
return;
|
||||
|
||||
var c = image.color;
|
||||
c.a = Mathf.Clamp01(alpha);
|
||||
image.color = c;
|
||||
}
|
||||
|
||||
private static Image GetMoreVisible(Image a, Image b)
|
||||
{
|
||||
var aa = GetImageAlpha(a);
|
||||
var bb = GetImageAlpha(b);
|
||||
return aa >= bb ? a : b;
|
||||
}
|
||||
|
||||
[Inject]
|
||||
public void Construct(EventCoordinator eventCoordinator, InputManager inputManager)
|
||||
{
|
||||
EventCoordinator = eventCoordinator;
|
||||
InputManager = inputManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user