Browse Source
Add TextMeshPro animation support via Link text - TMProLinkAnimator, auto adds to components - TMProLinkAnimLookup, stores all TMPro animation functions by link text hash - TMProLinkAnimEffects, default set of configurable TMPro animation functions built around color and Matrix transformations - Add TMPro Link Effect Demo scene - Adds defaults for shake, wiggle, wave, pivot, rainbow, ascend, pulsemaster
Steve Halliwell
5 years ago
committed by
GitHub
17 changed files with 6637 additions and 126 deletions
@ -0,0 +1,436 @@ |
|||||||
|
// 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 |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Samples and helpers for creating TMProLink animations |
||||||
|
/// </summary> |
||||||
|
namespace TMProLinkAnimEffects |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Used by BaseEffect and child classes to configure how locations and pivots are calculated |
||||||
|
/// before being passed to internal functions. |
||||||
|
/// </summary> |
||||||
|
public enum TMPLinkAnimatorMode |
||||||
|
{ |
||||||
|
PerCharacter, |
||||||
|
PerWord, |
||||||
|
PerSection, |
||||||
|
PerLine, |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Use of this is not required, all that the TMProLinkAnimLookup requires is a matching signature of |
||||||
|
/// void delegate(TMProLinkAnimator context, int start, int length). The base class however is used to |
||||||
|
/// create all the sample effects as they all operate in a similar underlying fashion, with a custom mode |
||||||
|
/// but ultimately on a character by character basis, doing a relative translation and color modifcation. |
||||||
|
/// |
||||||
|
/// Much of this class and its sample child effects are static functions to more easily allow reuse by |
||||||
|
/// custom effects you may wish to make. |
||||||
|
/// </summary> |
||||||
|
public abstract class BaseEffect |
||||||
|
{ |
||||||
|
public TMPLinkAnimatorMode mode; |
||||||
|
protected TMProLinkAnimator CurrentContext { get; set; } |
||||||
|
protected int CurrentStart { get; set; } |
||||||
|
protected int CurrentLength { get; set; } |
||||||
|
|
||||||
|
public virtual void DoEffect(TMProLinkAnimator context, int start, int length) |
||||||
|
{ |
||||||
|
CurrentContext = context; |
||||||
|
CurrentStart = start; |
||||||
|
CurrentLength = length; |
||||||
|
|
||||||
|
MeshVertUpdateLoop(context, start, length, TransFunc, ColorFunc, mode); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Matrix4x4 TransFunc(int index) |
||||||
|
{ |
||||||
|
return Matrix4x4.identity; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Color32 ColorFunc(int index, Color32 col) |
||||||
|
{ |
||||||
|
return col; |
||||||
|
} |
||||||
|
|
||||||
|
#region static helpers |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Helper for mesh vertex updating within a found link, adapted from TMPRo examples VertexJitter. |
||||||
|
/// </summary> |
||||||
|
/// <param name="context"></param> |
||||||
|
/// <param name="start"></param> |
||||||
|
/// <param name="length"></param> |
||||||
|
/// <param name="transformFunc"></param> |
||||||
|
/// <param name="colorFunc"></param> |
||||||
|
/// <param name="mode"></param> |
||||||
|
static public void MeshVertUpdateLoop(TMProLinkAnimator context, int start, int length, System.Func<int, Matrix4x4> transformFunc, System.Func<int, Color32, Color32> colorFunc, TMPLinkAnimatorMode mode) |
||||||
|
{ |
||||||
|
var tmproComponent = context.TMProComponent; |
||||||
|
var textInfo = tmproComponent.textInfo; |
||||||
|
var end = start + length; |
||||||
|
var cachedMeshInfo = context.CachedMeshInfo; |
||||||
|
|
||||||
|
Matrix4x4 matrix = Matrix4x4.identity; |
||||||
|
Vector2 middle = Vector2.zero; |
||||||
|
Color32 col = Color.white; |
||||||
|
int wordIndex = -1; |
||||||
|
int lineIndex = -1; |
||||||
|
|
||||||
|
for (int i = start; i < end; i++) |
||||||
|
{ |
||||||
|
//required as TMPro is putting non visible invalid elements in the charinfo array assuming I will follow this rule |
||||||
|
// if we don't character 0 ends up getting an effect applied to it even though it shouldn't |
||||||
|
if (!textInfo.characterInfo[i].isVisible) continue; |
||||||
|
|
||||||
|
// Get the index of the material used by the current character. |
||||||
|
int materialIndex = textInfo.characterInfo[i].materialReferenceIndex; |
||||||
|
|
||||||
|
// Get the index of the first vertex used by this text element. |
||||||
|
int vertexIndex = textInfo.characterInfo[i].vertexIndex; |
||||||
|
|
||||||
|
// Get the cached vertices of the mesh used by this text element (character or sprite). |
||||||
|
Vector3[] sourceVertices = cachedMeshInfo[materialIndex].vertices; |
||||||
|
Color32[] vertexColors = cachedMeshInfo[materialIndex].colors32; |
||||||
|
|
||||||
|
if (i == start && mode == TMPLinkAnimatorMode.PerSection) |
||||||
|
{ |
||||||
|
matrix = transformFunc(start); |
||||||
|
middle = CalcMidFromChars(context, start, end); |
||||||
|
col = colorFunc(start, vertexColors[vertexIndex]); |
||||||
|
} |
||||||
|
|
||||||
|
// Determine the center point of each character at the baseline. |
||||||
|
//Vector2 charMidBasline = new Vector2((sourceVertices[vertexIndex + 0].x + sourceVertices[vertexIndex + 2].x) / 2, charInfo.baseLine); |
||||||
|
// Determine the center point of each character. |
||||||
|
if (mode == TMPLinkAnimatorMode.PerCharacter) |
||||||
|
{ |
||||||
|
middle = (sourceVertices[vertexIndex + 0] + sourceVertices[vertexIndex + 2]) / 2; |
||||||
|
matrix = transformFunc(i); |
||||||
|
col = colorFunc(i, vertexColors[vertexIndex]); |
||||||
|
} |
||||||
|
else if (mode == TMPLinkAnimatorMode.PerWord) |
||||||
|
{ |
||||||
|
var newWordIndex = CalcWordFromChar(i, textInfo.wordInfo); |
||||||
|
if (newWordIndex != -1 && wordIndex != newWordIndex) |
||||||
|
{ |
||||||
|
wordIndex = newWordIndex; |
||||||
|
|
||||||
|
middle = CalcMidFromChars(context, Mathf.Max(start, textInfo.wordInfo[wordIndex].firstCharacterIndex), Mathf.Min(end, textInfo.wordInfo[wordIndex].lastCharacterIndex)); |
||||||
|
matrix = transformFunc(i); |
||||||
|
col = colorFunc(i, vertexColors[vertexIndex]); |
||||||
|
} |
||||||
|
} |
||||||
|
else if (mode == TMPLinkAnimatorMode.PerLine) |
||||||
|
{ |
||||||
|
var newLineIndex = textInfo.characterInfo[i].lineNumber; |
||||||
|
if (newLineIndex != -1 && lineIndex != newLineIndex) |
||||||
|
{ |
||||||
|
lineIndex = newLineIndex; |
||||||
|
|
||||||
|
middle = CalcMidFromChars(context, Mathf.Max(start, textInfo.lineInfo[lineIndex].firstCharacterIndex), Mathf.Min(end, textInfo.lineInfo[lineIndex].lastCharacterIndex)); |
||||||
|
matrix = transformFunc(i); |
||||||
|
col = colorFunc(i, vertexColors[vertexIndex]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Need to translate all 4 vertices of each quad to aligned with middle of character / baseline. |
||||||
|
// This is needed so the matrix TRS is applied at the origin for each character. |
||||||
|
Vector3 offset = middle; |
||||||
|
|
||||||
|
Vector3[] destinationVertices = textInfo.meshInfo[materialIndex].vertices; |
||||||
|
Color32[] destinationVertColors = textInfo.meshInfo[materialIndex].colors32; |
||||||
|
|
||||||
|
destinationVertices[vertexIndex + 0] = sourceVertices[vertexIndex + 0] - offset; |
||||||
|
destinationVertices[vertexIndex + 1] = sourceVertices[vertexIndex + 1] - offset; |
||||||
|
destinationVertices[vertexIndex + 2] = sourceVertices[vertexIndex + 2] - offset; |
||||||
|
destinationVertices[vertexIndex + 3] = sourceVertices[vertexIndex + 3] - offset; |
||||||
|
|
||||||
|
destinationVertices[vertexIndex + 0] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 0]); |
||||||
|
destinationVertices[vertexIndex + 1] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 1]); |
||||||
|
destinationVertices[vertexIndex + 2] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 2]); |
||||||
|
destinationVertices[vertexIndex + 3] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 3]); |
||||||
|
|
||||||
|
destinationVertices[vertexIndex + 0] += offset; |
||||||
|
destinationVertices[vertexIndex + 1] += offset; |
||||||
|
destinationVertices[vertexIndex + 2] += offset; |
||||||
|
destinationVertices[vertexIndex + 3] += offset; |
||||||
|
|
||||||
|
destinationVertColors[vertexIndex + 0] = col; |
||||||
|
destinationVertColors[vertexIndex + 1] = col; |
||||||
|
destinationVertColors[vertexIndex + 2] = col; |
||||||
|
destinationVertColors[vertexIndex + 3] = col; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Same as calcing a character mid but averaging over all characters in the given character range |
||||||
|
/// </summary> |
||||||
|
/// <param name="context"></param> |
||||||
|
/// <param name="start"></param> |
||||||
|
/// <param name="end"></param> |
||||||
|
/// <returns></returns> |
||||||
|
static public Vector2 CalcMidFromChars(TMProLinkAnimator context, int start, int end) |
||||||
|
{ |
||||||
|
Vector3 middle = Vector3.zero; |
||||||
|
var chInfo = context.TMProComponent.textInfo.characterInfo; |
||||||
|
|
||||||
|
for (int i = start; i < end; i++) |
||||||
|
{ |
||||||
|
int materialIndex = chInfo[i].materialReferenceIndex; |
||||||
|
int vertexIndex = chInfo[i].vertexIndex; |
||||||
|
Vector3[] sourceVertices = context.CachedMeshInfo[materialIndex].vertices; |
||||||
|
middle += (sourceVertices[vertexIndex + 0] + sourceVertices[vertexIndex + 2]) / 2; |
||||||
|
} |
||||||
|
|
||||||
|
return middle / (end - start); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Determine which TMPro World a given character index is within |
||||||
|
/// </summary> |
||||||
|
/// <param name="charIndex"></param> |
||||||
|
/// <param name="wordInfo"></param> |
||||||
|
/// <returns></returns> |
||||||
|
static public int CalcWordFromChar(int charIndex, TMPro.TMP_WordInfo[] wordInfo) |
||||||
|
{ |
||||||
|
for (int i = 0; i < wordInfo.Length; i++) |
||||||
|
{ |
||||||
|
//enforcing start letter results in punctuation attaching to the word to its left rather than its right. |
||||||
|
// which is more desirable for english at least |
||||||
|
if (charIndex >= wordInfo[i].firstCharacterIndex && wordInfo[i].lastCharacterIndex >= charIndex) |
||||||
|
{ |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Determine which TMPro Line a given character index is within |
||||||
|
/// </summary> |
||||||
|
/// <param name="charIndex"></param> |
||||||
|
/// <param name="wordInfo"></param> |
||||||
|
/// <returns></returns> |
||||||
|
static public int CalcLineFromChar(int charIndex, TMPro.TMP_WordInfo[] wordInfo) |
||||||
|
{ |
||||||
|
for (int i = 0; i < wordInfo.Length; i++) |
||||||
|
{ |
||||||
|
//enforcing start letter results in punctuation attaching to the word to its left rather than its right. |
||||||
|
// which is more desirable for english at least |
||||||
|
if (charIndex >= wordInfo[i].firstCharacterIndex && wordInfo[i].lastCharacterIndex >= charIndex) |
||||||
|
{ |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion static helpers |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Shake the element, by moving centre slightly and randomly rolling each update. |
||||||
|
/// </summary> |
||||||
|
public class ShakeEffect : BaseEffect |
||||||
|
{ |
||||||
|
public float rotScale; |
||||||
|
public Vector2 offsetScale = Vector2.one; |
||||||
|
|
||||||
|
public override Matrix4x4 TransFunc(int index) |
||||||
|
{ |
||||||
|
return ShakeTransformFunc(index, offsetScale, rotScale); |
||||||
|
} |
||||||
|
|
||||||
|
static public Matrix4x4 ShakeTransformFunc(int index, Vector2 positionOffsetScale, float rotDegScale) |
||||||
|
{ |
||||||
|
return Matrix4x4.TRS(new Vector3(Random.Range(-.25f, .25f) * positionOffsetScale.x, Random.Range(-.25f, .25f), 0) * positionOffsetScale.y, |
||||||
|
Quaternion.Euler(0, 0, Random.Range(-1f, 1f) * rotDegScale), |
||||||
|
Vector3.one); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Wiggle the position by over time using perlin noise to offset its centre. |
||||||
|
/// </summary> |
||||||
|
public class WiggleEffect : BaseEffect |
||||||
|
{ |
||||||
|
public float speed = 1; |
||||||
|
public Vector2 offsetScale = Vector2.one; |
||||||
|
|
||||||
|
public override Matrix4x4 TransFunc(int index) |
||||||
|
{ |
||||||
|
return WiggleTransformFunc(index, speed, offsetScale); |
||||||
|
} |
||||||
|
|
||||||
|
static public Matrix4x4 WiggleTransformFunc(int index, float speed, Vector2 wiggleScale) |
||||||
|
{ |
||||||
|
const int SAFE_PRIME_A = 11; |
||||||
|
const int SAFE_PRIME_B = 59; |
||||||
|
//add a pingpong |
||||||
|
var jitterOffset = new Vector3(Mathf.PerlinNoise(Time.time * speed + index * SAFE_PRIME_A, index * SAFE_PRIME_B), |
||||||
|
Mathf.PerlinNoise(Time.time * speed + index * SAFE_PRIME_B, index * SAFE_PRIME_A), |
||||||
|
0); |
||||||
|
jitterOffset *= 2; |
||||||
|
jitterOffset -= new Vector3(1, 1, 0); |
||||||
|
|
||||||
|
return Matrix4x4.TRS(jitterOffset * wiggleScale, |
||||||
|
Quaternion.identity, |
||||||
|
Vector3.one); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Use a sine wave by time to offset the height of the element. |
||||||
|
/// </summary> |
||||||
|
public class WaveEffect : BaseEffect |
||||||
|
{ |
||||||
|
public float speed, indexStep, scale; |
||||||
|
|
||||||
|
public override Matrix4x4 TransFunc(int index) |
||||||
|
{ |
||||||
|
return WaveTransformFunc(index, speed, indexStep, scale); |
||||||
|
} |
||||||
|
|
||||||
|
static public Matrix4x4 WaveTransformFunc(int index, float waveSpeed, float waveIndexStep, float waveScale) |
||||||
|
{ |
||||||
|
return Matrix4x4.TRS(new Vector3(0, Mathf.Sin(Time.time * waveSpeed + index * waveIndexStep) * waveScale, 0), |
||||||
|
Quaternion.identity, |
||||||
|
Vector3.one); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Use a sinewave to swing or pivot the element around its centre back n forth. |
||||||
|
/// </summary> |
||||||
|
public class PivotEffect : BaseEffect |
||||||
|
{ |
||||||
|
public float speed, degScale; |
||||||
|
|
||||||
|
public override Matrix4x4 TransFunc(int index) |
||||||
|
{ |
||||||
|
return PivotTransformFunc(index, speed, degScale); |
||||||
|
} |
||||||
|
|
||||||
|
static public Matrix4x4 PivotTransformFunc(int index, float pivotSpeed, float pivotDegScale) |
||||||
|
{ |
||||||
|
return Matrix4x4.TRS(Vector3.zero, |
||||||
|
Quaternion.Euler(0, 0, Mathf.Sin(Time.time * pivotSpeed + index) * pivotDegScale), |
||||||
|
Vector3.one); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Use a sine wave to animate the H,S,V elements individually, modifying them from their starting color. |
||||||
|
/// Use a sine wave to scale the element |
||||||
|
/// </summary> |
||||||
|
public class PulseEffect : BaseEffect |
||||||
|
{ |
||||||
|
public float speed = 1, HSVIntensityScale = 1, indexStep = 1, hueScale = 1, saturationScale = 1, valueScale = 1; |
||||||
|
public Vector3 scale = Vector2.zero; |
||||||
|
|
||||||
|
public override Color32 ColorFunc(int index, Color32 col) |
||||||
|
{ |
||||||
|
return HSVPulse(index, indexStep, speed, HSVIntensityScale, col, hueScale, saturationScale, valueScale); |
||||||
|
} |
||||||
|
|
||||||
|
public override Matrix4x4 TransFunc(int index) |
||||||
|
{ |
||||||
|
return ScalePulse(index, indexStep, speed, scale); |
||||||
|
} |
||||||
|
|
||||||
|
static public Color32 HSVPulse(int index, float indexStep, float speed, float colScale, Color32 startingColor, float hueScale, float saturationScale, float valueScale) |
||||||
|
{ |
||||||
|
float t = Mathf.Sin(Time.time * speed + index * indexStep) * colScale; |
||||||
|
Color.RGBToHSV(startingColor, out float h, out float s, out float v); |
||||||
|
|
||||||
|
var col = Color.HSVToRGB(Mathf.Repeat(h + t * hueScale, 1), |
||||||
|
Mathf.Clamp01(s + t * saturationScale), |
||||||
|
Mathf.Clamp01(v + t * valueScale)); |
||||||
|
return (Color32)col; |
||||||
|
} |
||||||
|
|
||||||
|
static public Matrix4x4 ScalePulse(int index, float indexStep, float speed, Vector3 scale) |
||||||
|
{ |
||||||
|
float t = Mathf.Sin(Time.time * speed + index * indexStep); |
||||||
|
return Matrix4x4.TRS(Vector3.zero, |
||||||
|
Quaternion.identity, |
||||||
|
Vector3.one + scale * t); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Bounce up and down on an endless parabolic curve. |
||||||
|
/// </summary> |
||||||
|
public class BounceEffect : BaseEffect |
||||||
|
{ |
||||||
|
public float indexStep = 1, speed = 1, scale = 1, fixedOffsetScale = 0.5f; |
||||||
|
|
||||||
|
public override Matrix4x4 TransFunc(int index) |
||||||
|
{ |
||||||
|
return Bounce(index, indexStep, speed, scale, fixedOffsetScale); |
||||||
|
} |
||||||
|
|
||||||
|
static public Matrix4x4 Bounce(int index, float indexStep, float speed, float scale, float fixedOffsetScale) |
||||||
|
{ |
||||||
|
float t = (Time.time * speed + index * indexStep) % 2.0f; |
||||||
|
|
||||||
|
t = -t * t + 2 * t; |
||||||
|
|
||||||
|
return Matrix4x4.TRS(Vector3.up * t * scale + Vector3.down * fixedOffsetScale * scale, |
||||||
|
Quaternion.identity, |
||||||
|
Vector3.one); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Create a staircase effect of the the elements. |
||||||
|
/// </summary> |
||||||
|
public class AscendEffect : BaseEffect |
||||||
|
{ |
||||||
|
public float totalStep; |
||||||
|
|
||||||
|
public override Matrix4x4 TransFunc(int index) |
||||||
|
{ |
||||||
|
return StepTransformFunc(index, index - CurrentStart, totalStep / CurrentLength); |
||||||
|
} |
||||||
|
|
||||||
|
static public Matrix4x4 StepTransformFunc(int index, int stepNum, float stepHeight) |
||||||
|
{ |
||||||
|
return Matrix4x4.TRS(Vector3.up * stepNum * stepHeight, |
||||||
|
Quaternion.identity, |
||||||
|
Vector3.one); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Cycle the colors of the elements by forcing color to a roling Hue and fixed SV color value. |
||||||
|
/// </summary> |
||||||
|
public class RainbowEffect : BaseEffect |
||||||
|
{ |
||||||
|
public float speed, indexStep, s, v; |
||||||
|
|
||||||
|
public override Color32 ColorFunc(int index, Color32 col) |
||||||
|
{ |
||||||
|
return CycleColor(index, speed, indexStep, s, v); |
||||||
|
} |
||||||
|
|
||||||
|
static public Color32 CycleColor(int index, float speed, float indexStep, float s, float v) |
||||||
|
{ |
||||||
|
float h = Time.time * speed + index * indexStep; |
||||||
|
var col = Color.HSVToRGB(h % 1.0f, s, v); |
||||||
|
return (Color32)col; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 5a2f0563cd4feb744975e63982dc480c |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
@ -0,0 +1,104 @@ |
|||||||
|
// 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 System.Collections.Generic; |
||||||
|
|
||||||
|
#if UNITY_2018_1_OR_NEWER |
||||||
|
|
||||||
|
namespace Fungus |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Static lookup for Text Mesh Pro Link animations. TMPro tracks and holds information about |
||||||
|
/// link tags in its text body and is recommended as one of the ways to achieve effects within |
||||||
|
/// a body of text. Giving you the text within the link and the name/hash of the link id itself. |
||||||
|
/// |
||||||
|
/// Such that {link="shake"}this text will be marked up as within shake link{/link}. |
||||||
|
/// |
||||||
|
/// By assigning to the LinkHashToEffect dictionary with a key of TMPro.TMP_TextUtilities.GetSimpleHashCode("shake") |
||||||
|
/// and a matching function signature that can then be used the the TMProLinkAnimator. |
||||||
|
/// |
||||||
|
/// See TMProLinkAnimEffects for sample basis for creating effects. |
||||||
|
/// </summary> |
||||||
|
public static class TMProLinkAnimLookup |
||||||
|
{ |
||||||
|
//required signature for all TMProAnim functions for use in the lookup |
||||||
|
public delegate void TMProAnimFunc(TMProLinkAnimator beh, int start, int length); |
||||||
|
|
||||||
|
//static lookup for all tmpro link style lookups |
||||||
|
//this is where additional effects would be added |
||||||
|
static public Dictionary<int, TMProAnimFunc> LinkHashToEffect = new Dictionary<int, TMProAnimFunc>() |
||||||
|
{ |
||||||
|
//comments left here for the effects that are added in the demo scene |
||||||
|
/* |
||||||
|
{TMPro.TMP_TextUtilities.GetSimpleHashCode("shake"), |
||||||
|
new TMProLinkAnimEffects.ShakeEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerLine, |
||||||
|
offsetScale = 2, |
||||||
|
rotScale = 15 |
||||||
|
}.DoEffect }, |
||||||
|
{TMPro.TMP_TextUtilities.GetSimpleHashCode("wiggle"), |
||||||
|
new TMProLinkAnimEffects.WiggleEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerSection, |
||||||
|
scale = 5 |
||||||
|
}.DoEffect }, |
||||||
|
{TMPro.TMP_TextUtilities.GetSimpleHashCode("wave"), |
||||||
|
new TMProLinkAnimEffects.WaveEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, |
||||||
|
speed = 10, |
||||||
|
indexStep = 0.3f, |
||||||
|
scale = 2 |
||||||
|
}.DoEffect }, |
||||||
|
{TMPro.TMP_TextUtilities.GetSimpleHashCode("pivot"), |
||||||
|
new TMProLinkAnimEffects.PivotEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerWord, |
||||||
|
speed = 10, |
||||||
|
degScale = 15 |
||||||
|
}.DoEffect |
||||||
|
}, |
||||||
|
{TMPro.TMP_TextUtilities.GetSimpleHashCode("rainbow"), |
||||||
|
new TMProLinkAnimEffects.RainbowEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, |
||||||
|
speed = 2, |
||||||
|
indexStep = 0.1f, |
||||||
|
s = 0.8f, |
||||||
|
v = 0.8f |
||||||
|
}.DoEffect |
||||||
|
}, |
||||||
|
{TMPro.TMP_TextUtilities.GetSimpleHashCode("ascend"), |
||||||
|
new TMProLinkAnimEffects.AscendEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, |
||||||
|
totalStep = 10 |
||||||
|
}.DoEffect |
||||||
|
}, |
||||||
|
*/ |
||||||
|
}; |
||||||
|
|
||||||
|
static public void AddHelper(string linkIdText, TMProAnimFunc func) |
||||||
|
{ |
||||||
|
LinkHashToEffect.Add(TMPro.TMP_TextUtilities.GetSimpleHashCode(linkIdText), func); |
||||||
|
} |
||||||
|
|
||||||
|
static public void AddHelper(string linkIdText, TMProLinkAnimEffects.BaseEffect baseEffect) |
||||||
|
{ |
||||||
|
LinkHashToEffect.Add(TMPro.TMP_TextUtilities.GetSimpleHashCode(linkIdText), baseEffect.DoEffect); |
||||||
|
} |
||||||
|
|
||||||
|
static public void Remove(string linkIdText) |
||||||
|
{ |
||||||
|
LinkHashToEffect.Remove(TMPro.TMP_TextUtilities.GetSimpleHashCode(linkIdText)); |
||||||
|
} |
||||||
|
|
||||||
|
static public void RemoveAll() |
||||||
|
{ |
||||||
|
LinkHashToEffect.Clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 3a3990bfc87d31047a6cc1e300dff1d7 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
@ -0,0 +1,206 @@ |
|||||||
|
// 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 |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 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. |
||||||
|
/// </summary> |
||||||
|
[DisallowMultipleComponent] |
||||||
|
public class TMProLinkAnimator : MonoBehaviour |
||||||
|
{ |
||||||
|
#region Auto Add Component |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Ensure we are being notified of TMPro changes. |
||||||
|
/// </summary> |
||||||
|
[RuntimeInitializeOnLoadMethod] |
||||||
|
public static void RegisterAutoAddTMPLinkAnim() |
||||||
|
{ |
||||||
|
TMPro.TMPro_EventManager.TEXT_CHANGED_EVENT.Add(AutoAddTMPLinkAnim); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Adds a suite of default link text animations. These can be removed via the |
||||||
|
/// TMProLinkAnimLookup if desired. |
||||||
|
/// </summary> |
||||||
|
[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), |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Called by TMPro when a text is changed, ensuring link animator is there and |
||||||
|
/// that data is ready for it to use. |
||||||
|
/// </summary> |
||||||
|
/// <param name="obj"></param> |
||||||
|
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<TMProLinkAnimator>(); |
||||||
|
if (tmpa == null) |
||||||
|
{ |
||||||
|
tmpa = tmp.gameObject.AddComponent<TMProLinkAnimator>(); |
||||||
|
tmpa.TMProComponent = tmp; |
||||||
|
} |
||||||
|
|
||||||
|
tmpa.SetDirty(); |
||||||
|
tmpa.UpdateAnimation(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Cache of the TMProLinkAnimator that just forced an update of the TMProText, used to |
||||||
|
/// prevent cyclic updates of TMPro mesh content. |
||||||
|
/// </summary> |
||||||
|
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<TMPro.TMP_Text>(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void Update() |
||||||
|
{ |
||||||
|
UpdateAnimation(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// If there is TMPro and a link to potentially animate then ask the AnimLookup for it |
||||||
|
/// </summary> |
||||||
|
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 |
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: beb7176ffd78d904497350708b9f8134 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,7 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: cffaf714f3026ad4db01e751fd5fd2a4 |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,7 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: df0f727d3fe53fe4e9e9ec53f00cdfe1 |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
@ -0,0 +1,72 @@ |
|||||||
|
// 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.Examples |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Used in TMPro Link Anim Demo, adds a number of sample animation styles. Serves as |
||||||
|
/// an example of how you might configure these effects and variations of them in |
||||||
|
/// your projects |
||||||
|
/// </summary> |
||||||
|
public class TMProLinkStylingDemo : MonoBehaviour |
||||||
|
{ |
||||||
|
private void Awake() |
||||||
|
{ |
||||||
|
//force clearing and adding our own effects here |
||||||
|
TMProLinkAnimLookup.RemoveAll(); |
||||||
|
|
||||||
|
TMProLinkAnimLookup.AddHelper("shake", new TMProLinkAnimEffects.ShakeEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, |
||||||
|
offsetScale = Vector2.one * 2, |
||||||
|
rotScale = 15 |
||||||
|
}); |
||||||
|
TMProLinkAnimLookup.AddHelper("wiggle", new TMProLinkAnimEffects.WiggleEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerSection, |
||||||
|
offsetScale = Vector2.one * 5 |
||||||
|
}); |
||||||
|
TMProLinkAnimLookup.AddHelper("wave", new TMProLinkAnimEffects.WaveEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, |
||||||
|
speed = 10, |
||||||
|
indexStep = 0.3f, |
||||||
|
scale = 2 |
||||||
|
}); |
||||||
|
TMProLinkAnimLookup.AddHelper("pivot", new TMProLinkAnimEffects.PivotEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerWord, |
||||||
|
speed = 10, |
||||||
|
degScale = 15 |
||||||
|
}); |
||||||
|
TMProLinkAnimLookup.AddHelper("rainbow", new TMProLinkAnimEffects.RainbowEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, |
||||||
|
speed = 2, |
||||||
|
indexStep = 0.1f, |
||||||
|
s = 0.8f, |
||||||
|
v = 0.8f |
||||||
|
}); |
||||||
|
TMProLinkAnimLookup.AddHelper("ascend", new TMProLinkAnimEffects.AscendEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerCharacter, |
||||||
|
totalStep = 10, |
||||||
|
}); |
||||||
|
TMProLinkAnimLookup.AddHelper("pulse", new TMProLinkAnimEffects.PulseEffect() |
||||||
|
{ |
||||||
|
mode = TMProLinkAnimEffects.TMPLinkAnimatorMode.PerWord, |
||||||
|
speed = 3, |
||||||
|
HSVIntensityScale = 0.15f, |
||||||
|
hueScale = 0, |
||||||
|
saturationScale = 0, |
||||||
|
scale = new Vector3(0.05f, 0.05f, 0), |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 1d3b019791b0794419d9937f571dd578 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
Loading…
Reference in new issue