using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; using UnityEngine.UI; using TMPro; namespace Superlazy.UI { public class SLUIValue : SLUIComponent { public string prefix; public string postfix; public bool useRaw; public bool userInput; public bool useUpdate; private bool UseTag => tagID != string.Empty; public string tagID; public string bindingValue; public string bindingMaxValue; [NonSerialized] public TextMeshProUGUI text; [NonSerialized] public Image image; [NonSerialized] public Slider slider; private SLEntity oldValue = false; private float oldMaxValue = -10000; // text only public bool useTypingEffect = false; public float startDelay = 0.2f; public float delay = 0.03f; public string typingEndState = string.Empty; public string typingEndBind = "Skipped"; public bool ignoreColorTag = false; public string textSound = "SoundEffect/SE_Text"; // smoothing public bool useSmoothing = false; public float smoothingDelay = 1.0f; private float smoothingValue = 0; public string beforeSmoothingValue = string.Empty; protected override void Validate() { text = GetComponent(); image = GetComponent(); slider = GetComponent(); } protected override void Init() { } protected override void Enable() { if (text != null) { SLUIUtil.UpdateFont(text); } oldValue = false; oldMaxValue = 0.0f; if (useUpdate && useRaw && bindingValue.Contains("{")) { useTypingEffect = false; useUpdate = true; } if (useUpdate) { SLGame.AddNotify("Time", OnChange); } if (useRaw) { OnChange(); return; } SLGame.AddNotify(bindParent.BindPath.CombinePath(bindingValue), OnChange); if (bindingMaxValue != string.Empty) { SLGame.AddNotify(bindParent.BindPath.CombinePath(bindingMaxValue), OnChange); } if (text != null && useTypingEffect && typingEndBind != string.Empty) { SLGame.AddNotify(bindParent.BindPath.CombinePath(typingEndBind), OnSkipped); } } protected override void Disable() { if (oldMaxValue <= -1000) return; // TEMP: SLUIValue Disabled가 최초 Enabled도 불리기 전에 불릴 가능성이 있음 if (text != null && useTypingEffect && typingEndBind != string.Empty) { SLGame.RemoveNotify(bindParent.BindPath.CombinePath(typingEndBind), OnSkipped); } if (useTypingEffect && string.IsNullOrEmpty(typingEndState) == false) { SLGame.Session["UIState"][typingEndState] = false; } if (useUpdate) { SLGame.RemoveNotify("Time", OnChange); } if (useRaw) return; SLGame.RemoveNotify(bindParent.BindPath.CombinePath(bindingValue), OnChange); if (bindingMaxValue != string.Empty) { SLGame.RemoveNotify(bindParent.BindPath.CombinePath(bindingMaxValue), OnChange); } } private void OnChange() { if (bindParent.Active == false) return; var sessionRoot = SLGame.SessionGet(bindParent.BindPath); if (sessionRoot == false) return; // TEMP: 세션루트가 삭제되었지만, 삭제되되기전 코루틴 처리가 있을 수 있음 if (text != null) { OnChangeText(); } else if (image != null) { OnChangeImage(); } else if (slider != null) { OnChangeSlider(); } } private void OnChangeText() { if (useRaw) { text.text = SLString.GetString(bindingValue, SLGame.SessionGet(bindParent.BindPath)); } else if (userInput) { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLGame.SessionGet(bindParent.BindPath).Get(bindingValue).ToString()) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } else if (UseTag) { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(SLTag.Apply(SLGame.SessionGet(bindParent.BindPath).Get(bindingValue), tagID), SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } else { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(SLGame.SessionGet(bindParent.BindPath).Get(bindingValue), SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } if (ignoreColorTag) { text.text = RemoveColorTag(text.text); } if (useTypingEffect) { StopAllCoroutines(); StartCoroutine(ShowText()); } if (useSmoothing) { StopAllCoroutines(); StartCoroutine(SmoothingText()); } } private void OnChangeImage() { var sessionRoot = SLGame.SessionGet(bindParent.BindPath); var newValue = sessionRoot.Get(bindingValue); if (UseTag) { newValue = SLTag.Apply(newValue, tagID); } if (oldValue == newValue) { return; } oldValue = newValue; image.sprite = SLResources.GetSprite(newValue); if (image.sprite == null) { SLLog.Error($"Can't Find Sprite. [{bindParent.BindPath}.{bindingValue}]: [{newValue}]"); } } private void OnChangeSlider() { var sessionRoot = SLGame.SessionGet(bindParent.BindPath); float newValue = sessionRoot.Get(bindingValue); float newMaxValue = SLGame.SessionGet(bindParent.BindPath).Get(bindingMaxValue); if (oldValue == newValue && oldMaxValue == newMaxValue) return; if (newValue < 0.0) { SLLog.Warn($"value can't negative. {bindParent.BindPath}.{bindingValue} : {newValue}", this); return; } if (newMaxValue <= 0.0) { SLLog.Warn($"max value can't negative or zero. {bindParent.BindPath}.{bindingMaxValue} : {newMaxValue}", this); return; } if (useSmoothing && smoothingValue < 0) { smoothingValue = oldValue; } oldValue = newValue; oldMaxValue = newMaxValue; if (useSmoothing) { StopAllCoroutines(); StartCoroutine(SmoothingSlider()); } else { slider.value = oldValue / oldMaxValue; slider.maxValue = 1; } } // TypingEffect private void OnSkipped() { var skipped = SLGame.SessionGet(bindParent.BindPath).Get(typingEndBind); if (skipped) { StopAllCoroutines(); if (useRaw) { text.text = SLString.GetString(bindingValue, SLGame.SessionGet(bindParent.BindPath)); } else if (userInput) { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLGame.SessionGet(bindParent.BindPath).Get(bindingValue).ToString()) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } else if (UseTag) { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(SLTag.Apply(SLGame.SessionGet(bindParent.BindPath).Get(bindingValue), tagID), SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } else { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(SLGame.SessionGet(bindParent.BindPath).Get(bindingValue), SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } if (string.IsNullOrEmpty(typingEndState) == false) { SLGame.Session["UIState"][typingEndState] = true; } } } private IEnumerator ShowText() { var originalText = text.text; var displayedText = new StringBuilder(); displayedText.Append(""); displayedText.Append(RemoveColorTag(originalText)); displayedText.Append(""); text.text = displayedText.ToString(); yield return new WaitForSeconds(startDelay); var currentIndex = -1; var index = -1; var openTags = new Stack(); var startTime = Time.time; while (true) { var elapsedTime = Time.time - startTime; var newIndex = Mathf.FloorToInt(elapsedTime / delay); if (currentIndex == -1) // 최초 0문자지만 여백을 다 채워두기 위한 트릭코드 { newIndex = 0; } if (newIndex > currentIndex) { for (var i = 0; i < newIndex - currentIndex; i++) { if (index + 1 >= originalText.Length) { break; } index += 1; while (index < originalText.Length && originalText[index] == '<') { var endIndex = originalText.IndexOf('>', index); var tag = originalText.Substring(index + 1, endIndex - index - 1); if (tag.StartsWith("/") == false) { openTags.Push(tag); } else { openTags.Pop(); } index = endIndex + 1; } } currentIndex = newIndex; displayedText.Clear(); displayedText.Append(originalText.Substring(0, Math.Min(originalText.Length, index + 1))); if (index < originalText.Length) { foreach (var tag in openTags) { var openTag = tag; if (openTag.Contains("=")) openTag = openTag.Substring(0, openTag.IndexOf("=")); displayedText.Append($""); } displayedText.Append(""); displayedText.Append(RemoveColorTag(originalText.Substring(index + 1, originalText.Length - index - 1))); displayedText.Append(""); } text.text = displayedText.ToString(); SLSound.PlaySound(textSound, "TextSound", false, false, true, 0.5f); } if (index + 1 >= originalText.Length) { break; } yield return null; } if (bindParent.Active && string.IsNullOrEmpty(typingEndBind) == false) { SLGame.SessionGet(bindParent.BindPath).Set(typingEndBind, true); } if (string.IsNullOrEmpty(typingEndState) == false) { SLGame.Session["UIState"][typingEndState] = true; } } private string RemoveColorTag(string text) { var result = new StringBuilder(text.Length); var index = 0; while (index < text.Length) { if (text[index] == '<' && text.Substring(index, 6).Equals("', index) + 1; index = endIndex; } else if (text[index] == '<' && text.Substring(index, 7).Equals("', index) + 1; index = endIndex; } else { result.Append(text[index]); index++; } } return result.ToString(); } private IEnumerator SmoothingSlider() { var startTime = Time.time; while (true) { var time = Time.time - startTime; smoothingValue = Mathf.Lerp(smoothingValue, oldValue, time / smoothingDelay); if (time >= smoothingDelay) { break; } slider.value = smoothingValue / oldMaxValue; slider.maxValue = 1; yield return null; } slider.value = oldValue / oldMaxValue; slider.maxValue = 1; } private IEnumerator SmoothingText() { var startTime = Time.time; smoothingValue = SLGame.SessionGet(bindParent.BindPath).Get(beforeSmoothingValue); float destValue = SLGame.SessionGet(bindParent.BindPath).Get(bindingValue); while (true) { var time = Time.time - startTime; smoothingValue = Mathf.Lerp(smoothingValue, destValue, time / smoothingDelay); if (time >= smoothingDelay) { break; } if (useRaw) { text.text = smoothingValue.ToString(); } else if (userInput) { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(smoothingValue.ToString()) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } else if (UseTag) { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(SLTag.Apply(smoothingValue, tagID), SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } else { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(smoothingValue.ToString(), SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } yield return null; } if (useRaw) { text.text = SLString.GetString(bindingValue, SLGame.SessionGet(bindParent.BindPath)); } else if (userInput) { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLGame.SessionGet(bindParent.BindPath).Get(bindingValue).ToString()) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } else if (UseTag) { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(SLTag.Apply(SLGame.SessionGet(bindParent.BindPath).Get(bindingValue), tagID), SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } else { text.text = new StringBuilder(SLString.GetString(prefix, SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(SLGame.SessionGet(bindParent.BindPath).Get(bindingValue), SLGame.SessionGet(bindParent.BindPath))) .Append(SLString.GetString(postfix, SLGame.SessionGet(bindParent.BindPath))).ToString(); } } } }