There's a neater way to do this, right? Coroutines

Ok, so I’m doing a bowling game, and I have it so that the ball hits the back of the lane, and the everything does its thing. But right now the code looks like

void pinThing()
{
  IEnumerator pickerUpperGoesDown()
    {
     // while loop for lerping position. 
      //add the pins within a spherecast of the lifter to an array
      yield return StartCoroutine(pickerUpperGoesUp());
     }

   IEnumerator pickerUpperGoesUp()
   {
     //lerp everythig back up
    yield return StartCoroutine(sweeperGoesDown());
    }

   IEnumerator sweeperGoesDown()
   {
     //lerp the bar down
     yield return StartCoroutine(sweeperGoesBack());
   }

 etc

and well, i feel like there has to be a nicer solution for this, rather than calling one coroutine from another coroutine from another coroutine, because i can just see that getting incredibly messy and hard to debug in more complex projects.

edit (moved from answer)

its multiple animations playing in sequence. when i put them all into one coroutine they play out together rather than in sequence.

IEnumerator lifterGoesUp()
{
    float time = 0;
    float LerpSpeed = .5f;
    while(time<1)
    {

        pickerUpper.transform.position = Vector3.Lerp(pickerUpperEndPoint.position, pickerUpperStartPoint.position, time);
        time += Time.deltaTime * LerpSpeed;
        yield return null;
    }
    yield return StartCoroutine(pinMovementVertical());
}


IEnumerator pinMovementVertical()
{

    pinsweeper.SetActive(true);

    float time = 0;
    float LerpSpeed = .75f;
    while(time<1)
    {
        pinsweeper.transform.position = Vector3.Lerp(pinsetRisePoint.position, pinsetStartPoint.position, time);
        time += Time.deltaTime * LerpSpeed;
        yield return null;
    }
    yield return StartCoroutine(pinMovementHorizontal());
}

IEnumerator pinMovementHorizontal()
{

    StopCoroutine(pinMovementVertical());

    float time = 0;
    float LerpSpeed = .75f;

    while(time<1)
    {
        pinsweeper.transform.position = Vector3.Lerp(pinsetStartPoint.position, pinsetEndPoint.position, time);
        time += Time.deltaTime * LerpSpeed;
        yield return null;
     }
     pinsweeper.SetActive(false);
    }
}

Well, unless there’s a good reason for it, why did you split the actions into seperate coroutines in the first place? If it’s a sequence of actions that should play out in sequence, just create a single coroutine. Since you only provided an abstract overview of your structure, we can’t really tell you what you should / shouldn’t do since you don’t do anything in the code you provided :slight_smile:

Apart from that, you can run sub coroutines without StartCoroutine for several years now. This is not only better for performance and reduces overhead, it also has the benefit that sub coroutines would be cancelled with the parent which isn’t the case when you use StartCoroutine for nested coroutines. When you use StartCoroutine you start an independent coroutine. Yielding the Coroutine that StartCoroutine returns just lets the outer coroutine wait for the inner one. Stopping the outer coroutine does not affect the inner one.

Since we don’t know what you’re actually doing and what you would call “nice” or “nicer” we can’t really help you here.

edit
Based on the new information let me add some more details. First of all you said

when i put them all into one coroutine
they play out together rather than in
sequence.

No, why would they? A coroutine is a sequence of actions that are executed in order. When it hits a yield statement the execution is suspended and resumed later at this point. So if you have 3 animations playing out in order, you just do them one after the other and you’re fine.

Another point I want to clear up is this line:

StopCoroutine(pinMovementVertical());

This does literally nothing besides generating garbage :slight_smile: You can not stop a coroutine this way. When you call a generator method (in your case the oinMovementVertical method), this method will create a new instance of a statemachine that it returns. Nothing else will happen when you do that. You usually would pass that object to StartCoroutine which will store that object internally and the coroutine scheduler of Unity would start “iterating” that compiler generated object. This StopCoroutine line can’t do anything because even if there’s another coroutine object running, the one you pass to StopCoroutine here is a completely new object that hasn’t been started yet. In order for the StopCoroutine to work, you would need to pass the exact same object instance that you passed to StartCoroutine. Since you didn’t store that instance yourself somewhere, you can’t stop it from outside.

Apart from that you generally should not stop coroutines from the outside. Even though coroutines can only be interrupted / stopped at a yield statement, terminating a coroutine from the outside can result in an intermediate state depending on what the coroutine does. In some cases it may work / do what you expect. Though it’s very easy to cause issues that way. It’s not as bad as terminating a Thread from the outside, but can have similar repercussions.

So here’s an example how you can combine your 3 animations into one coroutine:

IEnumerator lifterGoesUp()
{
    float LerpSpeed = .5f;
    for(float t = 0f; t < 1f; t += Time.deltaTime * LerpSpeed)
    {
        pickerUpper.transform.position = Vector3.Lerp(pickerUpperEndPoint.position, pickerUpperStartPoint.position, t);
        yield return null;
    }
    pinsweeper.SetActive(true);

    LerpSpeed = .75f;
    for(float t = 0f; t < 1f; t += Time.deltaTime * LerpSpeed)
    {
        pinsweeper.transform.position = Vector3.Lerp(pinsetRisePoint.position, pinsetStartPoint.position, t);
        yield return null;
    }

    LerpSpeed = .75f;
    for(float t = 0f; t < 1f; t += Time.deltaTime * LerpSpeed)
    {
        pinsweeper.transform.position = Vector3.Lerp(pinsetStartPoint.position, pinsetEndPoint.position, t);
        yield return null;
     }
     pinsweeper.SetActive(false);
}

This should do the same thing. Note that since you actually hardcode the different "LerpSpeed"s in the code, there’s no real advantage of using that variable in the first place. If it should be configurable, you may want to pass the speed values either as arguments to the coroutine, or store them in the class that contains the coroutine where they could be configured.

Finally we don’t know how exactly those variables are actually declared (pickerUpper, pinsweeper), though it looks like they are of type GameObject. You probably want to change their type to Transform. That way you can remove the .transform that you use which has to lookup the transform component each frame. Directly linking to the Transform component usually makes more sense.

Another thing you may want to consider is using a simple animation to carry out those movements. With the Animation window inside the Unity editor you can create and record / setup animations with its animation curves yourself directly inside the editor. That would make things a bit easier to adjust in the future. Though this of course depends on the usecase.

pickerUpper.transform.position

Thank you so much for this! it makes coroutines make a lot more sense.

and yes, i know i can use the animation mecanim, but this is sorta a side diversion of something simple enough that i can actually finish it without getting distracted just so i actually make it through all the steps of creating a project. what it diverted from is something that is going to be all physics based animation and this is simple baby steps towards how to transform objects solely through code. (playing with active ragdolls by turning them into bowling pins is quite the stress reliever)

and yeah, i didnt even think of just hardcoding in the lerp speed since that has no need to be variable, you are absolutely right i should just call it hard and save that processing power.

nice catch with the transforms as well. still getting used to unity and c# and sometimes it comes across as black magic which parts of the properties are necessary to call each time.

just…thank you.