You didn’t really improve your question. In a comment you said something about doing a parabolic raycast but it’s still not clear how exactly you want to utilize coroutines here. Weapons with a parabolic trajectory are usually projectile weapons. The weapon just shoots off the projectile and it just travels it’s parabolic way. All you need is a Rigidbody.
Of course fast travelling rigidbodies tend to just penetrate through objects due to the high speed and relatively low sample rate. That’s why there are scripts like DontGoThroughThings which do a raycast from the last position to the new one. Your projectiles should simply do the same. The physics system will take care of the actual trajectory.
Anyways, since your question is about storing coroutines i’m going to explain them once more.
IEnumerator
Coroutines in Unity use something that’s generally called a generator. Microsoft prefers the term iterator. A generator method is a masterpiece of compiler magic. The compiler transforms your method into a statemachine object. This object, which implements the IEnumerator interface, has a method called “MoveNext”. This method actually executes your code up to the next yield statement in the original method. The compiler has split up your code at yields into logical “parts” and each part belongs to it’s own state. The state is stored internally in the object.
Using an IEnumerator object works like this:
- You call MoveNect
- if it returns true the execution is not yet finished and the Current property contains the object that got yielded.
- As long as MoveNext returns true, continue with step 1
Coroutines in Unity
Now Unity uses this nice feature of the language to implement coroutines. When you create a coroutine, the IEnumerator on it’s own is nothing but a generator. The coroutine magic happens in Unity’s coroutine scheduler.
As you know to start a coroutine you have to pass an IEnumerator object to StartCoroutine, like this:
StartCoroutine(MyCoroutine());
What happens here is when calling MyCoroutine it returns our IEnumerator object which is passed to Unity. Unity stores the object in an internal list until MoveNext returns false which would be when the coroutine reaches the end of your coroutine or when you use yield break;
The scheduler does not simply iterate straight through your IEnumerator. Unity only execute MoveNext once and looks as the return value. If it’s an instance of the WaitForSeconds class, Unity reads out the delay which is stored in an internal field so it knows this coroutine has to wait this amount of time. It remembers when to continue this coroutine probably by using “scheduling lists”. So there would be a sorted list where Unity puts all coroutines in which are waiting for their timeout to be scheduled again.
The same happens for other return values like WaitForEndOfFrame. When Unity sees a “WaitForEndOfFrame” object it’s going to put the coroutine object into a list which will be executed when the current frame reaches the end (so when all rendering is done)
If Unity don’t know the meaning of a return value, like when you yield: null, 123, “Hello”, (new Plane()), … it simply “schedules” the coroutine for the next frame.
It’s actually quite easy to implement your own CoroutineScheduler, however you can’t use Unity’s objects for WaitForSeconds since you can’t access the internal stored value. That’s why the implementation i’ve linked simply uses float values as wait time.
That’s how coroutines work in Unity. Now, StartCoroutine returns an instance of “Coroutine”. This is the internal class which just stores the IEnumerator object. You as user can only use this instance only for one purpose: pass it as yield value in another coroutine to signal the coroutine scheduler you want to wait until this coroutine is finished.
IEnumerable
Next thing is Coroutines or IEnumerators can’t be “restarted”. You have to create a new instance by calling your generator again and pass the result to StartCoroutine.
Someone had a similar problem about a week ago. He wanted to “restart” his coroutine. Well there is a way, but you have to change your coroutine from generating an IEnumerator ro generate an IEnumerable (not the difference!)
The IEnumerable interface is even more simpler. The object returned only has 1 method which is called GetEnumerator(). This method again will return the same IEnumerator which our coroutine would have generated when it has IEnumerator as return type.
Of course you can’t pass an IEnumerable to StartCoroutine since it expects an IEnumerator. But you can use:
IEnumerable co = MyCoroutine();
StartCoroutine(co.GetEnumerator());
You may ask yourself: What’s that actually good for. Well, it allows you to create a new instance of your coroutine without calling your actual generator method again. This is useful when you pass parameters to your coroutine:
IEnumerable co = MyCoroutine(25.0f, "Some string");
StartCoroutine(co.GetEnumerator());
Each time you call co.GetEnumerator() it will create a new instance of your IEnumerator and the parameters will stay the same as they are stored inside the IEnumerable object.
As final note: I can almost guarantee that you already worked with IEnumerables. C# uses this interface in many places. The most common one is “foreach”. The foreach loop works always on IEnumerable object. A Unity example would be:
foreach(Transform t in transform)
{
//...
}
The Transform class implements the IEnumerable interface and therefor has a GetEnumerator method. The foreach loop simply creates a personal instance for thie iteration and use thie instance to iterate through the collection of child objects.
A simple more general example which isn’t Unity specific:
IEnumerable<string> CreateNames(string suffix)
{
yield return "Hans" + suffix;
yield return "Mike" + suffix;
yield return "Tony" + suffix;
yield return "Mel" + suffix;
}
foreach(string s in CreateNames(" Brooks"))
{
Debug.Log(s);
}
// result:
// Hans Brooks
// Mike Brooks
// Tony Brooks
// Mel Brooks
As you can see there are also generic versions of the IEnumerable and IEnumerator interface. They only allow a certain type to be yielded. The Current property is not of type “object” like in the non generic version but has the generic type, in this case “string”