Search Unity

Resolved How to loop a repetitive movement during a specific time frame

Discussion in 'Scripting' started by Peilo77, Jan 12, 2022.

  1. Peilo77

    Peilo77

    Joined:
    Aug 18, 2020
    Posts:
    22
    Dear all,

    I'd like to ask how to loop a repetitive movement during a specific time frame?

    Please reply to this first question for help and if you want to help me more read on :)

    So, more specifically here is what I am trying to do:

    In a 2d shoot'em up I want to create an enemy with a movement pattern consisting of several different repetitive moves that succeed each other in a loop. Since I am a beginner I need to first figure out how to make it possible to loop the first movement at which I didn't succeed.

    The first move is an up and down movement that should last for 10 seconds (and afterwards it should begin the next movement loop).

    Here is the code for the first movement, but it doesn't do the "ping pong move" while in the Start method and does it endlessly while in the Update method. What am I missing or what did I code wrong? Please help :)

    [SerializeField] GameObject enemy; // reference to the gameObject I want to move

    [SerializeField] float pingPongSpeed = 0.3f // speed of the movement

    [SerializeField] int seconds = 10; // the timer for the loop; it's the amount of seconds I want the loop to run

    void Start ()
    {
    for (int i = 0; i < seconds; i++)
    {
    float y = Math.PingPong(Time.time * pingPongSpeed, 1) * 3f - 1.5f;
    enemy.transform.position = new Vector3(transform.position.x, y, transform.position.z);
    }
    }

    I found the PingPong code from the internet (which works perfectly on its own) and inserted it in a loop in hopes it would loop during 10 seconds or ten times but nothing happens at all.

    If I understood correctly; i starts at 0 and gets incremented by 1 as long as it's not higher than 9 (since it starts at 0 I have my 10 seconds) and each time it gets incremented by 1 it runs the body of the loop or did I get this wrong?

    Many thanks in advance for your help on this.
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Quite the contrary, it's all happening. But it's all happening immediately, in a single frame, because you have never paused and let the engine show any frames.

    You can use either Update instead of a loop, or a coroutine.
    Code (csharp):
    1. private void Start() {
    2.     StartCoroutine(RunLoop());
    3. }
    4.  
    5. private IEnumerator RunLoop() {
    6.     for (float t=0f; t < 10f; t += Time.deltaTime) {
    7.         float y = Math.PingPong(Time.time * pingPongSpeed, 1) * 3f - 1.5f;
    8.         enemy.transform.position = new Vector3(transform.position.x, y, transform.position.z);
    9.         yield return null; // "wait for a frame"
    10.     }
    11. }
     
    Peilo77 likes this.
  3. Peilo77

    Peilo77

    Joined:
    Aug 18, 2020
    Posts:
    22
    Wow you‘re fast :)

    many thanks for taking the time to look at my problem. I will try this out as soon as I am home :)

    thanks so much
     
  4. Peilo77

    Peilo77

    Joined:
    Aug 18, 2020
    Posts:
    22
    Works like a charm :) thanks again.

    I'd just wanted to ask if I understood correctly what is happening here:

    1. you replaced the integers with floats that you declare as "float t" (you called them"t" for time) directly in the loop?

    2. you increment the floats by Time.deltaTime so "time" runs smoothly from 0 up to 9.999 and with the same speed on every system/computer?

    3. you put "yield return null" at the end of the loop body so that the script is read through the loop body and then waits until the next frame and then continues its execution back from the start?

    If possible, I would have another question as well: if I would like to have 3 different moves and once it finishes the third and final move it starts again from the beginning, how can I "jump" from one coroutine to the other and restart once the final one finished? I think I would need to reset the float values to 0 in order that they restart but would you use bools or another type of coding?
     
  5. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Yeah. That form of for loop is a pattern I use a lot for running a timed coroutine. "t" counts up from 0 and represents the amount of time that loop has been running.

    Yep

    For clarity, it continues its execution starting immediately after the "yield" line. In this case that does immediately jump back to the beginning of the for loop's block, but it doesn't have to.

    Code (csharp):
    1. Debug.Log("First frame");
    2. yield return null;
    3. Debug.Log("Second frame");
    4. yield return null;
    5. yield return null;
    6. yield return null;
    7. yield return null;
    8. yield return null;
    9. Debug.Log("Seventh frame");
    You can call StartCoroutine(RunOtherLoop()) from within a coroutine to kick off a different loop. (Be aware that starting a new coroutine does NOT end the current coroutine. It also doesn't pause the current coroutine, unless you do it like this:
    Code (csharp):
    1. yield return StartCoroutine(RunOtherLoop());
    But if you're just calling it at the end of the other coroutine that's not a concern.

    Or you can copy and paste a second for loop after the first one in the same coroutine. Coroutines are pretty versatile.
     
    Peilo77 likes this.
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
  7. Peilo77

    Peilo77

    Joined:
    Aug 18, 2020
    Posts:
    22
    Thank you so much for your time and extremely useful explanations which I'll take time to read through and which should definitely help me to get this done (I'll mark this thread as resolved once everything works out).

    Anyway, this is a significant shortcut to general knowledge about coroutines as well as to my specific problem resolution.

    You guys rock big time!
     
    Kurt-Dekker likes this.
  8. Peilo77

    Peilo77

    Joined:
    Aug 18, 2020
    Posts:
    22
    After trying different versions to see how to
    implement different movements that loop or just resolve once, the lines of code and explanations of StarManta as well as the collected references from Kurt-Dekker not only resolved the problem but makes the coroutine with its different “yield” commands so much more comprehensive.

    I’ll label this thread as resolved and need (I really do) to point out StarManta’s “time loop” above that is pure gold for a beginner like me :)
     
    Kurt-Dekker likes this.