How to keep same speed of automatic movement while looping a Coroutine?

Hello everyone, I am new in Unity. I am trying to create an automatic horizontal movement (left to right) of a text to display information to the user. The text shows the first information, then waits for seconds and then changes automatically to show the next piece of information. The movement should loop automatically as well. My problem is that in every repetition of the movement, the speed of the text increases. How can I maintain a constant movement keeping the same speed in every loop?
I tried to start my coroutine through the “Start” method but it doesn’t move, it moves if I locate it in “Update” method.
Please find my code below:

public class textLoop : MonoBehaviour
{
// setting boundaries and speed
    public float speed = 100f;
    private float textStart = 804;
    private float textStop = 404;
    RectTransform textMovement;
    [SerializeField] bool Looping = false; 
    private float secondTextStop = 0;
    private float thirdTextStop = -404;
    private float fourthTextStop = -804;
    public float seconds;


//Method to create movement
private void mainMovement()
{
textMovement.Translate(Vector3.left * speed * Time.deltaTime);
}
    
private void Start()
{  
textMovement = gameObject.GetComponent<RectTransform>();
}

private void Update()
{
    StartCoroutine(FirstMovement());
}

IEnumerator FirstMovement()
    {
        if(textMovement.localPosition.x > textStop)
    {
        mainMovement();
    }
    
    yield return new WaitForSecondsRealtime(seconds);

    if(textMovement.localPosition.x > secondTextStop)
    {
        mainMovement();
    }
    yield return new WaitForSecondsRealtime(seconds);

    if(textMovement.localPosition.x > thirdTextStop)
    {
        mainMovement();
    }
    yield return new WaitForSecondsRealtime(seconds);

    if(textMovement.localPosition.x > fourthTextStop)
    {
        if(Looping)
        {
            textMovement.localPosition = Vector3.right * textStart;
        }

    }   
    yield return null;
    
    }
}

Thank you!

,

Man the Coroutine Tutorials/Documentation really has to be improved. This issue is so common that i did not even have to look at the code to know what the issue was when i read My problem is that in every repetition of the movement, the speed of the text increases

The issue is that you currently start a lot of coroutines which are each doing the same thing: move your text. A Coroutine is started and then runs alongside all other code (but not in a seperat thread).
Each time you reach a yield statement the code is paused until the given yield statement is reached and execution of other code is done in the meantime.

Given this we now look at:

private void Update()
{
    StartCoroutine(FirstMovement());
}

Each Update you start a new instance of your Coroutine.
What you want to do instead is to just do this once:

private void Start()
{
    StartCoroutine(FirstMovement());
}

This however will break your current version as it funny enough currently only ever moves far enough because you start a new instance every Update. To fix this you have to repeat the mainMovement part and wait a frame until the stop condition is reached. To further improve this i’d suggest that you change your current code to the version below:

Instead of creating a variable for each stop have an array of stop locations:

  public float[] stopLocations = new float[] {0, -404, -804}

Which you can then iterate over to shorten your code and make it more readable and easier to extend (you don’t have to add any code to add another stop):

IEnumerator FirstMovement()
    {
        foreach(var currentStopPos in stopLocations)
        {
            if(textMovement.localPosition.x > currentStopPos)
            {
                //move one step, then wait for the next frame with "yield return null"
                mainMovement();
                yield return null;
            } else {
                //stop position, reached, wait a bit.
                yield return new WaitForSecondsRealtime(seconds);
            }
        }
        if(Looping)
        {
            textMovement.localPosition = Vector3.right * textStart;
            //to make this loop we have to restart this routine: 
            StartCoroutine(FirstMovement());
        }
    }

This should then work. (no guarantees, obviously not tested).
Let me know if that helped or if there are any questions.

Hello @Captain_Pineapple, thanks for replying.

You are totally right, it was simpler to create an array, I didn’t think to do that, it actually works well, the text stops where it needs to stop. However, the coroutine only moves when is locate it in the Update method, when located in Start, the text just moves a few pixels and then stops. I understood your explanation and why the speed is increased when running in the Update method, but I still don’t know how to make it work from the Start method. I will keep trying, if there is any other idea, please share!

I test it and I needed to modify little the loop and removing the “Else” conditional as below, (this is what I runned from the Update method):

public class textLoop : MonoBehaviour
{
// setting boundaries and speed
    public float speed = 100f;
    public float textStart = 804;
    public float textStop = -808;
    RectTransform textMovement;
    [SerializeField] bool Looping = false; 
    public float seconds;
    private float[] stopLocations = new float[]{404,0,-404,-808}; 


//Method to create movement
private void mainMovement()
{
textMovement.Translate(Vector3.left * speed * Time.deltaTime);
}
    
private void Start()
{  
textMovement = gameObject.GetComponent<RectTransform>();
}

private void Update()
{
  StartCoroutine(FirstMovement());  
}

IEnumerator FirstMovement()
    {
        foreach (var currentStopPos in stopLocations)
        {
               if(textMovement.localPosition.x > currentStopPos)
            {
             mainMovement();
            }
            
            yield return new WaitForSeconds(seconds);
        }

        if(textMovement.localPosition.x < textStop)
        {
            if (Looping)
            {
            textMovement.localPosition = Vector3.right * textStart;
            StartCoroutine(FirstMovement());
            }
        }
    
    }
}

Thank you!