5
\$\begingroup\$

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<Text>().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<Text>().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>().text = GameText.GetComponent<Text>().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.

\$\endgroup\$
5
  • \$\begingroup\$ Have you tried making it so that the fadeTo method takes in a character so that you could build a loop that grabs each character and calls that method to it, thus fading them individually? \$\endgroup\$
    – n_plum
    Commented Jan 31, 2017 at 21:07
  • \$\begingroup\$ It came to mind, and I've been giving it thought, but I'm becoming lost on how I would get it to do that specifically, because if I could figure it out, I think that would answer the first part of my question (the part that's not in the edit). \$\endgroup\$
    – Sora
    Commented Jan 31, 2017 at 21:12
  • \$\begingroup\$ Maybe comment out this one, not to lose it, and restructure a new version of it that involves taking in a character. Then get the text from your canvas, convert it to a charArray and fade those. Also in terms of the scene thing - the more scenes and the more transitining you have in unity the more processing, fps, etc are used \$\endgroup\$
    – n_plum
    Commented Jan 31, 2017 at 21:15
  • \$\begingroup\$ So pretty much I should stick to a string array or method change solution for the changing the text value, okay gotcha. \$\endgroup\$
    – Sora
    Commented Jan 31, 2017 at 21:17
  • \$\begingroup\$ Re: the edit, I don't think the method you use for fading the characters has any dependency on the game logic you use for selecting text to display in each scene or deciding whether to use the fading transition you have. These problems can be solved independently (the fade transition becomes just one more tool the game logic can call upon when appropriate), so try to focus your question on a single issue at a time. (If you want help with game logic part of the problem, consider asking about it in its own post) \$\endgroup\$
    – DMGregory
    Commented Jan 31, 2017 at 21:30

2 Answers 2

4
\$\begingroup\$

Here's a very ugly solution using rich text. It wraps a <color=#FFFFFF00> </color> 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<Text>();

        // 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("<color=#");
            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("</color>");
        }

        // 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.

\$\endgroup\$
9
  • \$\begingroup\$ While I like it, I don't think that it's going to be very practical in my situation, but I'll definitely take it into consideration as I may not find what I need. \$\endgroup\$
    – Sora
    Commented Jan 31, 2017 at 4:57
  • 1
    \$\begingroup\$ @Sora Sounds good. If there are particular aspects of your situation that would make a solution like this impractical, please consider editing your question to describe these constraints - it will help guide & focus future answers on your specific needs. \$\endgroup\$
    – DMGregory
    Commented Jan 31, 2017 at 17:50
  • 1
    \$\begingroup\$ efficient or not this is a pretty clever solution to the problem. \$\endgroup\$ Commented Jan 31, 2017 at 21:06
  • 1
    \$\begingroup\$ It works just fine, as you can see in the animation above. You might want to pay attention to the line at the top that says [RequireComponent(typeof(Text))] - that means that as-written you MUST put this script on your Text object, not the containing canvas. Alternatively, you can replace _text with a public UnityEngine.UI.Text variable and wire it up in the Inspector, or find the first Text in the child hierarchy using GetComponentInChildren<Text>(), etc. \$\endgroup\$
    – DMGregory
    Commented Feb 3, 2017 at 4:56
  • 1
    \$\begingroup\$ Glad I could be of use! Best of luck in your project! I hope to play it when you finish! \$\endgroup\$
    – DMGregory
    Commented Feb 4, 2017 at 6:40
-1
\$\begingroup\$

There are a couple things you could try but not necessarily with the code you have right now, but rather with tools Unity provides.

You could first try adding in a canvas with the text, when you add it in, from there you can directly modify the alpha value of the canvas which would fade it in/out. More Here.

You can also try GUITextures as they have fade properties as well. They have access to colors too. More Here.

And here is a C# solution I found, I am only linking this one because I can't take credit for it ;) It seems to be similar to yours in that it uses enumerators but it also does the update to it with input from the keyboard, which you could easily alter so whatever you want can call those methods.

\$\endgroup\$
8
  • \$\begingroup\$ All of these suggestions seem to fade the text as a whole, rather than one character at a time as requested in the question. Have I misunderstood? \$\endgroup\$
    – DMGregory
    Commented Jan 31, 2017 at 17:49
  • \$\begingroup\$ I wasn't 100% sure because they said they have one that fades per character and one that does them all. I don't particularly know for sure, but you may be able to do per character fade with something like the canvas, if you did some sort of fade across, like horizontally. \$\endgroup\$
    – n_plum
    Commented Jan 31, 2017 at 17:53
  • \$\begingroup\$ Your solution @DMGregory, imo, was viable as well, but since they didn't seem to want that I was just trying to provide other sources I know can do similar things. \$\endgroup\$
    – n_plum
    Commented Jan 31, 2017 at 17:54
  • \$\begingroup\$ I think you may have misread the question. They have one method that make characters appear one at a time (jumping from absent to opaque), and one method that makes the whole text fade gradually as a unit. They've asked for a method that gradually fades a character at a time. Your horizontal fade is an interesting proposal in this regard - I'd recommend expanding on that, to demonstrate how it can be achieved. \$\endgroup\$
    – DMGregory
    Commented Jan 31, 2017 at 18:00
  • \$\begingroup\$ Hmm.. It seems you can't be directional with most of these fade methods as they take in only what to fade to and timers. \$\endgroup\$
    – n_plum
    Commented Jan 31, 2017 at 18:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .