TypeWriter Effect

Hello, I was learning how to make a choose your own story game, and wanted to create a typewriter effect for the dialog. I found some code online, that, I believe would have worked if it weren’t for the fact that the course work redirected the text within the inspector to a different script called “states” so that when the player chose a state, the text would then change and be sent to the text spot. I tried to implement it myself and got coroutine errors. I haven’t learned about coroutines and ienumerators yet so I have no idea how to fix this. Anyway, here’s the code I found online:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TypeWriterEffect : MonoBehaviour
{
    public float delay = 0.1f;
    public string fullText;
    private string currentText = "";

    void Start()
    {
        StartCoroutine(ShowText());
    }
    IEnumerator ShowText()
            {
                for(int index = 0; index <= fullText.Length; index++)
                {
                    currentText = fullText.Substring(0,index);
                    yield return new WaitForSeconds(delay);
                }
            }
}

Here is the original State script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "State")]
public class State : ScriptableObject
{
        [TextArea(10,14)] [SerializeField] string storyText;
        [TextArea(2,5)] [SerializeField] string choice1;
        [TextArea(2,5)] [SerializeField] string choice2;
        [TextArea(2,5)] [SerializeField] string choice3;
        [SerializeField] State[] nextStates;

        public State[] GetNextStates()
        {
            return nextStates;
        }
        public string GetStoryState()
        {
            return storyText;
        }
        public string GetChoice1()
        {
            return choice1;
        }
        public string GetChoice2()
        {
            return choice2;
        }
        public string GetChoice3()
        {
            return choice3;
        }
}

and how I attempted to change it:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "State")]
public class State : ScriptableObject
{
        [TextArea(10,14)] [SerializeField] string storyText;
        [TextArea(2,5)] [SerializeField] string choice1;
        [TextArea(2,5)] [SerializeField] string choice2;
        [TextArea(2,5)] [SerializeField] string choice3;
        [SerializeField] State[] nextStates;
        private string currentText = "";
        [SerializeField] float delay;

        public State[] GetNextStates()
        {
            return nextStates;
        }
        public string GetStoryState()
        {
        StartCoroutine(ShowText());
        }
        public string GetChoice1()
        {
            return choice1;
        }
        public string GetChoice2()
        {
            return choice2;
        }
        public string GetChoice3()
        {
            return choice3;
        }
    IEnumerator ShowText()
        {
            for(int index = 0; index <= storyText.Length; index++)
            {
                currentText = storyText.Substring(0,index);
                yield return new WaitForSeconds(delay);
            }
        }
}

Hi Amber,

The error message says that there is no StartCoroutine in the context but I cannot see any issue in your TypeWriteEffect class.

Does MonoBehaviour have the same colour as your classname? If not, there might be a problem with Visual Studio. Please watch lecture “Fixing Visual Studio Problems” (currently #4).

Okay, I’ve somewhat narrowed down what the problem possibly is. At the moment it is telling me that not all code paths return a value. So I’ve tried to set it up this way:

    IEnumerator ShowText()
        {
            for(int index = 0; index <= storyText.Length; index++)
            {
                if(currentText.Length < storyText.Length)
                {
                    currentText = storyText.Substring(0,index);
                    yield return new WaitForSeconds(delay);
                }
                else
                {
                    yield return currentText;
                }
            }

And then

    IEnumerator ShowText()
        {
            for(int index = 0; index <= storyText.Length; index++)
            {
                if(currentText.Length < storyText.Length)
                {
                    currentText = storyText.Substring(0,index);
                    yield return new WaitForSeconds(delay);
                }
                else
                {
                    yield return currentText;
                }
            }
            yield return currentText;
        }

I’ve also tried to yield return null;

but it still keeps telling me that there is a route that doesn’t return a value. I can’t figure out how I should reword this to close all holes in the code.

Are there any error messages regarding yield return currentText? Since the return type is IEnumerator, one cannot return a string. Only WaitForSeconds, WaitForFixedUpdate, Coroutine, MonoBehaviour.StartCoroutine and null work.

If that’s not the issue, maybe this will fix the “route” issue:

        IEnumerator ShowText()
        {
            for(int index = 0; index <= storyText.Length; index++)
            {
                if(currentText.Length < storyText.Length)
                {
                    currentText = storyText.Substring(0,index);
                    yield return new WaitForSeconds(delay);
                }
            }

            yield return currentText;
        }

Your approach looks almost correct. However, at the moment, ShowText does not show anything. All it does is to process the string. Look up the StringBuilder class. Alternatively, concatenate the single characters or parts of the sentence. You need three variables: one for the source text, one for the current text and one control variable for the index. Have the ShowText method display the currentText string.

So far, looking around, I’m finding that I can’t run a coroutine on a scriptable object because startcoroutine is a monobehavior thing. So now I’m looking to work that around now. Hoping this leads me somewhere.

Good job on narrowing down the issue a bit furhter. :slight_smile:

What you could do is to write a MonoBehaviour script, e.g. TextDisplay or TypeWriterEffect, assign it to your Text Display game object with the Text or TMP component and have your TextDisplay (or TypeWriterEffect) component pass on the relevant data to the text property. Your ShowText method is almost correct.

Connect your AdventureGame instance with the aforementioned instance. Look up method overloading on DotNetPerls to learn how to pass on data to methods and other instances.

And if that’s too complicated, move your ShowText method to your AdventureGame class. That’s fine.

How are you getting on with this, @Amber_Rose?

I haven’t found a solution to it yet, but I plan to rework the script at some point. I’ve moved onto other aspects of my game, and feel that, if I come back, I’d be better equipped to fix the problem.

I am marking this as solved because I reworked this code and was able to get the typewriter effect to work. The link is to a new question that I made that has the reworked code, that happens to have a separate issue.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms