2
\$\begingroup\$

The following methods just write strings to the screen over time to make it look like a typing effect, instead of all at once. The only difference is the argument in the foreach loop.

I end up having lots of similar methods like these. How do I refactor them?

public IEnumerator TypeCharText(string message, Text textComp, bool resetTextComp = false)
{
    if (resetTextComp)
        textComp.text = "";

    foreach (char letter in message.ToCharArray())
    {
        textComp.text += letter;

        if (textComp.text.Length >= message.Length)
            OnMessageTyped(message);

        yield return new WaitForSeconds(2f);
    }
}

public IEnumerator TypeWordText(string message, Text textComp, bool resetTextComp = false)
{
    if (resetTextComp)
        textComp.text = "";

    foreach (string word in message.Split(' '))
    {
        textComp.text += " " + word;

        if (textComp.text.Length >= message.Length)
            OnMessageTyped(message);

        yield return new WaitForSeconds(2f);
    }
}

Here's what I have attempted:

public static IEnumerator TypeText<T>(Func<string, T> action, string message, Text textComp, bool resetTextComp = false)
{
    if (resetTextComp)
        textComp.text = "";

    textComp.text += action(message);

    if (textComp.text.Length >= message.Length)
        OnMessageTyped(message);

    yield return new WaitForSeconds(wordPause);
}

and to call it (StartCoroutine is a built-in Unity3D method that just calls a method):

StartCoroutine(TextTyper.TypeText<string>((x) => { x.Split(' '); return x;}, message, vanishText, true));
StartCoroutine(TextTyper.TypeText<string>((x) => { x.ToCharArray(); return x;}, message, vanishText, true));

It doesn't split the string up though or turn it into char array; action(message) still has the entire string.

\$\endgroup\$
2
\$\begingroup\$

You need a way to specify how the message will be transformed into a sequence, therefore your function should be typed as Func<string, IEnumerable<string>>. And the lambda expressions would look like this:

x => x.Split(' ')
x => x.Select(c => new string(c, 1))

But why pass a delegate for the split-algorithm instead of simply passing the sequence of text pieces directly?

public IEnumerator TypeWordText(IEnumerable<string> pieces, message, Text textComp,
                                bool resetTextComp = false, string separator = "")
{
    if (resetTextComp)
        textComp.text = "";

    foreach (string piece in pieces)
    {
        textComp.text += separator + piece;

        if (textComp.text.Length >= message.Length)
            OnMessageTyped(message);

        yield return new WaitForSeconds(2f);
    }
}

Since in one method you add a space and not in the other, I have added the separator as optional parameter.

I also suggest to transform the char-sequence into a string-sequence (with strings of length one), so that you can always work with strings.

TextTyper.TypeText(message.Split(' '), message, vanishText, true, " ")

TextTyper.TypeText(message.Select(c => new string(c, 1)), message, vanishText, true)

Note that a string is an IEnumerable<char>. The Selects will be executed lazily at each loop of the foreach-statement. Therefore, in a certain way, you are still passing an algorithm to the method in the second call, but hidden in an enumerator.


Algorithm: But why split the string at all, just in order to recombine it afterwards? Instead of splitting the message, you could keep it as is and keep track of the typed length in an int variable. Then display message.Substring(0, typedLength). This change requires a strategy for the increment of the typed length. See Strategy pattern.

public interface ITypingStrategy
{
    void TypeNext(string message, ref typedLength);
}

Now you can implement different strategies like CharByCharTypingStrategty or WordByWordTypingStrategy as classes. The adapted TypeWordText method would have an ITypingStrategy argument instead of an enumeration of text pieces.

|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.