473 lines
14 KiB
C#
473 lines
14 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using RPGTALK.Helper;
|
|
#if RPGTalk_TMP
|
|
using TMPro;
|
|
#endif
|
|
|
|
//We may see a lot of "Uncreachable code" warnings if have or don't TMP. Let's desable them for now
|
|
#pragma warning disable 0162
|
|
|
|
namespace RPGTALK.Texts
|
|
{
|
|
// This class has the objective of translate any variables that have different names between Unity's regular UI and TMPUGUI.
|
|
public class TMP_Translator
|
|
{
|
|
|
|
public Text UIText;
|
|
public bool hasUIText;
|
|
#if RPGTalk_TMP
|
|
public TextMeshProUGUI TMPText;
|
|
#endif
|
|
public bool errorSetting;
|
|
|
|
public TMP_Translator(GameObject obj)
|
|
{
|
|
Text isText = obj.GetComponent<Text>();
|
|
if (isText)
|
|
{
|
|
UIText = isText;
|
|
hasUIText = true;
|
|
errorSetting = false;
|
|
return;
|
|
}
|
|
|
|
#if RPGTalk_TMP
|
|
TextMeshProUGUI isTMP = obj.GetComponent<TextMeshProUGUI>();
|
|
if (isTMP)
|
|
{
|
|
TMPText = isTMP;
|
|
errorSetting = false;
|
|
return;
|
|
}
|
|
#endif
|
|
errorSetting = true;
|
|
}
|
|
|
|
public void ChangeTextTo(string text)
|
|
{
|
|
if (errorSetting)
|
|
{
|
|
DebugError();
|
|
return;
|
|
}
|
|
|
|
|
|
if (UIText != null)
|
|
{
|
|
UIText.text = text;
|
|
}
|
|
else
|
|
{
|
|
#if RPGTalk_TMP
|
|
|
|
TMPText.text = text;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public string GetCurrentText()
|
|
{
|
|
if (errorSetting)
|
|
{
|
|
DebugError();
|
|
return "";
|
|
}
|
|
|
|
|
|
if (UIText != null)
|
|
{
|
|
return UIText.text;
|
|
}
|
|
else
|
|
{
|
|
#if RPGTalk_TMP
|
|
|
|
return TMPText.text;
|
|
#endif
|
|
}
|
|
return "";
|
|
}
|
|
|
|
void DebugError()
|
|
{
|
|
Debug.LogError("The object setted on RPGTalk wasn't a Text or a Text Mesh Pro UGUI. Be sure to check RPGTalk Configuration if you wnat to use the later.");
|
|
}
|
|
|
|
|
|
public void ChangeRichText(bool active)
|
|
{
|
|
if (errorSetting)
|
|
{
|
|
DebugError();
|
|
return;
|
|
}
|
|
|
|
if (UIText != null)
|
|
{
|
|
UIText.supportRichText = active;
|
|
}
|
|
else
|
|
{
|
|
#if RPGTalk_TMP
|
|
|
|
TMPText.richText = active;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public bool RichText()
|
|
{
|
|
if (errorSetting)
|
|
{
|
|
DebugError();
|
|
return false;
|
|
}
|
|
|
|
if (UIText != null)
|
|
{
|
|
return UIText.supportRichText;
|
|
}
|
|
else
|
|
{
|
|
#if RPGTalk_TMP
|
|
|
|
return TMPText.richText;
|
|
#endif
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
public void Enabled(bool enable)
|
|
{
|
|
if (errorSetting)
|
|
{
|
|
DebugError();
|
|
}
|
|
|
|
if (UIText != null)
|
|
{
|
|
UIText.enabled = enable;
|
|
}
|
|
else
|
|
{
|
|
#if RPGTalk_TMP
|
|
|
|
TMPText.enabled = enable;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
public bool Enabled()
|
|
{
|
|
if (errorSetting)
|
|
{
|
|
DebugError();
|
|
return false;
|
|
}
|
|
|
|
if (UIText != null)
|
|
{
|
|
return UIText.enabled;
|
|
}
|
|
else
|
|
{
|
|
#if RPGTalk_TMP
|
|
|
|
return TMPText.enabled;
|
|
#endif
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public Object GetTextObject()
|
|
{
|
|
if (errorSetting)
|
|
{
|
|
DebugError();
|
|
return null;
|
|
}
|
|
|
|
if (UIText != null)
|
|
{
|
|
return UIText;
|
|
}
|
|
else
|
|
{
|
|
#if RPGTalk_TMP
|
|
|
|
return TMPText;
|
|
#endif
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A function that returns if the Object is an acceptable type (Text or TextMeshProUGUI)
|
|
/// </summary>
|
|
/// <returns><c>true</c>, if valid type was used, <c>false</c> otherwise.</returns>
|
|
/// <param name="obj">Object.</param>
|
|
public static bool IsValidType(GameObject obj)
|
|
{
|
|
if (obj.GetComponent<Text>())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#if RPGTalk_TMP
|
|
if (obj.GetComponent<TextMeshProUGUI>())
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// A simple function that returns true if the object has a Text Component
|
|
/// </summary>
|
|
public static bool IsText(GameObject obj)
|
|
{
|
|
if (obj.GetComponent<Text>())
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
public ITextWithIcon AddTextWithIconComponent(GameObject gameObject)
|
|
{
|
|
if (errorSetting)
|
|
{
|
|
DebugError();
|
|
return null;
|
|
}
|
|
|
|
|
|
if (hasUIText)
|
|
{
|
|
return gameObject.AddComponent<TextWithIconSimpleText>();
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public string GetCorrectSpriteLine(string line, ref List<RPGTalkSprite> sprites, ref List<RPGTalkSprite>spritesUsed, int spriteNum, int initialBracket, int finalBracket, int lineWithSprite, string tmpSpriteAtlas)
|
|
{
|
|
|
|
if (hasUIText)
|
|
{
|
|
|
|
//Neat, we definely have a sprite with a valid number. Time to keep track of it
|
|
RPGTalkSprite newSprite = new RPGTalkSprite();
|
|
newSprite.sprite = sprites[spriteNum].sprite;
|
|
newSprite.width = sprites[spriteNum].width;
|
|
newSprite.height = sprites[spriteNum].height;
|
|
newSprite.spritePosition = initialBracket;
|
|
//Make sure that the the sprite only work for that next line to be added to RpgTalkElements
|
|
//newSprite.lineWithSprite = rpgtalkElements.Count;
|
|
newSprite.lineWithSprite = lineWithSprite;
|
|
newSprite.animator = sprites[spriteNum].animator;
|
|
|
|
spritesUsed.Add(newSprite);
|
|
|
|
//Looking good! We found out that a sprite should be there and we are already keeping track of it
|
|
//But now we should remove the [sprite=X] from the line.
|
|
//The magic here is that we will replace it with the <color=#00000000> tag and the content will be
|
|
//a text with the length of the sprite's width. So in fact there will be text in there so the next word
|
|
//will have the right margin, but the text will be invisible so the sprite can take its place
|
|
string filledText = "";
|
|
for (int i = 0; i < Mathf.CeilToInt(newSprite.width); i++)
|
|
{
|
|
//The letter "S" is used to fill because in most fonts the letter S occupies a perfect character square
|
|
filledText += "S";
|
|
}
|
|
|
|
if (finalBracket == line.Length - 1)
|
|
{
|
|
//if the sprite was the last thing on the text, we should place an empty space to align correctly the vertexes
|
|
filledText += "SS";
|
|
}
|
|
return line.Substring(0, initialBracket) +
|
|
"<color=#00000000>" + filledText + "</color>" +
|
|
line.Substring(finalBracket + 1);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
#if RPGTalk_TMP
|
|
|
|
//if we are using TMP, everything is easier. We just need to make the text says <sprite=X index=Y> and let TMP's components do the rest
|
|
return line.Substring(0, initialBracket) +
|
|
"<sprite=\"" + tmpSpriteAtlas + "\" index="+spriteNum+">" +
|
|
line.Substring(finalBracket + 1);
|
|
#endif
|
|
return line;
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Structure to hold pre-computed animation data.
|
|
/// </summary>
|
|
private struct VertexAnim
|
|
{
|
|
public float angleRange;
|
|
public float angle;
|
|
public float speed;
|
|
}
|
|
|
|
//Jitters the part of the text
|
|
public IEnumerator Jitter(RPGTalkJitter jitter)
|
|
{
|
|
#if RPGTalk_TMP
|
|
if(TMPText == null)
|
|
{
|
|
Debug.LogError("Only TextMeshPro users can use the Jitter Tag");
|
|
yield return null;
|
|
}
|
|
|
|
|
|
TMP_TextInfo textInfo = TMPText.textInfo;
|
|
// Cache the vertex data of the text object as the Jitter FX is applied to the original position of the characters.
|
|
TMP_MeshInfo[] cachedMeshInfo = textInfo.CopyMeshInfoVertexData();
|
|
int characterCount = textInfo.characterCount;
|
|
|
|
// Create an Array which contains pre-computed Angle Ranges and Speeds for a bunch of characters.
|
|
VertexAnim[] vertexAnim = new VertexAnim[1024];
|
|
for (int i = 0; i < 1024; i++)
|
|
{
|
|
vertexAnim[i].angleRange = Random.Range(10f, 25f);
|
|
vertexAnim[i].speed = Random.Range(1f, 3f);
|
|
}
|
|
|
|
int loopCount = 0;
|
|
|
|
Matrix4x4 matrix;
|
|
|
|
while (true)
|
|
{
|
|
int repeatUntil = jitter.jitterPosition + jitter.numberOfCharacters;
|
|
|
|
|
|
// yield until we have all the characters in the jitter
|
|
while (characterCount < repeatUntil)
|
|
{
|
|
|
|
// Update the copy of the vertex data for the text object.
|
|
cachedMeshInfo = textInfo.CopyMeshInfoVertexData();
|
|
characterCount = textInfo.characterCount;
|
|
|
|
|
|
yield return new WaitForEndOfFrame();
|
|
continue;
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = jitter.jitterPosition; i < repeatUntil; i++)
|
|
{
|
|
TMP_CharacterInfo charInfo = textInfo.characterInfo[i];
|
|
|
|
// Skip characters that are not visible and thus have no geometry to manipulate.
|
|
if (!charInfo.isVisible)
|
|
continue;
|
|
|
|
// Retrieve the pre-computed animation data for the given character.
|
|
VertexAnim vertAnim = vertexAnim[i];
|
|
|
|
// 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;
|
|
|
|
// If we dont have the vertices yet, don't do it
|
|
if (sourceVertices.Length < vertexIndex+3)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// 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.
|
|
Vector2 charMidBasline = (sourceVertices[vertexIndex + 0] + sourceVertices[vertexIndex + 2]) / 2;
|
|
|
|
// 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 = charMidBasline;
|
|
|
|
Vector3[] destinationVertices = textInfo.meshInfo[materialIndex].vertices;
|
|
|
|
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;
|
|
|
|
vertAnim.angle = Mathf.SmoothStep(-vertAnim.angleRange, vertAnim.angleRange, Mathf.PingPong(loopCount / 25f * vertAnim.speed, 1f));
|
|
Vector3 jitterOffset = new Vector3(Random.Range(-.25f, .25f), Random.Range(-.25f, .25f), 0);
|
|
|
|
matrix = Matrix4x4.TRS(jitterOffset * jitter.jitter, Quaternion.Euler(0, 0, Random.Range(-5f, 5f) * jitter.angle), Vector3.one);
|
|
|
|
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;
|
|
|
|
vertexAnim[i] = vertAnim;
|
|
}
|
|
|
|
// Push changes into meshes
|
|
for (int i = 0; i < textInfo.meshInfo.Length; i++)
|
|
{
|
|
textInfo.meshInfo[i].mesh.vertices = textInfo.meshInfo[i].vertices;
|
|
TMPText.UpdateGeometry(textInfo.meshInfo[i].mesh, i);
|
|
}
|
|
|
|
loopCount += 1;
|
|
|
|
yield return new WaitForSeconds(0.1f);
|
|
}
|
|
#else
|
|
|
|
Debug.LogError("Only TextMeshPro users can use the Jitter Tag");
|
|
yield return null;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
#pragma warning restore 0162 |