Files
Ipseity-Demo/Assets/_Content/Scripts/Features/ShifitingSubtitles/PerLetterFontShifter.cs
T

198 lines
5.5 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
namespace Unheimlich.UI
{
[RequireComponent(typeof(TextMeshProUGUI))]
public class PerLetterFontShifter : MonoBehaviour
{
#region Serialized Fields
[Header("Fonts")]
[SerializeField] private TMP_FontAsset[] _fonts;
[Header("Timing")]
[SerializeField] private float _shiftInterval = 3f;
[SerializeField] private float _minLetterDelay = 0.05f;
[SerializeField] private float _maxLetterDelay = 0.25f;
[Header("Style")]
[SerializeField] private float _fontSize = 36f;
[SerializeField] private Color _textColor = Color.white;
#endregion
#region Private Fields
private TextMeshProUGUI _originalTmp;
private string _originalText;
private readonly List<TextMeshProUGUI> _letterObjects = new List<TextMeshProUGUI>();
private Coroutine _shiftLoopRoutineHandle;
#endregion
#region Unity Lifecycle Methods
private void Awake()
{
_originalTmp = GetComponent<TextMeshProUGUI>();
_originalText = _originalTmp.text;
_originalTmp.enabled = false;
}
private void OnEnable()
{
BuildLetterObjects();
_shiftLoopRoutineHandle = StartCoroutine(ShiftLoopRoutine());
}
private void OnDisable()
{
if (_shiftLoopRoutineHandle != null)
StopCoroutine(_shiftLoopRoutineHandle);
}
private void OnDestroy()
{
DestroyLetterObjects();
}
#endregion
#region Public Methods
public void SetText(string newText)
{
_originalText = newText;
BuildLetterObjects();
}
#endregion
#region Private Methods
private void BuildLetterObjects()
{
DestroyLetterObjects();
foreach (char character in _originalText)
{
GameObject letterGo = new GameObject($"Letter_{character}");
letterGo.transform.SetParent(transform, false);
TextMeshProUGUI letterTmp = letterGo.AddComponent<TextMeshProUGUI>();
letterTmp.text = character.ToString();
letterTmp.fontSize = _fontSize;
letterTmp.color = _textColor;
letterTmp.alignment = TextAlignmentOptions.Center;
_letterObjects.Add(letterTmp);
}
ArrangeLetters();
ShuffleFonts();
}
private void DestroyLetterObjects()
{
foreach (TextMeshProUGUI letter in _letterObjects)
{
if (letter != null)
Destroy(letter.gameObject);
}
_letterObjects.Clear();
}
private void ArrangeLetters()
{
HorizontalLayoutGroup layout = gameObject.GetComponent<HorizontalLayoutGroup>();
if (layout == null)
layout = gameObject.AddComponent<HorizontalLayoutGroup>();
layout.childAlignment = TextAnchor.MiddleCenter;
layout.childForceExpandWidth = false;
layout.childForceExpandHeight = false;
layout.childControlWidth = true;
layout.childControlHeight = true;
layout.spacing = 0f;
foreach (TextMeshProUGUI letter in _letterObjects)
{
ContentSizeFitter fitter = letter.gameObject.GetComponent<ContentSizeFitter>();
if (fitter == null)
fitter = letter.gameObject.AddComponent<ContentSizeFitter>();
fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
}
}
private void ShuffleFonts()
{
if (_fonts == null || _fonts.Length == 0)
{
Debug.LogWarning($"[{nameof(PerLetterFontShifter)}] No fonts assigned.", this);
return;
}
StartCoroutine(ShuffleStaggeredRoutine());
}
private List<int> GetShuffledIndices()
{
List<int> indices = new List<int>();
for (int i = 0; i < _letterObjects.Count; i++)
indices.Add(i);
for (int i = indices.Count - 1; i > 0; i--)
{
int j = Random.Range(0, i + 1);
(indices[i], indices[j]) = (indices[j], indices[i]);
}
return indices;
}
#endregion
#region Coroutines
private IEnumerator ShiftLoopRoutine()
{
while (true)
{
yield return new WaitForSeconds(_shiftInterval);
ShuffleFonts();
}
}
private IEnumerator ShuffleStaggeredRoutine()
{
List<int> shuffledIndices = GetShuffledIndices();
foreach (int index in shuffledIndices)
{
TextMeshProUGUI letter = _letterObjects[index];
if (letter.text == " " || letter.text == "\n")
continue;
letter.font = _fonts[Random.Range(0, _fonts.Length)];
yield return new WaitForSeconds(Random.Range(_minLetterDelay, _maxLetterDelay));
}
}
#endregion
#if UNITY_EDITOR
[ContextMenu("Test Shuffle Now")]
private void TestShuffle() => ShuffleFonts();
#endif
}
}