DOTweenTextMeshPro.cs 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  1. // Author: Daniele Giardini - http://www.demigiant.com
  2. // Created: 2015/03/27 19:02
  3. //
  4. // License Copyright (c) Daniele Giardini.
  5. // This work is subject to the terms at http://dotween.demigiant.com/license.php
  6. #if false // MODULE_MARKER
  7. using System;
  8. using System.Globalization;
  9. using System.Collections.Generic;
  10. using DG.Tweening.Core;
  11. using DG.Tweening.Plugins.Options;
  12. using UnityEngine;
  13. using TMPro;
  14. using Object = UnityEngine.Object;
  15. namespace DG.Tweening
  16. {
  17. public enum TMPSkewSpanMode
  18. {
  19. /// <summary>Applies the skew as-is (like normal skew works): the longer the text-span the higher the last character will be</summary>
  20. Default,
  21. /// <summary>Applies the skew scaled by the size of the text-span: the max skew/displacement will be the given skew factor</summary>
  22. AsMaxSkewFactor
  23. }
  24. /// <summary>
  25. /// Methods that extend TMP_Text objects and allow to directly create and control tweens from their instances.
  26. /// </summary>
  27. public static class ShortcutExtensionsTMPText
  28. {
  29. #region Colors
  30. /// <summary>Tweens a TextMeshPro's color to the given value.
  31. /// Also stores the TextMeshPro as the tween's target so it can be used for filtered operations</summary>
  32. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  33. public static TweenerCore<Color, Color, ColorOptions> DOColor(this TMP_Text target, Color endValue, float duration)
  34. {
  35. TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
  36. t.SetTarget(target);
  37. return t;
  38. }
  39. /// <summary>Tweens a TextMeshPro's faceColor to the given value.
  40. /// Also stores the TextMeshPro as the tween's target so it can be used for filtered operations</summary>
  41. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  42. public static TweenerCore<Color, Color, ColorOptions> DOFaceColor(this TMP_Text target, Color32 endValue, float duration)
  43. {
  44. TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.faceColor, x => target.faceColor = x, endValue, duration);
  45. t.SetTarget(target);
  46. return t;
  47. }
  48. /// <summary>Tweens a TextMeshPro's outlineColor to the given value.
  49. /// Also stores the TextMeshPro as the tween's target so it can be used for filtered operations</summary>
  50. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  51. public static TweenerCore<Color, Color, ColorOptions> DOOutlineColor(this TMP_Text target, Color32 endValue, float duration)
  52. {
  53. TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.outlineColor, x => target.outlineColor = x, endValue, duration);
  54. t.SetTarget(target);
  55. return t;
  56. }
  57. /// <summary>Tweens a TextMeshPro's glow color to the given value.
  58. /// Also stores the TextMeshPro as the tween's target so it can be used for filtered operations</summary>
  59. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  60. /// <param name="useSharedMaterial">If TRUE will use the fontSharedMaterial instead than the fontMaterial</param>
  61. public static TweenerCore<Color, Color, ColorOptions> DOGlowColor(this TMP_Text target, Color endValue, float duration, bool useSharedMaterial = false)
  62. {
  63. TweenerCore<Color, Color, ColorOptions> t = useSharedMaterial
  64. ? target.fontSharedMaterial.DOColor(endValue, "_GlowColor", duration)
  65. : target.fontMaterial.DOColor(endValue, "_GlowColor", duration);
  66. t.SetTarget(target);
  67. return t;
  68. }
  69. /// <summary>Tweens a TextMeshPro's alpha color to the given value.
  70. /// Also stores the TextMeshPro as the tween's target so it can be used for filtered operations</summary>
  71. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  72. public static TweenerCore<Color, Color, ColorOptions> DOFade(this TMP_Text target, float endValue, float duration)
  73. {
  74. TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
  75. t.SetTarget(target);
  76. return t;
  77. }
  78. /// <summary>Tweens a TextMeshPro faceColor's alpha to the given value.
  79. /// Also stores the TextMeshPro as the tween's target so it can be used for filtered operations</summary>
  80. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  81. public static TweenerCore<Color, Color, ColorOptions> DOFaceFade(this TMP_Text target, float endValue, float duration)
  82. {
  83. TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.faceColor, x => target.faceColor = x, endValue, duration);
  84. t.SetTarget(target);
  85. return t;
  86. }
  87. #endregion
  88. #region Other
  89. /// <summary>Tweens a TextMeshPro's scale to the given value (using correct uniform scale as TMP requires).
  90. /// Also stores the TextMeshPro as the tween's target so it can be used for filtered operations</summary>
  91. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  92. public static TweenerCore<Vector3, Vector3, VectorOptions> DOScale(this TMP_Text target, float endValue, float duration)
  93. {
  94. Transform trans = target.transform;
  95. Vector3 endValueV3 = new Vector3(endValue, endValue, endValue);
  96. TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => trans.localScale, x => trans.localScale = x, endValueV3, duration);
  97. t.SetTarget(target);
  98. return t;
  99. }
  100. /// <summary>
  101. /// Tweens a TextMeshPro's text from one integer to another, with options for thousands separators
  102. /// </summary>
  103. /// <param name="fromValue">The value to start from</param>
  104. /// <param name="endValue">The end value to reach</param>
  105. /// <param name="duration">The duration of the tween</param>
  106. /// <param name="addThousandsSeparator">If TRUE (default) also adds thousands separators</param>
  107. /// <param name="culture">The <see cref="CultureInfo"/> to use (InvariantCulture if NULL)</param>
  108. public static TweenerCore<int, int, NoOptions> DOCounter(
  109. this TMP_Text target, int fromValue, int endValue, float duration, bool addThousandsSeparator = true, CultureInfo culture = null
  110. ){
  111. int v = fromValue;
  112. CultureInfo cInfo = !addThousandsSeparator ? null : culture ?? CultureInfo.InvariantCulture;
  113. TweenerCore<int, int, NoOptions> t = DOTween.To(() => v, x => {
  114. v = x;
  115. target.text = addThousandsSeparator
  116. ? v.ToString("N0", cInfo)
  117. : v.ToString();
  118. }, endValue, duration);
  119. t.SetTarget(target);
  120. return t;
  121. }
  122. /// <summary>Tweens a TextMeshPro's fontSize to the given value.
  123. /// Also stores the TextMeshPro as the tween's target so it can be used for filtered operations</summary>
  124. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  125. public static TweenerCore<float, float, FloatOptions> DOFontSize(this TMP_Text target, float endValue, float duration)
  126. {
  127. TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.fontSize, x => target.fontSize = x, endValue, duration);
  128. t.SetTarget(target);
  129. return t;
  130. }
  131. /// <summary>Tweens a TextMeshPro's maxVisibleCharacters to the given value.
  132. /// Also stores the TextMeshPro as the tween's target so it can be used for filtered operations</summary>
  133. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  134. public static TweenerCore<int, int, NoOptions> DOMaxVisibleCharacters(this TMP_Text target, int endValue, float duration)
  135. {
  136. TweenerCore<int, int, NoOptions> t = DOTween.To(() => target.maxVisibleCharacters, x => target.maxVisibleCharacters = x, endValue, duration);
  137. t.SetTarget(target);
  138. return t;
  139. }
  140. /// <summary>Tweens a TextMeshPro's text to the given value.
  141. /// Also stores the TextMeshPro as the tween's target so it can be used for filtered operations</summary>
  142. /// <param name="endValue">The end string to tween to</param><param name="duration">The duration of the tween</param>
  143. /// <param name="richTextEnabled">If TRUE (default), rich text will be interpreted correctly while animated,
  144. /// otherwise all tags will be considered as normal text</param>
  145. /// <param name="scrambleMode">The type of scramble mode to use, if any</param>
  146. /// <param name="scrambleChars">A string containing the characters to use for scrambling.
  147. /// Use as many characters as possible (minimum 10) because DOTween uses a fast scramble mode which gives better results with more characters.
  148. /// Leave it to NULL (default) to use default ones</param>
  149. public static TweenerCore<string, string, StringOptions> DOText(this TMP_Text target, string endValue, float duration, bool richTextEnabled = true, ScrambleMode scrambleMode = ScrambleMode.None, string scrambleChars = null)
  150. {
  151. TweenerCore<string, string, StringOptions> t = DOTween.To(() => target.text, x => target.text = x, endValue, duration);
  152. t.SetOptions(richTextEnabled, scrambleMode, scrambleChars)
  153. .SetTarget(target);
  154. return t;
  155. }
  156. #endregion
  157. }
  158. #region DOTweenTMPAnimator
  159. // █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
  160. // ███ CLASS ███████████████████████████████████████████████████████████████████████████████████████████████████████████
  161. // █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
  162. /// <summary>
  163. /// Wrapper for <see cref="TMP_Text"/> objects that enables per-character tweening
  164. /// (you don't need this if instead you want to animate the whole text object).
  165. /// It also contains various handy methods to simply deform text without animating it ;)
  166. /// <para><code>EXAMPLE:<para/>
  167. /// DOTweenTMPAnimator animator = new DOTweenTMPAnimator(myTextMeshProTextField);<para/>
  168. /// Tween tween = animator.DOCharScale(characterIndex, scaleValue, duration);
  169. /// </code></para>
  170. /// </summary>
  171. public class DOTweenTMPAnimator : IDisposable
  172. {
  173. /// <summary><see cref="TMP_Text"/> that this animator is linked to</summary>
  174. public TMP_Text target { get; private set; }
  175. public TMP_TextInfo textInfo { get; private set; }
  176. readonly List<CharTransform> _charTransforms = new List<CharTransform>();
  177. TMP_MeshInfo[] _cachedMeshInfos;
  178. bool _ignoreTextChangedEvent;
  179. /// <summary>
  180. /// Creates a new instance of the <see cref="DOTweenTMPAnimator"/>, which is necessary to animate <see cref="TMP_Text"/> by single characters.
  181. /// If you want to animate the whole text object you don't need this, and you can use direct <see cref="TMP_Text"/> DO shortcuts instead.<para/>
  182. /// IMPORTANT: the <see cref="TMP_Text"/> target must have been enabled/activated at least once before you can use it with this
  183. /// </summary>
  184. /// <param name="target">The <see cref="TMP_Text"/> that will be linked to this animator</param>
  185. public DOTweenTMPAnimator(TMP_Text target)
  186. {
  187. if (!target.gameObject.activeInHierarchy) {
  188. Debugger.LogError("You can't create a DOTweenTMPAnimator if its target is disabled");
  189. return;
  190. }
  191. this.target = target;
  192. Refresh();
  193. // Listeners
  194. TMPro_EventManager.TEXT_CHANGED_EVENT.Add(OnTextChanged);
  195. }
  196. /// <summary>
  197. /// Clears and disposes of this object
  198. /// </summary>
  199. public void Dispose()
  200. {
  201. target = null;
  202. _charTransforms.Clear();
  203. textInfo = null;
  204. _cachedMeshInfos = null;
  205. TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(OnTextChanged);
  206. }
  207. /// <summary>
  208. /// Refreshes the animator text data and resets all transformation data. Call this after you change the target <see cref="TMP_Text"/>
  209. /// </summary>
  210. public void Refresh()
  211. {
  212. _ignoreTextChangedEvent = true;
  213. target.ForceMeshUpdate(true);
  214. textInfo = target.textInfo;
  215. _cachedMeshInfos = textInfo.CopyMeshInfoVertexData();
  216. int totChars = textInfo.characterCount;
  217. int totCurrent = _charTransforms.Count;
  218. if (totCurrent > totChars) {
  219. _charTransforms.RemoveRange(totChars, totCurrent - totChars);
  220. totCurrent = totChars;
  221. }
  222. for (int i = 0; i < totCurrent; ++i) {
  223. CharTransform c = _charTransforms[i];
  224. c.ResetTransformationData();
  225. c.Refresh(textInfo, _cachedMeshInfos);
  226. _charTransforms[i] = c;
  227. }
  228. for (int i = totCurrent; i < totChars; ++i) _charTransforms.Add(new CharTransform(i, textInfo, _cachedMeshInfos));
  229. _ignoreTextChangedEvent = false;
  230. }
  231. /// <summary>
  232. /// Resets all deformations
  233. /// </summary>
  234. public void Reset()
  235. {
  236. int totCurrent = _charTransforms.Count;
  237. for (int i = 0; i < totCurrent; ++i) _charTransforms[i].ResetAll(target, textInfo.meshInfo, _cachedMeshInfos);
  238. }
  239. void OnTextChanged(Object obj)
  240. {
  241. if (_ignoreTextChangedEvent || target == null || obj != target) return;
  242. Refresh();
  243. }
  244. bool ValidateChar(int charIndex, bool isTween = true)
  245. {
  246. if (textInfo.characterCount <= charIndex) {
  247. Debugger.LogError(string.Format("CharIndex {0} doesn't exist", charIndex));
  248. return false;
  249. }
  250. if (!textInfo.characterInfo[charIndex].isVisible) {
  251. if (Debugger.logPriority > 1) {
  252. if (isTween) {
  253. Debugger.Log(string.Format(
  254. "CharIndex {0} isn't visible, ignoring it and returning an empty tween (TextMesh Pro will behave weirdly if invisible chars are included in the animation)",
  255. charIndex
  256. ));
  257. } else {
  258. Debugger.Log(string.Format("CharIndex {0} isn't visible, ignoring it", charIndex));
  259. }
  260. }
  261. return false;
  262. }
  263. return true;
  264. }
  265. bool ValidateSpan(int fromCharIndex, int toCharIndex, out int firstVisibleCharIndex, out int lastVisibleCharIndex)
  266. {
  267. firstVisibleCharIndex = -1; // First visible/existing charIndex from given index
  268. lastVisibleCharIndex = -1; // Last visible/existing charIndex backwards from given index
  269. int charCount = textInfo.characterCount;
  270. if (fromCharIndex >= charCount) return false;
  271. if (toCharIndex >= charCount) toCharIndex = charCount - 1;
  272. for (int i = fromCharIndex; i < toCharIndex + 1; ++i) {
  273. if (!_charTransforms[i].isVisible) continue;
  274. firstVisibleCharIndex = i;
  275. break;
  276. }
  277. if (firstVisibleCharIndex == -1) return false;
  278. for (int i = toCharIndex; i > firstVisibleCharIndex - 1; --i) {
  279. if (!_charTransforms[i].isVisible) continue;
  280. lastVisibleCharIndex = i;
  281. break;
  282. }
  283. if (lastVisibleCharIndex == -1) return false;
  284. return true;
  285. }
  286. #region Word Setters
  287. /// <summary>
  288. /// Skews a span of characters uniformly (like normal skew works in graphic applications)
  289. /// </summary>
  290. /// <param name="fromCharIndex">First char index of the span to skew</param>
  291. /// <param name="toCharIndex">Last char index of the span to skew</param>
  292. /// <param name="skewFactor">Skew factor</param>
  293. /// <param name="skewTop">If TRUE skews the top side of the span, otherwise the bottom one</param>
  294. public void SkewSpanX(int fromCharIndex, int toCharIndex, float skewFactor, bool skewTop = true)
  295. {
  296. int firstVisibleCharIndex, lastVisibleCharIndex;
  297. if (!ValidateSpan(fromCharIndex, toCharIndex, out firstVisibleCharIndex, out lastVisibleCharIndex)) return;
  298. for (int i = firstVisibleCharIndex; i < lastVisibleCharIndex + 1; ++i) {
  299. if (!_charTransforms[i].isVisible) continue;
  300. CharVertices v = _charTransforms[i].GetVertices();
  301. float skew = SkewCharX(i, skewFactor, skewTop);
  302. }
  303. }
  304. /// <summary>
  305. /// Skews a span of characters uniformly (like normal skew works in graphic applications)
  306. /// </summary>
  307. /// <param name="fromCharIndex">First char index of the span to skew</param>
  308. /// <param name="toCharIndex">Last char index of the span to skew</param>
  309. /// <param name="skewFactor">Skew factor</param>
  310. /// <param name="mode">Skew mode</param>
  311. /// <param name="skewRight">If TRUE skews the right side of the span, otherwise the left one</param>
  312. public void SkewSpanY(
  313. int fromCharIndex, int toCharIndex, float skewFactor,
  314. TMPSkewSpanMode mode = TMPSkewSpanMode.Default, bool skewRight = true
  315. ){
  316. int firstVisibleCharIndex, lastVisibleCharIndex;
  317. if (!ValidateSpan(fromCharIndex, toCharIndex, out firstVisibleCharIndex, out lastVisibleCharIndex)) return;
  318. if (mode == TMPSkewSpanMode.AsMaxSkewFactor) {
  319. CharVertices firstVisibleCharVertices = _charTransforms[firstVisibleCharIndex].GetVertices();
  320. CharVertices lastVisibleCharVertices = _charTransforms[lastVisibleCharIndex].GetVertices();
  321. float spanW = Mathf.Abs(lastVisibleCharVertices.bottomRight.x - firstVisibleCharVertices.bottomLeft.x);
  322. float spanH = Mathf.Abs(lastVisibleCharVertices.topRight.y - lastVisibleCharVertices.bottomRight.y);
  323. float ratio = spanH / spanW;
  324. skewFactor *= ratio;
  325. }
  326. float offsetY = 0;
  327. CharVertices prevCharVertices = new CharVertices();
  328. float prevCharSkew = 0;
  329. if (skewRight) {
  330. for (int i = firstVisibleCharIndex; i < lastVisibleCharIndex + 1; ++i) {
  331. if (!_charTransforms[i].isVisible) continue;
  332. CharVertices v = _charTransforms[i].GetVertices();
  333. float skew = SkewCharY(i, skewFactor, skewRight);
  334. if (i > firstVisibleCharIndex) {
  335. float prevCharW = Mathf.Abs(prevCharVertices.bottomLeft.x - prevCharVertices.bottomRight.x);
  336. float charsDist = Mathf.Abs(v.bottomLeft.x - prevCharVertices.bottomRight.x);
  337. offsetY += prevCharSkew + (prevCharSkew * charsDist) / prevCharW;
  338. SetCharOffset(i, new Vector3(0, _charTransforms[i].offset.y + offsetY, 0));
  339. }
  340. prevCharVertices = v;
  341. prevCharSkew = skew;
  342. }
  343. } else {
  344. for (int i = lastVisibleCharIndex; i > firstVisibleCharIndex - 1; --i) {
  345. if (!_charTransforms[i].isVisible) continue;
  346. CharVertices v = _charTransforms[i].GetVertices();
  347. float skew = SkewCharY(i, skewFactor, skewRight);
  348. if (i < lastVisibleCharIndex) {
  349. float prevCharW = Mathf.Abs(prevCharVertices.bottomLeft.x - prevCharVertices.bottomRight.x);
  350. float charsDist = Mathf.Abs(v.bottomRight.x - prevCharVertices.bottomLeft.x);
  351. offsetY += prevCharSkew + (prevCharSkew * charsDist) / prevCharW;
  352. SetCharOffset(i, new Vector3(0, _charTransforms[i].offset.y + offsetY, 0));
  353. }
  354. prevCharVertices = v;
  355. prevCharSkew = skew;
  356. }
  357. }
  358. }
  359. #endregion
  360. #region Char Getters
  361. /// <summary>
  362. /// Returns the current color of the given character, if it exists and is visible.
  363. /// </summary>
  364. /// <param name="charIndex">Character index</param>
  365. public Color GetCharColor(int charIndex)
  366. {
  367. if (!ValidateChar(charIndex)) return Color.white;
  368. return _charTransforms[charIndex].GetColor(textInfo.meshInfo);
  369. }
  370. /// <summary>
  371. /// Returns the current offset of the given character, if it exists and is visible.
  372. /// </summary>
  373. /// <param name="charIndex">Character index</param>
  374. public Vector3 GetCharOffset(int charIndex)
  375. {
  376. if (!ValidateChar(charIndex)) return Vector3.zero;
  377. return _charTransforms[charIndex].offset;
  378. }
  379. /// <summary>
  380. /// Returns the current rotation of the given character, if it exists and is visible.
  381. /// </summary>
  382. /// <param name="charIndex">Character index</param>
  383. public Vector3 GetCharRotation(int charIndex)
  384. {
  385. if (!ValidateChar(charIndex)) return Vector3.zero;
  386. return _charTransforms[charIndex].rotation.eulerAngles;
  387. }
  388. /// <summary>
  389. /// Returns the current scale of the given character, if it exists and is visible.
  390. /// </summary>
  391. /// <param name="charIndex">Character index</param>
  392. public Vector3 GetCharScale(int charIndex)
  393. {
  394. if (!ValidateChar(charIndex)) return Vector3.zero;
  395. return _charTransforms[charIndex].scale;
  396. }
  397. #endregion
  398. #region Char Setters
  399. /// <summary>
  400. /// Immediately sets the color of the given character.
  401. /// Will do nothing if the <see cref="charIndex"/> is invalid or the character isn't visible
  402. /// </summary>
  403. /// <param name="charIndex">Character index</param>
  404. /// <param name="color">Color to set</param>
  405. public void SetCharColor(int charIndex, Color32 color)
  406. {
  407. if (!ValidateChar(charIndex)) return;
  408. CharTransform c = _charTransforms[charIndex];
  409. c.UpdateColor(target, color, textInfo.meshInfo);
  410. _charTransforms[charIndex] = c;
  411. }
  412. /// <summary>
  413. /// Immediately sets the offset of the given character.
  414. /// Will do nothing if the <see cref="charIndex"/> is invalid or the character isn't visible
  415. /// </summary>
  416. /// <param name="charIndex">Character index</param>
  417. /// <param name="offset">Offset to set</param>
  418. public void SetCharOffset(int charIndex, Vector3 offset)
  419. {
  420. if (!ValidateChar(charIndex)) return;
  421. CharTransform c = _charTransforms[charIndex];
  422. c.UpdateGeometry(target, offset, c.rotation, c.scale, _cachedMeshInfos);
  423. _charTransforms[charIndex] = c;
  424. }
  425. /// <summary>
  426. /// Immediately sets the rotation of the given character.
  427. /// Will do nothing if the <see cref="charIndex"/> is invalid or the character isn't visible
  428. /// </summary>
  429. /// <param name="charIndex">Character index</param>
  430. /// <param name="rotation">Rotation to set</param>
  431. public void SetCharRotation(int charIndex, Vector3 rotation)
  432. {
  433. if (!ValidateChar(charIndex)) return;
  434. CharTransform c = _charTransforms[charIndex];
  435. c.UpdateGeometry(target, c.offset, Quaternion.Euler(rotation), c.scale, _cachedMeshInfos);
  436. _charTransforms[charIndex] = c;
  437. }
  438. /// <summary>
  439. /// Immediately sets the scale of the given character.
  440. /// Will do nothing if the <see cref="charIndex"/> is invalid or the character isn't visible
  441. /// </summary>
  442. /// <param name="charIndex">Character index</param>
  443. /// <param name="scale">Scale to set</param>
  444. public void SetCharScale(int charIndex, Vector3 scale)
  445. {
  446. if (!ValidateChar(charIndex)) return;
  447. CharTransform c = _charTransforms[charIndex];
  448. c.UpdateGeometry(target, c.offset, c.rotation, scale, _cachedMeshInfos);
  449. _charTransforms[charIndex] = c;
  450. }
  451. /// <summary>
  452. /// Immediately shifts the vertices of the given character by the given factor.
  453. /// Will do nothing if the <see cref="charIndex"/> is invalid or the character isn't visible
  454. /// </summary>
  455. /// <param name="charIndex">Character index</param>
  456. /// <param name="topLeftShift">Top left offset</param>
  457. /// <param name="topRightShift">Top right offset</param>
  458. /// <param name="bottomLeftShift">Bottom left offset</param>
  459. /// <param name="bottomRightShift">Bottom right offset</param>
  460. public void ShiftCharVertices(int charIndex, Vector3 topLeftShift, Vector3 topRightShift, Vector3 bottomLeftShift, Vector3 bottomRightShift)
  461. {
  462. if (!ValidateChar(charIndex)) return;
  463. CharTransform c = _charTransforms[charIndex];
  464. c.ShiftVertices(target, topLeftShift, topRightShift, bottomLeftShift, bottomRightShift);
  465. _charTransforms[charIndex] = c;
  466. }
  467. /// <summary>
  468. /// Skews the given character horizontally along the X axis and returns the skew amount applied (based on the character's size)
  469. /// </summary>
  470. /// <param name="charIndex">Character index</param>
  471. /// <param name="skewFactor">skew amount</param>
  472. /// <param name="skewTop">If TRUE skews the top side of the character, otherwise the bottom one</param>
  473. public float SkewCharX(int charIndex, float skewFactor, bool skewTop = true)
  474. {
  475. if (!ValidateChar(charIndex)) return 0;
  476. Vector3 skewV = new Vector3(skewFactor, 0, 0);
  477. CharTransform c = _charTransforms[charIndex];
  478. if (skewTop) c.ShiftVertices(target, skewV, skewV, Vector3.zero, Vector3.zero);
  479. else c.ShiftVertices(target, Vector3.zero, Vector3.zero, skewV, skewV);
  480. _charTransforms[charIndex] = c;
  481. return skewFactor;
  482. }
  483. /// <summary>
  484. /// Skews the given character vertically along the Y axis and returns the skew amount applied (based on the character's size)
  485. /// </summary>
  486. /// <param name="charIndex">Character index</param>
  487. /// <param name="skewFactor">skew amount</param>
  488. /// <param name="skewRight">If TRUE skews the right side of the character, otherwise the left one</param>
  489. /// <param name="fixedSkew">If TRUE applies exactly the given <see cref="skewFactor"/>,
  490. /// otherwise modifies it based on the aspectRation of the character</param>
  491. public float SkewCharY(int charIndex, float skewFactor, bool skewRight = true, bool fixedSkew = false)
  492. {
  493. if (!ValidateChar(charIndex)) return 0;
  494. float skew = fixedSkew ? skewFactor : skewFactor * textInfo.characterInfo[charIndex].aspectRatio;
  495. Vector3 skewV = new Vector3(0, skew, 0);
  496. CharTransform c = _charTransforms[charIndex];
  497. if (skewRight) c.ShiftVertices(target, Vector3.zero, skewV, Vector3.zero, skewV);
  498. else c.ShiftVertices(target, skewV, Vector3.zero, skewV, Vector3.zero);
  499. _charTransforms[charIndex] = c;
  500. return skew;
  501. }
  502. /// <summary>
  503. /// Resets the eventual vertices shift applied to the given character via <see cref="ShiftCharVertices"/>.
  504. /// Will do nothing if the <see cref="charIndex"/> is invalid or the character isn't visible
  505. /// </summary>
  506. /// <param name="charIndex">Character index</param>
  507. public void ResetVerticesShift(int charIndex)
  508. {
  509. if (!ValidateChar(charIndex)) return;
  510. CharTransform c = _charTransforms[charIndex];
  511. c.ResetVerticesShift(target);
  512. _charTransforms[charIndex] = c;
  513. }
  514. #endregion
  515. #region Char Tweens
  516. /// <summary>Tweens a character's alpha to the given value and returns the <see cref="Tween"/>.
  517. /// Will return NULL if the <see cref="charIndex"/> is invalid or the character isn't visible.</summary>
  518. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  519. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  520. public TweenerCore<Color, Color, ColorOptions> DOFadeChar(int charIndex, float endValue, float duration)
  521. {
  522. if (!ValidateChar(charIndex)) return null;
  523. TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => _charTransforms[charIndex].GetColor(textInfo.meshInfo), x => {
  524. _charTransforms[charIndex].UpdateAlpha(target, x, textInfo.meshInfo);
  525. }, endValue, duration);
  526. return t;
  527. }
  528. /// <summary>Tweens a character's color to the given value and returns the <see cref="Tween"/>.
  529. /// Will return NULL if the <see cref="charIndex"/> is invalid or the character isn't visible.</summary>
  530. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  531. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  532. public TweenerCore<Color, Color, ColorOptions> DOColorChar(int charIndex, Color endValue, float duration)
  533. {
  534. if (!ValidateChar(charIndex)) return null;
  535. TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => _charTransforms[charIndex].GetColor(textInfo.meshInfo), x => {
  536. _charTransforms[charIndex].UpdateColor(target, x, textInfo.meshInfo);
  537. }, endValue, duration);
  538. return t;
  539. }
  540. /// <summary>Tweens a character's offset to the given value and returns the <see cref="Tween"/>.
  541. /// Will return NULL if the <see cref="charIndex"/> is invalid or the character isn't visible.</summary>
  542. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  543. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  544. public TweenerCore<Vector3, Vector3, VectorOptions> DOOffsetChar(int charIndex, Vector3 endValue, float duration)
  545. {
  546. if (!ValidateChar(charIndex)) return null;
  547. TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => _charTransforms[charIndex].offset, x => {
  548. CharTransform charT = _charTransforms[charIndex];
  549. charT.UpdateGeometry(target, x, charT.rotation, charT.scale, _cachedMeshInfos);
  550. _charTransforms[charIndex] = charT;
  551. }, endValue, duration);
  552. return t;
  553. }
  554. /// <summary>Tweens a character's rotation to the given value and returns the <see cref="Tween"/>.
  555. /// Will return NULL if the <see cref="charIndex"/> is invalid or the character isn't visible.</summary>
  556. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  557. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  558. /// <param name="mode">Rotation mode</param>
  559. public TweenerCore<Quaternion, Vector3, QuaternionOptions> DORotateChar(int charIndex, Vector3 endValue, float duration, RotateMode mode = RotateMode.Fast)
  560. {
  561. if (!ValidateChar(charIndex)) return null;
  562. TweenerCore<Quaternion, Vector3, QuaternionOptions> t = DOTween.To(() => _charTransforms[charIndex].rotation, x => {
  563. CharTransform charT = _charTransforms[charIndex];
  564. charT.UpdateGeometry(target, charT.offset, x, charT.scale, _cachedMeshInfos);
  565. _charTransforms[charIndex] = charT;
  566. }, endValue, duration);
  567. t.plugOptions.rotateMode = mode;
  568. return t;
  569. }
  570. /// <summary>Tweens a character's scale to the given value and returns the <see cref="Tween"/>.
  571. /// Will return NULL if the <see cref="charIndex"/> is invalid or the character isn't visible.</summary>
  572. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  573. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  574. public TweenerCore<Vector3, Vector3, VectorOptions> DOScaleChar(int charIndex, float endValue, float duration)
  575. {
  576. return DOScaleChar(charIndex, new Vector3(endValue, endValue, endValue), duration);
  577. }
  578. /// <summary>Tweens a character's color to the given value and returns the <see cref="Tween"/>.
  579. /// Will return NULL if the <see cref="charIndex"/> is invalid or the character isn't visible.</summary>
  580. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  581. /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
  582. public TweenerCore<Vector3, Vector3, VectorOptions> DOScaleChar(int charIndex, Vector3 endValue, float duration)
  583. {
  584. if (!ValidateChar(charIndex)) return null;
  585. TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => _charTransforms[charIndex].scale, x => {
  586. CharTransform charT = _charTransforms[charIndex];
  587. charT.UpdateGeometry(target, charT.offset, charT.rotation, x, _cachedMeshInfos);
  588. _charTransforms[charIndex] = charT;
  589. }, endValue, duration);
  590. return t;
  591. }
  592. /// <summary>Punches a character's offset towards the given direction and then back to the starting one
  593. /// as if it was connected to the starting position via an elastic.</summary>
  594. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  595. /// <param name="punch">The punch strength</param>
  596. /// <param name="duration">The duration of the tween</param>
  597. /// <param name="vibrato">Indicates how much will the punch vibrate per second</param>
  598. /// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting size when bouncing backwards.
  599. /// 1 creates a full oscillation between the punch offset and the opposite offset,
  600. /// while 0 oscillates only between the punch offset and the start offset</param>
  601. public Tweener DOPunchCharOffset(int charIndex, Vector3 punch, float duration, int vibrato = 10, float elasticity = 1)
  602. {
  603. if (!ValidateChar(charIndex)) return null;
  604. if (duration <= 0) {
  605. if (Debugger.logPriority > 0) Debug.LogWarning("Duration can't be 0, returning NULL without creating a tween");
  606. return null;
  607. }
  608. return DOTween.Punch(() => _charTransforms[charIndex].offset, x => {
  609. CharTransform charT = _charTransforms[charIndex];
  610. charT.UpdateGeometry(target, x, charT.rotation, charT.scale, _cachedMeshInfos);
  611. _charTransforms[charIndex] = charT;
  612. }, punch, duration, vibrato, elasticity);
  613. }
  614. /// <summary>Punches a character's rotation towards the given direction and then back to the starting one
  615. /// as if it was connected to the starting position via an elastic.</summary>
  616. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  617. /// <param name="punch">The punch strength</param>
  618. /// <param name="duration">The duration of the tween</param>
  619. /// <param name="vibrato">Indicates how much will the punch vibrate per second</param>
  620. /// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting size when bouncing backwards.
  621. /// 1 creates a full oscillation between the punch rotation and the opposite rotation,
  622. /// while 0 oscillates only between the punch rotation and the start rotation</param>
  623. public Tweener DOPunchCharRotation(int charIndex, Vector3 punch, float duration, int vibrato = 10, float elasticity = 1)
  624. {
  625. if (!ValidateChar(charIndex)) return null;
  626. if (duration <= 0) {
  627. if (Debugger.logPriority > 0) Debug.LogWarning("Duration can't be 0, returning NULL without creating a tween");
  628. return null;
  629. }
  630. return DOTween.Punch(() => _charTransforms[charIndex].rotation.eulerAngles, x => {
  631. CharTransform charT = _charTransforms[charIndex];
  632. charT.UpdateGeometry(target, charT.offset, Quaternion.Euler(x), charT.scale, _cachedMeshInfos);
  633. _charTransforms[charIndex] = charT;
  634. }, punch, duration, vibrato, elasticity);
  635. }
  636. /// <summary>Punches a character's scale towards the given direction and then back to the starting one
  637. /// as if it was connected to the starting position via an elastic.</summary>
  638. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  639. /// <param name="punch">The punch strength (added to the character's current scale)</param>
  640. /// <param name="duration">The duration of the tween</param>
  641. /// <param name="vibrato">Indicates how much will the punch vibrate per second</param>
  642. /// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting size when bouncing backwards.
  643. /// 1 creates a full oscillation between the punch scale and the opposite scale,
  644. /// while 0 oscillates only between the punch scale and the start scale</param>
  645. public Tweener DOPunchCharScale(int charIndex, float punch, float duration, int vibrato = 10, float elasticity = 1)
  646. {
  647. return DOPunchCharScale(charIndex, new Vector3(punch, punch, punch), duration, vibrato, elasticity);
  648. }
  649. /// <summary>Punches a character's scale towards the given direction and then back to the starting one
  650. /// as if it was connected to the starting position via an elastic.</summary>
  651. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  652. /// <param name="punch">The punch strength (added to the character's current scale)</param>
  653. /// <param name="duration">The duration of the tween</param>
  654. /// <param name="vibrato">Indicates how much will the punch vibrate per second</param>
  655. /// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting size when bouncing backwards.
  656. /// 1 creates a full oscillation between the punch scale and the opposite scale,
  657. /// while 0 oscillates only between the punch scale and the start scale</param>
  658. public Tweener DOPunchCharScale(int charIndex, Vector3 punch, float duration, int vibrato = 10, float elasticity = 1)
  659. {
  660. if (!ValidateChar(charIndex)) return null;
  661. if (duration <= 0) {
  662. if (Debugger.logPriority > 0) Debug.LogWarning("Duration can't be 0, returning NULL without creating a tween");
  663. return null;
  664. }
  665. return DOTween.Punch(() => _charTransforms[charIndex].scale, x => {
  666. CharTransform charT = _charTransforms[charIndex];
  667. charT.UpdateGeometry(target, charT.offset, charT.rotation, x, _cachedMeshInfos);
  668. _charTransforms[charIndex] = charT;
  669. }, punch, duration, vibrato, elasticity);
  670. }
  671. /// <summary>Shakes a character's offset with the given values.</summary>
  672. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  673. /// <param name="duration">The duration of the tween</param>
  674. /// <param name="strength">The shake strength</param>
  675. /// <param name="vibrato">Indicates how much will the shake vibrate</param>
  676. /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
  677. /// Setting it to 0 will shake along a single direction.</param>
  678. /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
  679. public Tweener DOShakeCharOffset(int charIndex, float duration, float strength, int vibrato = 10, float randomness = 90, bool fadeOut = true)
  680. {
  681. return DOShakeCharOffset(charIndex, duration, new Vector3(strength, strength, strength), vibrato, randomness, fadeOut);
  682. }
  683. /// <summary>Shakes a character's offset with the given values.</summary>
  684. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  685. /// <param name="duration">The duration of the tween</param>
  686. /// <param name="strength">The shake strength</param>
  687. /// <param name="vibrato">Indicates how much will the shake vibrate</param>
  688. /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
  689. /// Setting it to 0 will shake along a single direction.</param>
  690. /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
  691. public Tweener DOShakeCharOffset(int charIndex, float duration, Vector3 strength, int vibrato = 10, float randomness = 90, bool fadeOut = true)
  692. {
  693. if (!ValidateChar(charIndex)) return null;
  694. if (duration <= 0) {
  695. if (Debugger.logPriority > 0) Debug.LogWarning("Duration can't be 0, returning NULL without creating a tween");
  696. return null;
  697. }
  698. return DOTween.Shake(() => _charTransforms[charIndex].offset, x => {
  699. CharTransform charT = _charTransforms[charIndex];
  700. charT.UpdateGeometry(target, x, charT.rotation, charT.scale, _cachedMeshInfos);
  701. _charTransforms[charIndex] = charT;
  702. }, duration, strength, vibrato, randomness, fadeOut);
  703. }
  704. /// <summary>Shakes a character's rotation with the given values.</summary>
  705. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  706. /// <param name="duration">The duration of the tween</param>
  707. /// <param name="strength">The shake strength</param>
  708. /// <param name="vibrato">Indicates how much will the shake vibrate</param>
  709. /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
  710. /// Setting it to 0 will shake along a single direction.</param>
  711. /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
  712. public Tweener DOShakeCharRotation(int charIndex, float duration, Vector3 strength, int vibrato = 10, float randomness = 90, bool fadeOut = true)
  713. {
  714. if (!ValidateChar(charIndex)) return null;
  715. if (duration <= 0) {
  716. if (Debugger.logPriority > 0) Debug.LogWarning("Duration can't be 0, returning NULL without creating a tween");
  717. return null;
  718. }
  719. return DOTween.Shake(() => _charTransforms[charIndex].rotation.eulerAngles, x => {
  720. CharTransform charT = _charTransforms[charIndex];
  721. charT.UpdateGeometry(target, charT.offset, Quaternion.Euler(x), charT.scale, _cachedMeshInfos);
  722. _charTransforms[charIndex] = charT;
  723. }, duration, strength, vibrato, randomness, fadeOut);
  724. }
  725. /// <summary>Shakes a character's scale with the given values.</summary>
  726. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  727. /// <param name="duration">The duration of the tween</param>
  728. /// <param name="strength">The shake strength</param>
  729. /// <param name="vibrato">Indicates how much will the shake vibrate</param>
  730. /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
  731. /// Setting it to 0 will shake along a single direction.</param>
  732. /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
  733. public Tweener DOShakeCharScale(int charIndex, float duration, float strength, int vibrato = 10, float randomness = 90, bool fadeOut = true)
  734. {
  735. return DOShakeCharScale(charIndex, duration, new Vector3(strength, strength, strength), vibrato, randomness, fadeOut);
  736. }
  737. /// <summary>Shakes a character's scale with the given values.</summary>
  738. /// <param name="charIndex">The index of the character to tween (will throw an error if it doesn't exist)</param>
  739. /// <param name="duration">The duration of the tween</param>
  740. /// <param name="strength">The shake strength</param>
  741. /// <param name="vibrato">Indicates how much will the shake vibrate</param>
  742. /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
  743. /// Setting it to 0 will shake along a single direction.</param>
  744. /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
  745. public Tweener DOShakeCharScale(int charIndex, float duration, Vector3 strength, int vibrato = 10, float randomness = 90, bool fadeOut = true)
  746. {
  747. if (!ValidateChar(charIndex)) return null;
  748. if (duration <= 0) {
  749. if (Debugger.logPriority > 0) Debug.LogWarning("Duration can't be 0, returning NULL without creating a tween");
  750. return null;
  751. }
  752. return DOTween.Shake(() => _charTransforms[charIndex].scale, x => {
  753. CharTransform charT = _charTransforms[charIndex];
  754. charT.UpdateGeometry(target, charT.offset, charT.rotation, x, _cachedMeshInfos);
  755. _charTransforms[charIndex] = charT;
  756. }, duration, strength, vibrato, randomness, fadeOut);
  757. }
  758. #endregion
  759. // ███ INTERNAL CLASSES ████████████████████████████████████████████████████████████████████████████████████████████████
  760. struct CharVertices
  761. {
  762. public Vector3 bottomLeft, topLeft, topRight, bottomRight;
  763. public CharVertices(Vector3 bottomLeft, Vector3 topLeft, Vector3 topRight, Vector3 bottomRight)
  764. {
  765. this.bottomLeft = bottomLeft;
  766. this.topLeft = topLeft;
  767. this.topRight = topRight;
  768. this.bottomRight = bottomRight;
  769. }
  770. }
  771. // █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
  772. // Vertices of each character are:
  773. // 0 : bottom left, 1 : top left, 2 : top right, 3 : bottom right
  774. struct CharTransform
  775. {
  776. public int charIndex;
  777. public bool isVisible { get; private set; } // FALSE both if it's invisible or if it's a space
  778. public Vector3 offset;
  779. public Quaternion rotation;
  780. public Vector3 scale;
  781. Vector3 _topLeftShift, _topRightShift, _bottomLeftShift, _bottomRightShift;
  782. Vector3 _charMidBaselineOffset;
  783. int _matIndex, _firstVertexIndex;
  784. TMP_MeshInfo _meshInfo;
  785. public CharTransform(int charIndex, TMP_TextInfo textInfo, TMP_MeshInfo[] cachedMeshInfos) : this()
  786. {
  787. this.charIndex = charIndex;
  788. offset = Vector3.zero;
  789. rotation = Quaternion.identity;
  790. scale = Vector3.one;
  791. Refresh(textInfo, cachedMeshInfos);
  792. }
  793. public void Refresh(TMP_TextInfo textInfo, TMP_MeshInfo[] cachedMeshInfos)
  794. {
  795. TMP_CharacterInfo charInfo = textInfo.characterInfo[charIndex];
  796. bool isSpaceChar = charInfo.character == ' ';
  797. isVisible = charInfo.isVisible && !isSpaceChar;
  798. _matIndex = charInfo.materialReferenceIndex;
  799. _firstVertexIndex = charInfo.vertexIndex;
  800. _meshInfo = textInfo.meshInfo[_matIndex];
  801. Vector3[] cachedVertices = cachedMeshInfos[_matIndex].vertices;
  802. _charMidBaselineOffset = isSpaceChar
  803. ? Vector3.zero
  804. : (cachedVertices[_firstVertexIndex] + cachedVertices[_firstVertexIndex + 2]) * 0.5f;
  805. }
  806. public void ResetAll(TMP_Text target, TMP_MeshInfo[] meshInfos, TMP_MeshInfo[] cachedMeshInfos)
  807. {
  808. ResetGeometry(target, cachedMeshInfos);
  809. ResetColors(target, meshInfos);
  810. }
  811. public void ResetTransformationData()
  812. {
  813. offset = Vector3.zero;
  814. rotation = Quaternion.identity;
  815. scale = Vector3.one;
  816. _topLeftShift = _topRightShift = _bottomLeftShift = _bottomRightShift = Vector3.zero;
  817. }
  818. public void ResetGeometry(TMP_Text target, TMP_MeshInfo[] cachedMeshInfos)
  819. {
  820. ResetTransformationData();
  821. Vector3[] destinationVertices = _meshInfo.vertices;
  822. Vector3[] cachedVertices = cachedMeshInfos[_matIndex].vertices;
  823. destinationVertices[_firstVertexIndex + 0] = cachedVertices[_firstVertexIndex + 0];
  824. destinationVertices[_firstVertexIndex + 1] = cachedVertices[_firstVertexIndex + 1];
  825. destinationVertices[_firstVertexIndex + 2] = cachedVertices[_firstVertexIndex + 2];
  826. destinationVertices[_firstVertexIndex + 3] = cachedVertices[_firstVertexIndex + 3];
  827. _meshInfo.mesh.vertices = _meshInfo.vertices;
  828. target.UpdateGeometry(_meshInfo.mesh, _matIndex);
  829. }
  830. public void ResetColors(TMP_Text target, TMP_MeshInfo[] meshInfos)
  831. {
  832. Color color = target.color;
  833. Color32[] vertexCols = meshInfos[_matIndex].colors32;
  834. vertexCols[_firstVertexIndex] = color;
  835. vertexCols[_firstVertexIndex + 1] = color;
  836. vertexCols[_firstVertexIndex + 2] = color;
  837. vertexCols[_firstVertexIndex + 3] = color;
  838. target.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);
  839. }
  840. public Color32 GetColor(TMP_MeshInfo[] meshInfos)
  841. {
  842. return meshInfos[_matIndex].colors32[_firstVertexIndex];
  843. }
  844. public CharVertices GetVertices()
  845. {
  846. return new CharVertices(
  847. _meshInfo.vertices[_firstVertexIndex], _meshInfo.vertices[_firstVertexIndex + 1],
  848. _meshInfo.vertices[_firstVertexIndex + 2], _meshInfo.vertices[_firstVertexIndex + 3]
  849. );
  850. }
  851. public void UpdateAlpha(TMP_Text target, Color alphaColor, TMP_MeshInfo[] meshInfos, bool apply = true)
  852. {
  853. byte alphaByte = (byte)(alphaColor.a * 255);
  854. Color32[] vertexCols = meshInfos[_matIndex].colors32;
  855. vertexCols[_firstVertexIndex].a = alphaByte;
  856. vertexCols[_firstVertexIndex + 1].a = alphaByte;
  857. vertexCols[_firstVertexIndex + 2].a = alphaByte;
  858. vertexCols[_firstVertexIndex + 3].a = alphaByte;
  859. if (apply) target.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);
  860. }
  861. public void UpdateColor(TMP_Text target, Color32 color, TMP_MeshInfo[] meshInfos, bool apply = true)
  862. {
  863. Color32[] vertexCols = meshInfos[_matIndex].colors32;
  864. vertexCols[_firstVertexIndex] = color;
  865. vertexCols[_firstVertexIndex + 1] = color;
  866. vertexCols[_firstVertexIndex + 2] = color;
  867. vertexCols[_firstVertexIndex + 3] = color;
  868. if (apply) target.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);
  869. }
  870. public void UpdateGeometry(TMP_Text target, Vector3 offset, Quaternion rotation, Vector3 scale, TMP_MeshInfo[] cachedMeshInfos, bool apply = true)
  871. {
  872. this.offset = offset;
  873. this.rotation = rotation;
  874. this.scale = scale;
  875. if (!apply) return;
  876. Vector3[] destinationVertices = _meshInfo.vertices;
  877. Vector3[] cachedVertices = cachedMeshInfos[_matIndex].vertices;
  878. destinationVertices[_firstVertexIndex] = cachedVertices[_firstVertexIndex + 0] - _charMidBaselineOffset;
  879. destinationVertices[_firstVertexIndex + 1] = cachedVertices[_firstVertexIndex + 1] - _charMidBaselineOffset;
  880. destinationVertices[_firstVertexIndex + 2] = cachedVertices[_firstVertexIndex + 2] - _charMidBaselineOffset;
  881. destinationVertices[_firstVertexIndex + 3] = cachedVertices[_firstVertexIndex + 3] - _charMidBaselineOffset;
  882. Matrix4x4 matrix = Matrix4x4.TRS(this.offset, this.rotation, this.scale);
  883. destinationVertices[_firstVertexIndex]
  884. = matrix.MultiplyPoint3x4(destinationVertices[_firstVertexIndex + 0]) + _charMidBaselineOffset + _bottomLeftShift;
  885. destinationVertices[_firstVertexIndex + 1]
  886. = matrix.MultiplyPoint3x4(destinationVertices[_firstVertexIndex + 1]) + _charMidBaselineOffset + _topLeftShift;
  887. destinationVertices[_firstVertexIndex + 2]
  888. = matrix.MultiplyPoint3x4(destinationVertices[_firstVertexIndex + 2]) + _charMidBaselineOffset + _topRightShift;
  889. destinationVertices[_firstVertexIndex + 3]
  890. = matrix.MultiplyPoint3x4(destinationVertices[_firstVertexIndex + 3]) + _charMidBaselineOffset + _bottomRightShift;
  891. _meshInfo.mesh.vertices = _meshInfo.vertices;
  892. target.UpdateGeometry(_meshInfo.mesh, _matIndex);
  893. }
  894. public void ShiftVertices(TMP_Text target, Vector3 topLeftShift, Vector3 topRightShift, Vector3 bottomLeftShift, Vector3 bottomRightShift)
  895. {
  896. _topLeftShift += topLeftShift;
  897. _topRightShift += topRightShift;
  898. _bottomLeftShift += bottomLeftShift;
  899. _bottomRightShift += bottomRightShift;
  900. Vector3[] destinationVertices = _meshInfo.vertices;
  901. destinationVertices[_firstVertexIndex] = destinationVertices[_firstVertexIndex] + _bottomLeftShift;
  902. destinationVertices[_firstVertexIndex + 1] = destinationVertices[_firstVertexIndex + 1] + _topLeftShift;
  903. destinationVertices[_firstVertexIndex + 2] = destinationVertices[_firstVertexIndex + 2] + _topRightShift;
  904. destinationVertices[_firstVertexIndex + 3] = destinationVertices[_firstVertexIndex + 3] + _bottomRightShift;
  905. _meshInfo.mesh.vertices = _meshInfo.vertices;
  906. target.UpdateGeometry(_meshInfo.mesh, _matIndex);
  907. }
  908. public void ResetVerticesShift(TMP_Text target)
  909. {
  910. Vector3[] destinationVertices = _meshInfo.vertices;
  911. destinationVertices[_firstVertexIndex] = destinationVertices[_firstVertexIndex] - _bottomLeftShift;
  912. destinationVertices[_firstVertexIndex + 1] = destinationVertices[_firstVertexIndex + 1] - _topLeftShift;
  913. destinationVertices[_firstVertexIndex + 2] = destinationVertices[_firstVertexIndex + 2] - _topRightShift;
  914. destinationVertices[_firstVertexIndex + 3] = destinationVertices[_firstVertexIndex + 3] - _bottomRightShift;
  915. _meshInfo.mesh.vertices = _meshInfo.vertices;
  916. target.UpdateGeometry(_meshInfo.mesh, _matIndex);
  917. _topLeftShift = _topRightShift = _bottomLeftShift = _bottomRightShift = Vector3.zero;
  918. }
  919. }
  920. }
  921. #endregion
  922. }
  923. #endif