- Home /
Coroutine only fires once instead of looping until stopped.
Hi, and thanks for taking the time to help.
I'm trying to have all rigid bodies in my scene gravitate towards a(n) object(s) with a GravityWell C# script attached to them.
The idea of the following code is to run a new co-routine for each rigid body so that they handle applying force on their own.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GravityWell : MonoBehaviour {
// Use this for initialization
void Start () {
int i = 0;
foreach (Rigidbody rigidbody in FindObjectsOfType<Rigidbody>())
{
i++;
rigidbody.name = rigidbody.name + " " + i;
StartCoroutine(Gravitate(rigidbody));
}
}
// Update is called once per frame
void Update () {
}
IEnumerator Gravitate(Rigidbody rigidbody)
{
rigidbody.AddForce(transform.position - rigidbody.position);
Debug.Log("Gravitate() Coroutine is running with " + rigidbody.name);
yield return new WaitForSeconds(.1f);
}
}
The Log output is shown for all rigid bodies in my scene, however it is only shown the first time instead of constantly filling up the log output as one would expect.
It's probably something simple I'm missing, but why are the co-routines not continuously running?
Thanks.
Answer by oStaiko · Nov 01, 2016 at 05:25 AM
Are you setting your object/script to unactive or destroying it? If the object/script starting the coroutine is no longer active, it'll cause the coroutine to hang and probably some other problems. Try making the Gravitate a public function, and see if that fixes it, and I'd highly recommend not touching the script that calls a coroutine.
I had a very similar issue yesterday and this was the problem. Ended up using a static $$anonymous$$onoBehaviour for all coroutines.
public static class CoroutineStarter
{
private static readonly $$anonymous$$onoBehaviour coroutineStarter;
public static Coroutine StartCoroutine(IEnumerator function)
{
return coroutineStarter.StartCoroutine(function);
}
public static void StopCoroutine(IEnumerator function)
{
if (function != null)
{
coroutineStarter.StopCoroutine(function);
}
}
static CoroutineStarter()
{
coroutineStarter = new GameObject("CoroutineStarter").AddComponent<$$anonymous$$onoBehaviour>();
Object.DontDestroyOnLoad(coroutineStarter.gameObject);
}
}
Note that this won't work in 5.4, as adding pure $$anonymous$$onoBehaviours is forbidden. (I'm still using 5.3)
No I'm not setting the object/script to unactive or destroying it, yet. $$anonymous$$aking Gravitate() public yields the same result of only running once, rather than continuously. Possibly I have co-routines confused. I thought they were a function/method you could call that ran continuously like an update loop does until you stop them. Here this is not the case they fire once like a regular old function...
I realized that a script instance will actually fire up another co-existing update loop, so I simply attached a new script to all the objects with rigid bodies rather than having one script fire up the same amount of co-routines as I have now attached scripts.
In theory this way should cause more overhead, but it doesn't seem to, or is not noticeable.
Thanks for trying to help me out. If you know a better way let me know
What I use now:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ForceGravitate : $$anonymous$$onoBehaviour {
[SerializeField]
Transform gravitateTowards;
private float nextActionTime = 0.0f;
private float tic = .1f;
void Start () {
//Find an object with an attached GravityWell script and use it's transform to gravitate towards
gravitateTowards = FindObjectOfType<GravityWell>().transform;
}
void Update () {
//If more time has passed than the value of a tic GravitateTowards
if (Time.time > nextActionTime)
{
nextActionTime += tic;
GravitateTowards(gravitateTowards);
}
}
void GravitateTowards(Transform gravitateTowards)
{
// If we are farther than 4 units in magnitude away apply and keep aplying force, otherwise use default physics
if (Vector3.$$anonymous$$agnitude(gravitateTowards.position - transform.position) > 4f)
{
GetComponent<Rigidbody>().AddForce(gravitateTowards.position - transform.position);
}
}
}
Had another post refresh my memory you can call InvokeRepeating( ), which is probably what I really wanted, much simpler fix, and we can now just throw in gravity wells (still needs better application of forces) and anything with a rigid body get pulled towards them.
Thanks to you both for the help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GravityWell : $$anonymous$$onoBehaviour
{
// Use this for initialization
void Start()
{
GravitateRepeating();
}
// Update is called once per frame
void Update()
{
}
void Gravitate()
{
foreach (Rigidbody rigidbody in FindObjectsOfType<Rigidbody>())
{
rigidbody.AddForce(transform.position - rigidbody.position);
}
}
void GravitateRepeating()
{
InvokeRepeating("Gravitate", 0.1f, 0.1f);
}
}
Answer by Zenix · Nov 01, 2016 at 09:46 AM
Coroutines don't automatically repeat, they work just like normal methods. If you want something to repeat, you'll have to add that functionality yourself.
Your answer
Follow this Question
Related Questions
Had difficulties implementing intro to Coroutines from unitypatterns.com. Help? 1 Answer
Does UnityEngine.CustomeYieldInstruction works in a seperate thread? 2 Answers
Coroutine not working - what I did wrong? 2 Answers
How to stop a coroutine started in Script A from within Script B 1 Answer
Coroutines not passing yield 1 Answer