- Home /
StartCoroutine in a separate class
Hi, I'm experimenting with StartCoroutine and i've ran into an issue. I have a SphereController class which contains the following:
Sphere referenceToSphereOne = CurrentlySelected[0].GetComponent<Sphere> ();
Sphere referenceToSphereTwo = CurrentlySelected[1].GetComponent<Sphere> ();
// Set targets to the opposite transforms
referenceToSphereOne.SetSphereTarget (CurrentlySelected[1].transform.position);
referenceToSphereTwo.SetSphereTarget (CurrentlySelected[0].transform.position);
StartCoroutine ( referenceToSphereOne.IETESTUpdatePosition() );
So basically it access the script type Sphere of two game objects and tells them to swap their positions. I then have a Sphere class which contains the following:
public IEnumerator IETESTUpdatePosition()
{
Debug.Log("Entering ie test");
transform.position = Vector3.Lerp (transform.position, targetPosition, Time.deltaTime * 4.0f);
yield return AreAtSamePosition( transform.position, targetPosition );
}
public bool AreAtSamePosition (Vector3 vectorOne, Vector3 vectorTwo)
{
// Note that we ignore the Z axis
if (Mathf.Approximately (vectorOne.x, vectorTwo.x) && Mathf.Approximately (vectorOne.y, vectorTwo.y)) {
return true;
} else {
return false;
}
}
So what I'm trying to accomplish is the SphereController to tell the Sphere to swap positions and only return when they have reached their target positions.
However, I'm running into issues as soon as I trying to call the coroutine in SphereController:
StartCoroutine ( referenceToSphereOne.IETESTUpdatePosition() );
Although I have a reference to the script, the compiler is complaining that an object reference is required to access non static member etc etc
As I'm inexperienced with coroutines I wondered if anyone could help with this error and also whether the remaining code will achieve what I'm after!
Many thanks
Answer by Bunny83 · Nov 22, 2011 at 09:48 PM
Well, that's not how coroutines works. You can see coroutines are "normal" functions but they can be interrupted at certain points. Those points are your "yield return" statements. The yield return statement will leave the function at this point and just returns a value back to the controlling system. Unity is the controlling system in our case and it will decide what to do next on the basis of the value you returned. In your case you call your function AreAtSamePosition which returns a boolean. If you yield return a boolean Unity will just wait a single frame and continue after your yield statement. Your function ends there so nothing special happens in this case.
I guess you want to move it towards the target until it reaches the end. You have to use a loop like this:
public IEnumerator IETESTUpdatePosition()
{
Debug.Log("Entering ie test");
while (!AreAtSamePosition( transform.position, targetPosition ))
{
transform.position = Vector3.Lerp (transform.position, targetPosition, Time.deltaTime * 4.0f);
yield return false; // this will wait for the next frame.
}
Debug.Log("Reached target");
}
Thanks for the comments guys - the other issues still remains however, this:
StartCoroutine ( referenceToSphereOne.IETESTUpdatePosition() );
still returns the same error as I described above. $$anonymous$$any thanks for your time.
Where do you execute this line:
StartCoroutine ( referenceToSphereOne.IETESTUpdatePosition() );
?
It looks like you try to call StartCoroutine from a static function? You know that StartCoroutine is a member-function of $$anonymous$$onoBehaviour? Coroutines always run on $$anonymous$$onoBehaviour-instances. Usually you call StartCoroutine from inside a $$anonymous$$onoBehaviour. When you have your code in a static function (that doesn't belong to any instance) you have to provide a $$anonymous$$onoBehaviour instance.
Beside this technical fact, i guess you also want to run the coroutine on the Sphere object itself...
You have two ways to accomplish that:
Call StartCoroutine of your Sphere instance (Sphere is a class that inherits from $$anonymous$$onoBehaviour) by using the reference to your instance
Provide a seperate function that starts your coroutine which can be called from outside.
Solution 1:
In your static function call StartCoroutine this way:
referenceToSphereOne.StartCoroutine ( referenceToSphereOne.IETESTUpdatePosition() );
Solution 2:
This is the cleaner one since it avoids the ugly reference madness:
// In your Sphere script:
private IEnumerator IETESTUpdatePosition()
{
// Your coroutine is now private since only the class itself is going to use it.
// [...]
}
public void TESTUpdatePosition()
{
StartCoroutine(IETESTUpdatePosition());
}
// In your static function you can use the TESTUpdatePosition function of your Sphere class like that:
referenceToSphereOne.TESTUpdatePosition();
Answer by Eric5h5 · Nov 22, 2011 at 09:45 PM
You can't use "yield return" with AreAtSamePosition, or rather you can, but it won't accomplish anything. In order to properly yield on a function, it needs to be a coroutine, and that coroutine needs to take place over a period of time, not return immediately (which means you can't make a coroutine return true or false, since all coroutines must return IEnumerator). Also, you're not using Lerp correctly. It's just a math function that returns a value immediately, it doesn't "do stuff over time" or anything. See the Translation function here for an example of a coroutine that uses Lerp repeatedly to move an object from one position to another.
Answer by AntLewis · Nov 30, 2011 at 08:51 AM
Bunny83 - thanks so much for taking the time to explain this. Theoretically, I'm starting to get this now. However, I just created a project based on what you said above. So I have Advanced Yield Test Script and Sphere:
using UnityEngine; using System.Collections;
public class AdvancedYieldTest : MonoBehaviour {
public GameObject referenceToObject;
private static Sphere referenceToScriptForObject;
// Use this for initialization
void Start () {
// Reference the script on the sphere
referenceToScriptForObject = referenceToObject.GetComponent<Sphere>();
}
// Update is called once per frame
void Update () {
if ( Input.GetMouseButtonUp ( 0 ) )
{
StaticFunctionTest();
}
}
private static void StaticFunctionTest()
{
Debug.Log("Called static function.");
referenceToScriptForObject.AccessToUpdatePosition();
}
using UnityEngine; using System.Collections;
public class Sphere : MonoBehaviour {
// Use this for initialization
void Start () {
}
public IEnumerator AccessToUpdatePosition()
{
Debug.Log("AccessToUpdatePosition");
Vector3 target = new Vector3 ( 1.0f, 0.0f, 0.0f );
// wait until it's reached taget position
yield return StartCoroutine( UpdatePosition( target ) );
//Now do something else
Debug.Log("ITS ARRIVED!!!!!");
}
// Test IENumerator function
private IEnumerator UpdatePosition( Vector3 target)
{
Debug.Log("UpdatePosition");
transform.Translate ( 0.2f, 0.0f, 0.0f );
while ( transform.position.x!=target.x )
{
// This waits for next frame
yield return 0;
}
}
}
}
Which I think is very close to your example above. The issue is that when I run and step through it, it gets to StaticFunctionTest(), logs out "Called static function", enters AccessToUpdatePosition() on the actual object but then actually returns without even logging out Debug.Log("AccessToUpdatePosition");
Really struggling to understand why? Cheers
$$anonymous$$y "TESTUpdatePosition" is NOT a coroutine. Coroutines are something totally different than normal functions. Coroutines are also called generator functions. The coroutine itself just return an object (IEnumerator) which can be used to "iterate" through the code inside your coroutine. This "iteration" is done by Unity but you have to pass the IEnumerator to StartCoroutine() so Unity can actually run the coroutine.
If you just call your generator function without StartCoroutine it will return the IEnumerator object and you throw it away.
Your "StaticFunctionTest" won't work since AccessToUpdatePosition() is also a coroutine / generator function which have to be used along with StartCoroutine.
After you start a coroutine it runs on it's own. You can't wait in normal functions for it's completion, only in other coroutines.
If you want to understand how coroutines work you may study this excellent custom coroutine scheduler. Also (if you have missed it in the post) check out the blog article about coroutines.
btw. Answers should exclusively used for answers. I know it's a pain to write such a long post in a comment but you can use almost the same features in comments, you just don't have the nice helper-buttons on top.
Just saw your UpdatePosition function. Transform.Translate will immediately move your object by the given amount. Your loop waits until the x coordinate will match the targets x position but inside the loop you don't move your object. Beside that if you increment the position each frame by 0.2 it won't reach the target position due to float inaccuarcy. 5.7 is not the same as 5.69999999 or 5.70000001 even when it's visually the same.
Your first lerp approach will reduce the step each frame as it comes closer to the target. This results in a decelerated movement so it never goes beond the target. But even with $$anonymous$$athf.Approximately it could take quite a time to reach "equality".
Your answer

Follow this Question
Related Questions
Invoke a Coroutine From OnCollisionEnter 1 Answer
Is there anything like "StartCoroutine", but for a non MonoBehaviour? 2 Answers
Run Coroutine only once 1 Answer
GET Request dont save data 0 Answers