260 lines
8.4 KiB
C#
260 lines
8.4 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.Serialization;
|
||
using UnityEngine.Video;
|
||
|
||
public class VideoUtility : MonoBehaviour
|
||
{
|
||
[SerializeField, FormerlySerializedAs("player")] private VideoPlayer _primaryPlayer;
|
||
[SerializeField] private VideoPlayer _secondaryPlayer;
|
||
[SerializeField, FormerlySerializedAs("audioSource")] private AudioSource _audioSource;
|
||
[SerializeField, FormerlySerializedAs("fon")] private VideoClip _menuBackground;
|
||
[SerializeField, FormerlySerializedAs("ads")] private VideoClip _ads;
|
||
[SerializeField, FormerlySerializedAs("ending")] private VideoClip _ending;
|
||
[SerializeField] private float _crossFadeDuration = 1f;
|
||
[SerializeField, FormerlySerializedAs("canvas")] private Canvas _canvas;
|
||
|
||
private static VideoUtility _instance;
|
||
private readonly Dictionary<VideoClip, List<System.Action>> _pendingReleases = new();
|
||
private VideoPlayer _activeBackgroundPlayer;
|
||
private Coroutine _crossFadeCoroutine;
|
||
|
||
public static float AdsLength => (float)_instance._ads.length;
|
||
public static float VictoryLength => (float)_instance._ending.length;
|
||
|
||
private void Update()
|
||
{
|
||
if (Input.GetKeyDown(KeyCode.Alpha5))
|
||
PlayFon();
|
||
|
||
if (Input.GetKeyDown(KeyCode.Alpha6))
|
||
PlayAds();
|
||
|
||
if (Input.GetKeyDown(KeyCode.Alpha7))
|
||
PlayEnding();
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
SettingsManager.OnSettingsChanged -= ApplyAudioSettings;
|
||
|
||
foreach (List<System.Action> releases in _pendingReleases.Values)
|
||
{
|
||
foreach (System.Action release in releases)
|
||
release?.Invoke();
|
||
}
|
||
|
||
_pendingReleases.Clear();
|
||
|
||
if (_instance == this)
|
||
_instance = null;
|
||
}
|
||
|
||
public void Initialize()
|
||
{
|
||
if (_instance == null)
|
||
_instance = this;
|
||
|
||
ConfigureBackgroundPlayer(_primaryPlayer);
|
||
ConfigureBackgroundPlayer(_secondaryPlayer);
|
||
_activeBackgroundPlayer = _primaryPlayer;
|
||
ApplyAudioSettings();
|
||
SettingsManager.OnSettingsChanged -= ApplyAudioSettings;
|
||
SettingsManager.OnSettingsChanged += ApplyAudioSettings;
|
||
PlayBackgroundImmediately(_menuBackground);
|
||
}
|
||
|
||
public static void CrossFadeToCharacter(int characterIndex, VideoClip backgroundVideo)
|
||
{
|
||
if (backgroundVideo == null)
|
||
{
|
||
Debug.LogError($"Не найден видеофон для персонажа с индексом {characterIndex}");
|
||
return;
|
||
}
|
||
|
||
_instance.StartCrossFade(backgroundVideo);
|
||
}
|
||
|
||
public static void CrossFadeToMenu()
|
||
=> _instance.StartCrossFade(_instance._menuBackground);
|
||
|
||
public static void PlayFon()
|
||
=> CrossFadeToMenu();
|
||
|
||
public static void PlayAds()
|
||
=> _instance.PlayForeground(_instance._ads);
|
||
|
||
public static void PlayEnding()
|
||
=> _instance.PlayForeground(_instance._ending);
|
||
|
||
public static void ReleaseBackgroundWhenUnused(VideoClip clip, System.Action release)
|
||
{
|
||
if (release == null)
|
||
return;
|
||
|
||
if (_instance == null || clip == null || !_instance.IsClipAssigned(clip))
|
||
{
|
||
release.Invoke();
|
||
return;
|
||
}
|
||
|
||
if (!_instance._pendingReleases.TryGetValue(clip, out List<System.Action> releases))
|
||
{
|
||
releases = new List<System.Action>();
|
||
_instance._pendingReleases[clip] = releases;
|
||
}
|
||
|
||
releases.Add(release);
|
||
}
|
||
|
||
private void StartCrossFade(VideoClip targetClip)
|
||
{
|
||
if (targetClip == null)
|
||
{
|
||
Debug.LogError("Не назначен видеофон для переключения");
|
||
return;
|
||
}
|
||
|
||
NormalizeInterruptedCrossFade();
|
||
|
||
if (_activeBackgroundPlayer.clip == targetClip && _activeBackgroundPlayer.isPlaying)
|
||
return;
|
||
|
||
_crossFadeCoroutine = StartCoroutine(CrossFadeCoroutine(targetClip));
|
||
}
|
||
|
||
private IEnumerator CrossFadeCoroutine(VideoClip targetClip)
|
||
{
|
||
VideoPlayer outgoingPlayer = _activeBackgroundPlayer;
|
||
VideoPlayer incomingPlayer = GetInactivePlayer();
|
||
|
||
ConfigureBackgroundPlayer(incomingPlayer);
|
||
incomingPlayer.Stop();
|
||
incomingPlayer.clip = targetClip;
|
||
incomingPlayer.targetCameraAlpha = 0f;
|
||
incomingPlayer.Prepare();
|
||
|
||
//while (incomingPlayer.isPrepared == false)
|
||
// yield return null;
|
||
|
||
incomingPlayer.Play();
|
||
|
||
float elapsedTime = 0f;
|
||
float duration = Mathf.Max(_crossFadeDuration, 0.01f);
|
||
|
||
while (elapsedTime < duration)
|
||
{
|
||
elapsedTime += Time.unscaledDeltaTime;
|
||
float progress = Mathf.Clamp01(elapsedTime / duration);
|
||
incomingPlayer.targetCameraAlpha = progress;
|
||
outgoingPlayer.targetCameraAlpha = 1f - progress;
|
||
yield return null;
|
||
}
|
||
|
||
incomingPlayer.targetCameraAlpha = 1f;
|
||
outgoingPlayer.targetCameraAlpha = 0f;
|
||
outgoingPlayer.Stop();
|
||
ReleasePlayerClip(outgoingPlayer);
|
||
_activeBackgroundPlayer = incomingPlayer;
|
||
_crossFadeCoroutine = null;
|
||
}
|
||
|
||
private void NormalizeInterruptedCrossFade()
|
||
{
|
||
if (_crossFadeCoroutine == null)
|
||
return;
|
||
|
||
StopCoroutine(_crossFadeCoroutine);
|
||
_crossFadeCoroutine = null;
|
||
|
||
_activeBackgroundPlayer = _primaryPlayer.targetCameraAlpha >= _secondaryPlayer.targetCameraAlpha
|
||
? _primaryPlayer
|
||
: _secondaryPlayer;
|
||
|
||
VideoPlayer inactivePlayer = GetInactivePlayer();
|
||
_activeBackgroundPlayer.targetCameraAlpha = 1f;
|
||
inactivePlayer.targetCameraAlpha = 0f;
|
||
inactivePlayer.Stop();
|
||
ReleasePlayerClip(inactivePlayer);
|
||
}
|
||
|
||
private VideoPlayer GetInactivePlayer()
|
||
=> _activeBackgroundPlayer == _primaryPlayer ? _secondaryPlayer : _primaryPlayer;
|
||
|
||
private void PlayBackgroundImmediately(VideoClip clip)
|
||
{
|
||
_secondaryPlayer.Stop();
|
||
_secondaryPlayer.targetCameraAlpha = 0f;
|
||
ReleasePlayerClip(_secondaryPlayer);
|
||
_activeBackgroundPlayer.Stop();
|
||
ReleasePlayerClip(_activeBackgroundPlayer);
|
||
_activeBackgroundPlayer = _primaryPlayer;
|
||
_activeBackgroundPlayer.clip = clip;
|
||
_activeBackgroundPlayer.targetCameraAlpha = 1f;
|
||
_activeBackgroundPlayer.Play();
|
||
}
|
||
|
||
private void ConfigureBackgroundPlayer(VideoPlayer videoPlayer)
|
||
{
|
||
videoPlayer.renderMode = VideoRenderMode.CameraFarPlane;
|
||
videoPlayer.targetCamera = _primaryPlayer.targetCamera;
|
||
videoPlayer.aspectRatio = _primaryPlayer.aspectRatio;
|
||
videoPlayer.isLooping = true;
|
||
videoPlayer.playOnAwake = false;
|
||
videoPlayer.audioOutputMode = VideoAudioOutputMode.None;
|
||
}
|
||
|
||
private void PlayForeground(VideoClip clip)
|
||
{
|
||
NormalizeInterruptedCrossFade();
|
||
_primaryPlayer.Stop();
|
||
_secondaryPlayer.Stop();
|
||
ReleasePlayerClip(_primaryPlayer);
|
||
ReleasePlayerClip(_secondaryPlayer);
|
||
_activeBackgroundPlayer = _primaryPlayer;
|
||
_primaryPlayer.renderMode = VideoRenderMode.CameraNearPlane;
|
||
_primaryPlayer.targetCameraAlpha = 1f;
|
||
_primaryPlayer.isLooping = false;
|
||
_primaryPlayer.clip = clip;
|
||
_primaryPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource;
|
||
_primaryPlayer.EnableAudioTrack(0, true);
|
||
_primaryPlayer.SetTargetAudioSource(0, _audioSource);
|
||
_audioSource.Stop();
|
||
StartCoroutine(PrepareAndPlayForeground());
|
||
}
|
||
|
||
private IEnumerator PrepareAndPlayForeground()
|
||
{
|
||
_primaryPlayer.Prepare();
|
||
|
||
while (_primaryPlayer.isPrepared == false)
|
||
yield return null;
|
||
|
||
_audioSource.Play();
|
||
_primaryPlayer.Play();
|
||
}
|
||
|
||
private void ApplyAudioSettings()
|
||
=> _audioSource.volume = SettingsManager.GetFloat(SettingKey.PROMO_VOLUME);
|
||
|
||
private bool IsClipAssigned(VideoClip clip)
|
||
=> _primaryPlayer.clip == clip || _secondaryPlayer.clip == clip;
|
||
|
||
private void ReleasePlayerClip(VideoPlayer videoPlayer)
|
||
{
|
||
VideoClip releasedClip = videoPlayer.clip;
|
||
videoPlayer.clip = null;
|
||
NotifyClipUnused(releasedClip);
|
||
}
|
||
|
||
private void NotifyClipUnused(VideoClip clip)
|
||
{
|
||
if (clip == null || IsClipAssigned(clip) || !_pendingReleases.Remove(clip, out List<System.Action> releases))
|
||
return;
|
||
|
||
foreach (System.Action release in releases)
|
||
release?.Invoke();
|
||
}
|
||
}
|