Tuesday, May 31, 2016

unity - Fading Text in one character at a time


I'm a Unity game developer. I have a bit of a conundrum. I have two enumerators, one that displays text one character at a time, and the other takes the whole text and fades it in over time:


private IEnumerator FadeTo(float aValue, float tValue)

{
float alpha = GameText.GetComponent().color.a;
for (float i = 0; i < 1.0f; i += Time.deltaTime / tValue)
{
Color alphaChange = new Color(1, 1, 1, Mathf.Lerp(alpha, aValue, i));
GameText.GetComponent().color = alphaChange;
yield return null;
}
}
private IEnumerator CharXChar(string text)

{
for (int i = 0; i < text.Length; i++)
{
yield return new WaitForSeconds(0.1f);
GameText.GetComponent().text = GameText.GetComponent().text + text[i];
}
}

The problem is, I do not know how to get the FadeTo enumerator to modify each character individually. I just need a step in the right direction. Any help would be greatly thanked.



Answer




Here's a very ugly solution using rich text. It wraps a tag around every letter, then updates the alpha values as the fade progresses along the string.


I'm using the StringBuilder to cut down on unnecessary string allocations, but this still generates one new allocation every frame while the fade is running (fortunately it's always the same size so it shouldn't cause much trouble).


There are probably more efficient options, like positioning a second text element to hold the fading characters, but this was quick and dirty:


using UnityEngine;
using UnityEngine.UI;
using System.Collections;

[RequireComponent(typeof(Text))]
public class TextFade : MonoBehaviour {


[Tooltip("Number of seconds each character should take to fade up")]
public float fadeDuration = 2f;

[Tooltip("Speed the reveal travels along the text, in characters per second")]
public float travelSpeed = 8f;

// Cached reference to our Text object.
Text _text;

Coroutine _fade;


// Lookup table for hex characters.
static readonly char[] NIBBLE_TO_HEX = new char[] {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'A', 'B',
'C', 'D', 'E', 'F'};

// Use this for initialization
void Start () {

_text = GetComponent();

// If you don't want the text to fade right away, skip this line.
FadeTo(_text.text);
}

public void FadeTo(string text)
{
// Abort a fade in progress, if any.
StopFade();


// Start fading, and keep track of the coroutine so we can interrupt if needed.
_fade = StartCoroutine(FadeText(text));
}

public void StopFade() {
if(_fade != null)
StopCoroutine(_fade);
}


// Currently this expects a string of plain text,
// and will not correctly handle rich text tags etc.
IEnumerator FadeText(string text) {

int length = text.Length;

// Build a character buffer of our desired text,
// with a rich text "color" tag around every character.
var builder = new System.Text.StringBuilder(length * 26);
Color32 color = _text.color;

for(int i = 0; i < length; i++) {
builder.Append(" builder.Append(NIBBLE_TO_HEX[color.r >> 4]);
builder.Append(NIBBLE_TO_HEX[color.r & 0xF]);
builder.Append(NIBBLE_TO_HEX[color.g >> 4]);
builder.Append(NIBBLE_TO_HEX[color.g & 0xF]);
builder.Append(NIBBLE_TO_HEX[color.b >> 4]);
builder.Append(NIBBLE_TO_HEX[color.b & 0xF]);
builder.Append("00>");
builder.Append(text[i]);

builder.Append("");
}

// Each frame, update the alpha values along the fading frontier.
float fadingProgress = 0f;
int opaqueChars = -1;
while(opaqueChars < length - 1) {
yield return null;

fadingProgress += Time.deltaTime;


float leadingEdge = fadingProgress * travelSpeed;

int lastChar = Mathf.Min(length - 1, Mathf.FloorToInt(leadingEdge));

int newOpaque = opaqueChars;

for(int i = lastChar; i > opaqueChars; i--) {
byte fade = (byte)(255f * Mathf.Clamp01((leadingEdge - i)/(travelSpeed * fadeDuration)));
builder[i * 26 + 14] = NIBBLE_TO_HEX[fade >> 4];

builder[i * 26 + 15] = NIBBLE_TO_HEX[fade & 0xF];

if (fade == 255)
newOpaque = Mathf.Max(newOpaque, i);
}

opaqueChars = newOpaque;

// This allocates a new string.
_text.text = builder.ToString();

}

// Once all the characters are opaque,
// ditch the unnecessary markup and end the routine.
_text.text = text;

// Mark the fade transition as finished.
// This can also fire an event/message if you want to signal UI.
_fade = null;
}

}

Here's an example of the effect:


Animation showing text fading in character by character.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...