// This code is part of the Fungus library (https://github.com/snozbot/fungus) // It is released for free under the MIT open source license (https://github.com/snozbot/fungus/blob/master/LICENSE) using UnityEngine; #if UNITY_2018_1_OR_NEWER namespace Fungus { /// /// Component that is automatically added to all tmpro texts that contain links. Caches /// local data for that TMProText and uses the TMProLinkAnimLookup to the actual animation. /// [DisallowMultipleComponent] public class TMProLinkAnimator : MonoBehaviour { #region Auto Add Component /// /// Ensure we are being notified of TMPro changes. /// [RuntimeInitializeOnLoadMethod] public static void RegisterAutoAddTMPLinkAnim() { TMPro.TMPro_EventManager.TEXT_CHANGED_EVENT.Add(AutoAddTMPLinkAnim); } /// /// Adds a suite of default link text animations. These can be removed via the /// TMProLinkAnimLookup if desired. /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] public static void RegisterDefaultTMPLinkAnims() { TMProLinkAnimLookup.AddHelper("jitter", new TMProLinkAnimEffects.ShakeEffect() { mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, offsetScale = new Vector2(1, 4), rotScale = 10 }); TMProLinkAnimLookup.AddHelper("angry", new TMProLinkAnimEffects.ShakeEffect() { mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerWord, offsetScale = new Vector2(1, 8), rotScale = 4 }); TMProLinkAnimLookup.AddHelper("spooky", new TMProLinkAnimEffects.WiggleEffect() { mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerSection, offsetScale = new Vector2(6, 10), speed = 1.5f, }); TMProLinkAnimLookup.AddHelper("unknowable", new TMProLinkAnimEffects.WiggleEffect() { mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, offsetScale = new Vector2(4, 8), speed = 1f, }); TMProLinkAnimLookup.AddHelper("wave", new TMProLinkAnimEffects.WaveEffect() { mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, speed = 10, indexStep = 0.3f, scale = 2 }); TMProLinkAnimLookup.AddHelper("swing", new TMProLinkAnimEffects.PivotEffect() { mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerWord, speed = 10, degScale = 15 }); TMProLinkAnimLookup.AddHelper("bounce", new TMProLinkAnimEffects.BounceEffect() { mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerWord, speed = 4, scale = 5, }); TMProLinkAnimLookup.AddHelper("excited", new TMProLinkAnimEffects.BounceEffect() { mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, speed = 7, scale = 2, indexStep = 11.0f / 3.0f, }); TMProLinkAnimLookup.AddHelper("glow", new TMProLinkAnimEffects.PulseEffect() { mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerWord, speed = 4, HSVIntensityScale = 0.15f, hueScale = 0, saturationScale = 0.1f, scale = new Vector3(0.06f, 0.06f, 0), }); } /// /// Called by TMPro when a text is changed, ensuring link animator is there and /// that data is ready for it to use. /// /// public static void AutoAddTMPLinkAnim(object obj) { if (Application.isPlaying) { var tmp = (obj as TMPro.TMP_Text); if (forcedUpdater == null && tmp.textInfo.linkCount > 0) { var tmpa = tmp.GetComponent(); if (tmpa == null) { tmpa = tmp.gameObject.AddComponent(); tmpa.TMProComponent = tmp; } tmpa.SetDirty(); tmpa.UpdateAnimation(); } } } /// /// Cache of the TMProLinkAnimator that just forced an update of the TMProText, used to /// prevent cyclic updates of TMPro mesh content. /// protected static TMProLinkAnimator forcedUpdater; #endregion Auto Add Component public TMPro.TMP_Text TMProComponent { get; protected set; } public bool dirty = true; protected bool needsToForceMeshUpdate = true; public TMPro.TMP_MeshInfo[] CachedMeshInfo { get; protected set; } public void SetDirty() { dirty = true; } protected void Awake() { if (TMProComponent == null) { TMProComponent = GetComponent(); } } protected void Update() { UpdateAnimation(); } /// /// If there is TMPro and a link to potentially animate then ask the AnimLookup for it /// protected void UpdateAnimation() { //could we anim if (TMProComponent != null && enabled) { bool requiresVertexDataUpdate = false; //for all found links for (int i = 0; i < TMProComponent.textInfo.linkCount; i++) { var curLink = TMProComponent.textInfo.linkInfo[i]; //if a static lookup exists, ask it to run its animation with us as the context if (TMProLinkAnimLookup.LinkHashToEffect.TryGetValue(curLink.hashCode, out TMProLinkAnimLookup.TMProAnimFunc animFunc)) { //only update caches if we actually need it HandleDirty(); animFunc(this, curLink.linkTextfirstCharacterIndex, curLink.linkTextLength); requiresVertexDataUpdate = true; } } // Push changes if we actually found a matching effect if (requiresVertexDataUpdate) { TMProComponent.UpdateVertexData(); } } } protected void HandleDirty() { //update internal cache if underlying data has changed if (dirty) { if (needsToForceMeshUpdate) { forcedUpdater = this; TMProComponent.ForceMeshUpdate(); forcedUpdater = null; } CachedMeshInfo = TMProComponent.textInfo.CopyMeshInfoVertexData(); dirty = false; } } } } #endif